Browse Source

Merge branch 'feat/db' into feat/namespace

Tienson Qin 1 year ago
parent
commit
8d1d5363a0
84 changed files with 1641 additions and 1367 deletions
  1. 5 1
      .gitignore
  2. 3 3
      deps.edn
  3. 8 1
      deps/common/src/logseq/common/util/macro.cljs
  4. 35 5
      deps/db/src/logseq/db.cljs
  5. 1 1
      deps/db/src/logseq/db/frontend/class.cljs
  6. 5 3
      deps/db/src/logseq/db/frontend/content.cljs
  7. 1 1
      deps/db/src/logseq/db/frontend/db_ident.cljc
  8. 35 23
      deps/db/src/logseq/db/frontend/delete_blocks.cljs
  9. 9 14
      deps/db/src/logseq/db/frontend/property.cljs
  10. 2 1
      deps/db/src/logseq/db/sqlite/build.cljs
  11. 27 1
      deps/db/test/logseq/db_test.cljs
  12. 82 63
      deps/graph-parser/src/logseq/graph_parser/block.cljs
  13. 109 68
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  14. 10 5
      deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs
  15. 2 1
      deps/graph-parser/test/resources/exporter-test-graph/journals/2024_02_14.md
  16. 1 0
      deps/graph-parser/test/resources/exporter-test-graph/pages/page with #tag.md
  17. 1 0
      deps/graph-parser/test/resources/exporter-test-graph/pages/type.md
  18. 27 17
      deps/outliner/src/logseq/outliner/core.cljs
  19. 24 6
      deps/outliner/src/logseq/outliner/property.cljs
  20. 23 2
      deps/outliner/src/logseq/outliner/validate.cljs
  21. 0 1
      deps/shui/src/logseq/shui/table/core.cljc
  22. 7 5
      deps/shui/src/logseq/shui/table/impl.cljc
  23. 0 2
      gulpfile.js
  24. 1 1
      libs/src/LSPlugin.ts
  25. 2 2
      package.json
  26. 3 3
      src/main/frontend/commands.cljs
  27. 1 1
      src/main/frontend/common.css
  28. 10 1
      src/main/frontend/common/search_fuzzy.cljs
  29. 67 47
      src/main/frontend/components/block.cljs
  30. 2 2
      src/main/frontend/components/block.css
  31. 13 10
      src/main/frontend/components/cmdk/core.cljs
  32. 1 1
      src/main/frontend/components/container.cljs
  33. 18 15
      src/main/frontend/components/editor.cljs
  34. 85 0
      src/main/frontend/components/file_based/query.cljs
  35. 1 1
      src/main/frontend/components/file_based/query_table.cljs
  36. 66 65
      src/main/frontend/components/header.cljs
  37. 51 57
      src/main/frontend/components/icon.cljs
  38. 18 16
      src/main/frontend/components/objects.cljs
  39. 11 27
      src/main/frontend/components/page.cljs
  40. 14 21
      src/main/frontend/components/page_menu.cljs
  41. 51 43
      src/main/frontend/components/property.cljs
  42. 1 1
      src/main/frontend/components/property.css
  43. 23 22
      src/main/frontend/components/property/config.cljs
  44. 7 2
      src/main/frontend/components/property/value.cljs
  45. 29 86
      src/main/frontend/components/query.cljs
  46. 4 3
      src/main/frontend/components/query/builder.cljs
  47. 2 1
      src/main/frontend/components/repo.cljs
  48. 3 10
      src/main/frontend/components/right_sidebar.cljs
  49. 5 3
      src/main/frontend/components/settings.cljs
  50. 4 2
      src/main/frontend/components/title.cljs
  51. 79 83
      src/main/frontend/components/views.cljs
  52. 37 0
      src/main/frontend/date.cljs
  53. 33 108
      src/main/frontend/db/model.cljs
  54. 8 2
      src/main/frontend/db/query_dsl.cljs
  55. 11 4
      src/main/frontend/extensions/code.cljs
  56. 4 1
      src/main/frontend/extensions/graph.cljs
  57. 26 26
      src/main/frontend/extensions/graph/pixi.cljs
  58. 1 1
      src/main/frontend/format/block.cljs
  59. 2 2
      src/main/frontend/handler/code.cljs
  60. 55 38
      src/main/frontend/handler/common/page.cljs
  61. 2 1
      src/main/frontend/handler/db_based/editor.cljs
  62. 7 5
      src/main/frontend/handler/db_based/page.cljs
  63. 56 63
      src/main/frontend/handler/editor.cljs
  64. 0 63
      src/main/frontend/handler/events.cljs
  65. 77 1
      src/main/frontend/handler/file_based/events.cljs
  66. 87 120
      src/main/frontend/handler/graph.cljs
  67. 7 0
      src/main/frontend/handler/page.cljs
  68. 29 23
      src/main/frontend/handler/route.cljs
  69. 24 14
      src/main/frontend/state.cljs
  70. 26 26
      src/main/frontend/ui.cljs
  71. 30 18
      src/main/frontend/util.cljc
  72. 9 3
      src/main/frontend/util/cursor.cljs
  73. 1 1
      src/main/frontend/worker/db_worker.cljs
  74. 6 1
      src/main/frontend/worker/handler/page.cljs
  75. 2 2
      src/main/frontend/worker/search.cljs
  76. 11 7
      src/main/logseq/api.cljs
  77. 0 1
      src/resources/dicts/en.edn
  78. 0 0
      src/rtc_e2e_test/example.cljs
  79. 1 1
      src/test/frontend/db/db_based_model_test.cljs
  80. 8 28
      src/test/frontend/db/model_test.cljs
  81. 15 7
      src/test/frontend/db/query_dsl_test.cljs
  82. 6 3
      src/test/frontend/test/helper.cljs
  83. 1 4
      tailwind.all.css
  84. 70 43
      yarn.lock

+ 5 - 1
.gitignore

@@ -36,7 +36,11 @@ strings.csv
 resources/electron.js
 .lsp/.cache
 .clj-kondo/.cache
-.clj-kondo/babashka/sci
+.clj-kondo/babashka
+.clj-kondo/http-kit
+.clj-kondo/metosin/malli
+.clj-kondo/rewrite-clj
+.clj-kondo/taoensso
 /libs/dist/
 charlie/
 .vscode

+ 3 - 3
deps.edn

@@ -45,7 +45,7 @@
  :aliases {:cljs {:extra-paths ["src/dev-cljs/" "src/test/" "src/electron/"]
                   :extra-deps  {org.clojure/clojurescript        {:mvn/version "1.11.132"}
                                 org.clojure/tools.namespace      {:mvn/version "0.2.11"}
-                                cider/cider-nrepl                {:mvn/version "0.47.0"}
+                                cider/cider-nrepl                {:mvn/version "0.50.2"}
                                 org.clojars.knubie/cljs-run-test {:mvn/version "1.0.1"}
                                 tortue/spy                       {:mvn/version "2.14.0"}}
                   :main-opts   ["-m" "shadow.cljs.devtools.cli"]}
@@ -56,12 +56,12 @@
                                 pjstadig/humane-test-output      {:mvn/version "0.11.0"}
                                 org.clojars.knubie/cljs-run-test {:mvn/version "1.0.1"}
                                 tortue/spy                       {:mvn/version "2.14.0"}
-                                cider/cider-nrepl                {:mvn/version "0.47.0"}}
+                                cider/cider-nrepl                {:mvn/version "0.50.2"}}
                   :main-opts   ["-m" "shadow.cljs.devtools.cli"]}
 
            :rtc-e2e-test {:extra-paths ["src/rtc_e2e_test"]
                           :extra-deps {org.clojure/clojurescript        {:mvn/version "1.11.132"}
-                                       cider/cider-nrepl                {:mvn/version "0.47.0"}}
+                                       cider/cider-nrepl                {:mvn/version "0.50.2"}}
                           :main-opts ["-m" "shadow.cljs.devtools.cli"]}
 
            :bench {:extra-paths ["src/bench/"]

+ 8 - 1
deps/common/src/logseq/common/util/macro.cljs

@@ -1,15 +1,22 @@
 (ns logseq.common.util.macro
-  "Core vars and util fns for user macros e.g. {{foo}}"
+  "Core vars and util fns for built-in macros e.g. {{query }} and user macros e.g. {{foo}}"
   (:require [clojure.string :as string]))
 
 (def left-braces "Opening characters for macro" "{{")
 (def right-braces "Closing characters for macro" "}}")
+(def query-macro (str left-braces "query"))
 
 (defn macro?
   [*s]
   (when-let [s (and (string? *s) (string/trim *s))]
     (and (string/starts-with? s left-braces) (string/ends-with? s right-braces))))
 
+(defn query-macro?
+  [s]
+  (and (string? s)
+       (string/includes? s (str query-macro " "))
+       (not (string/includes? s (str "`" query-macro)))))
+
 (defn macro-subs
   [macro-content arguments]
   (loop [s macro-content

+ 35 - 5
deps/db/src/logseq/db.cljs

@@ -56,6 +56,21 @@
   [f]
   (when f (reset! *transact-fn f)))
 
+(defn- remove-temp-block-data
+  [tx-data]
+  (let [remove-block-temp-f (fn [m]
+                              (->> (remove (fn [[k _v]] (= "block.temp" (namespace k))) m)
+                                   (into {})))]
+    (map (fn [m]
+           (if (map? m)
+             (cond->
+              (remove-block-temp-f m)
+               (and (seq (:block/refs m))
+                    (every? map? (:block/refs m)))
+               (update :block/refs (fn [refs] (map remove-block-temp-f refs))))
+             m))
+         tx-data)))
+
 (defn transact!
   "`repo-or-conn`: repo for UI thread and conn for worker/node"
   ([repo-or-conn tx-data]
@@ -64,10 +79,11 @@
    (let [tx-data (map (fn [m]
                         (if (map? m)
                           (dissoc m :block/children :block/meta :block/top? :block/bottom? :block/anchor
-                                  :block.temp/ast-title :block.temp/ast-body :block/level :block/container :db/other-tx :block.temp/parent-title?
+                                  :block/level :block/container :db/other-tx
                                   :block/unordered)
                           m)) tx-data)
-         tx-data (->> (common-util/fast-remove-nils tx-data)
+         tx-data (->> (remove-temp-block-data tx-data)
+                      (common-util/fast-remove-nils)
                       (remove empty?))
          delete-blocks-tx (when-not (string? repo-or-conn)
                             (delete-blocks/update-refs-and-macros @repo-or-conn tx-data tx-meta))
@@ -482,6 +498,19 @@
        (contains? (set (get-in (db-class/built-in-classes (:db/ident class-entity)) [:schema :properties]))
                   (:db/ident property-entity))))
 
+(defn private-built-in-page?
+  "Private built-in pages should not be navigable or searchable by users. Later it
+   could be useful to use this for the All Pages view"
+  [page]
+  (cond (property? page)
+        (not (public-built-in-property? page))
+        (or (class? page) (= "page" (:block/type page)))
+        false
+        ;; Default to true for closed value and future internal types.
+        ;; Other types like whiteboard are not considered because they aren't built-in
+        :else
+        true))
+
 (def write-transit-str sqlite-util/write-transit-str)
 (def read-transit-str sqlite-util/read-transit-str)
 
@@ -524,7 +553,8 @@
         :block/parent [:block/uuid page-id]
         :block/order (db-order/gen-key nil)
         :block/page [:block/uuid page-id]
-        :logseq.property/view-for [:block/uuid page-id]})])))
+        :logseq.property/view-for [:block/uuid page-id]
+        :logseq.property/built-in? true})])))
 
 (defn get-key-value
   [db key-ident]
@@ -584,8 +614,8 @@
       (loop [current-parent parent]
         (when (and
                current-parent
-               (class? parent)
-               (not (contains? @*classes (:db/id parent))))
+               (class? current-parent)
+               (not (contains? @*classes (:db/id current-parent))))
           (swap! *classes conj (:db/id current-parent))
           (recur (:logseq.property/parent current-parent)))))
     @*classes))

+ 1 - 1
deps/db/src/logseq/db/frontend/class.cljs

@@ -5,7 +5,7 @@
 
 (def ^:large-vars/data-var built-in-classes
   "Map of built-in classes for db graphs with their :db/ident as keys"
-  {:logseq.class/Root {:title "Root tag"}
+  {:logseq.class/Root {:title "Root Tag"}
 
    :logseq.class/Query
    {:title "Query"

+ 5 - 3
deps/db/src/logseq/db/frontend/content.cljs

@@ -101,7 +101,8 @@
 
 (defn- replace-page-ref-with-id
   [content page-name id replace-tag?]
-  (let [page-name (str page-name)
+  (let [page-name (-> (str page-name)
+                      (string/replace "HashTag-" "#"))
         id (str id)
         content' (replace-page-ref content page-name id)]
     (if replace-tag?
@@ -121,8 +122,9 @@
                    ref))
                refs)]
     (reduce
-     (fn [content {uuid' :block/uuid :block/keys [title]}]
-       (replace-page-ref-with-id content title uuid' replace-tag?))
+     (fn [content {uuid' :block/uuid :block/keys [title] :as block}]
+       (let [title' (or (:block.temp/original-page-name block) title)]
+         (replace-page-ref-with-id content title' uuid' replace-tag?)))
      title
      (filter :block/title refs'))))
 

+ 1 - 1
deps/db/src/logseq/db/frontend/db_ident.cljc

@@ -68,7 +68,7 @@
          :cljs (exists? js/process)
          :default false)
     ;; So that we don't have to change :user.{property|class} in our tests
-    (keyword user-namespace name-string)
+    (keyword user-namespace (string/replace name-string "/" "-"))
     (keyword user-namespace
              (str (rand-nth non-int-char-range)
                   (nano-id 20)

+ 35 - 23
deps/db/src/logseq/db/frontend/delete_blocks.cljs

@@ -8,33 +8,45 @@
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.content :as db-content]))
 
-(defn- build-retracted-tx [retracted-blocks]
-  (->> (for [block retracted-blocks]
-         (let [refs (:block/_refs block)]
-           (mapcat (fn [ref]
-                     (let [id (:db/id ref)
-                           block-content (:block/title block)
-                           new-content (some-> (:block/raw-title ref)
-                                               (string/replace (re-pattern (common-util/format "(?i){{embed \\(\\(%s\\)\\)\\s?}}" (str (:block/uuid block))))
-                                                               block-content)
+(defn- replace-ref-with-deleted-block-title
+  [block ref-raw-title]
+  (let [block-content (:block/title block)]
+    (some-> ref-raw-title
+            (string/replace (re-pattern (common-util/format "(?i){{embed \\(\\(%s\\)\\)\\s?}}" (str (:block/uuid block))))
+                            block-content)
 
-                                               (string/replace (block-ref/->block-ref (str (:block/uuid block)))
-                                                               block-content)
+            (string/replace (block-ref/->block-ref (str (:block/uuid block)))
+                            block-content)
 
                                                ;; Replace object
-                                               (string/replace (db-content/block-id->special-id-ref (:block/uuid block))
-                                                               block-content)
+            (string/replace (db-content/block-id->special-id-ref (:block/uuid block))
+                            block-content)
                                                ;; Replace non-object
-                                               (string/replace (page-ref/->page-ref (str (:block/uuid block)))
-                                                               block-content))
-                           tx (cond->
-                               [[:db/retract (:db/id ref) :block/refs (:db/id block)]
-                                [:db/retract (:db/id ref) :block/path-refs (:db/id block)]]
-                                new-content
-                                (conj [:db/add id :block/title new-content]))]
-                       tx))
-                   refs)))
-       (apply concat)))
+            (string/replace (page-ref/->page-ref (str (:block/uuid block)))
+                            block-content))))
+
+(defn- build-retracted-tx
+  [retracted-blocks]
+  (let [refs (->> (mapcat (fn [block] (:block/_refs block)) retracted-blocks)
+                  (common-util/distinct-by :db/id))]
+    (mapcat
+     (fn [ref]
+       (let [id (:db/id ref)
+             replaced-title (when-let [raw-title (:block/raw-title ref)]
+                              (reduce
+                               (fn [raw-title block]
+                                 (replace-ref-with-deleted-block-title block raw-title))
+                               raw-title
+                               retracted-blocks))
+             tx (cond->
+                 (mapcat
+                  (fn [block]
+                    [[:db/retract (:db/id ref) :block/refs (:db/id block)]
+                     [:db/retract (:db/id ref) :block/path-refs (:db/id block)]]) retracted-blocks)
+                  replaced-title
+                  (conj [:db/add id :block/title replaced-title]))]
+         tx))
+     refs)))
 
 (defn update-refs-and-macros
   "When a block is deleted, refs are updated. For file graphs, macros associated

+ 9 - 14
deps/db/src/logseq/db/frontend/property.cljs

@@ -42,16 +42,16 @@
                             :schema {:type :node
                                      :public? true
                                      :view-context :page}}
-   :logseq.property.class/properties {:title "Tag properties"
+   :logseq.property.class/properties {:title "Tag Properties"
                                       :schema {:type :property
                                                :cardinality :many
                                                :public? true
                                                :view-context :never}}
-   :logseq.property.class/hide-from-node {:title "Hide from node"
+   :logseq.property.class/hide-from-node {:title "Hide from Node"
                                           :schema {:type :checkbox
                                                    :public? true
                                                    :view-context :class}}
-   :logseq.property/page-tags {:title "pageTags"
+   :logseq.property/page-tags {:title "Page Tags"
                                :schema {:type :page
                                         :public? true
                                         :view-context :page
@@ -67,15 +67,6 @@
                                                     :hide? true}}
    :logseq.property/built-in?             {:schema {:type :checkbox
                                                     :hide? true}}
-   :logseq.property/query-table {:schema {:type :checkbox
-                                          :hide? true}}
-   ;; query-properties is a coll of property db-idents and keywords where keywords are special frontend keywords
-   :logseq.property/query-properties {:schema {:type :coll
-                                               :hide? true}}
-   :logseq.property/query-sort-by {:schema {:type :keyword
-                                            :hide? true}}
-   :logseq.property/query-sort-desc {:schema {:type :checkbox
-                                              :hide? true}}
    :logseq.property/ls-type {:schema {:type :keyword
                                       :hide? true}}
    :logseq.property/hl-type {:schema {:type :keyword :hide? true}}
@@ -105,7 +96,7 @@
                                            :hide? true}}
 
    ;; Journal props
-   :logseq.property.journal/title-format {:title "Title format"
+   :logseq.property.journal/title-format {:title "Title Format"
                                           :schema
                                           {:type :string
                                            :public? false}}
@@ -185,7 +176,7 @@
                                   :public? true}}
 
    :logseq.property.view/type
-   {:title "View type"
+   {:title "View Type"
     :schema
     {:type :default
      :public? false
@@ -242,6 +233,10 @@
   "Internal properties that are also db schema attributes"
   #{:block/alias :block/tags})
 
+(def read-only-properties
+  "Property values that shouldn't be updated"
+  #{:logseq.property/built-in?})
+
 (assert (= db-attribute-properties
            (set (keep (fn [[k {:keys [attribute]}]] (when attribute k))
                       built-in-properties)))

+ 2 - 1
deps/db/src/logseq/db/sqlite/build.cljs

@@ -156,7 +156,8 @@
                                 (let [new-block
                                       (merge (sqlite-util/build-new-property (get-ident all-idents prop-name)
                                                                              (:block/schema prop-m)
-                                                                             {:block-uuid (:block/uuid prop-m)})
+                                                                             {:block-uuid (:block/uuid prop-m)
+                                                                              :title (:block/title prop-m)})
                                              {:db/id (or (property-db-ids prop-name)
                                                          (throw (ex-info "No :db/id for property" {:property prop-name})))})
                                       pvalue-tx-m (->property-value-tx-m new-block (:build/properties prop-m) properties all-idents)]

+ 27 - 1
deps/db/test/logseq/db_test.cljs

@@ -2,7 +2,8 @@
   (:require [cljs.test :refer [deftest is]]
             [logseq.db.frontend.schema :as db-schema]
             [datascript.core :as d]
-            [logseq.db :as ldb]))
+            [logseq.db :as ldb]
+            [logseq.db.sqlite.create-graph :as sqlite-create-graph]))
 
 
 ;;; datoms
@@ -27,3 +28,28 @@
            (try (ldb/get-block-children-ids db #uuid "e538d319-48d4-4a6d-ae70-c03bb55b6fe4")
                 (catch :default e
                   (ex-message e)))))))
+
+(def class-parents-data
+  [{:block/type "class"
+    :block/title "x"
+    :block/name "x"
+    :block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"}
+   {:block/type "class"
+    :block/title "y"
+    :block/name "y"
+    :block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99"
+    :logseq.property/parent [:block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"]}
+   {:block/type "class"
+    :block/title "z"
+    :block/name "z"
+    :block/uuid #uuid "d95f2912-a7af-41b9-8ed5-28861f7fc0be"
+    :logseq.property/parent [:block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99"]}])
+
+(deftest get-class-parents-test
+  (let [conn (d/create-conn db-schema/schema-for-db-based-graph)]
+    (d/transact! conn (sqlite-create-graph/build-db-initial-data "{}"))
+    (d/transact! conn class-parents-data)
+    (is (= #{"x" "y"}
+           (->> (ldb/get-class-parents (ldb/get-page @conn "z"))
+                (map #(:block/title (d/entity @conn %)))
+                set)))))

+ 82 - 63
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -297,6 +297,64 @@
 
 (def convert-page-if-journal (memoize convert-page-if-journal-impl))
 
+(defn- page-name-string->map
+  [original-page-name db date-formatter
+   {:keys [with-timestamp? page-uuid from-page class? skip-existing-page-check?]}]
+  (let [db-based? (ldb/db-based-graph? db)
+        original-page-name (common-util/remove-boundary-slashes original-page-name)
+        [original-page-name' page-name journal-day] (convert-page-if-journal original-page-name date-formatter)
+        namespace? (and (not db-based?)
+                        (not (boolean (text/get-nested-page-name original-page-name')))
+                        (text/namespace-page? original-page-name'))
+        page-entity (when (and db (not skip-existing-page-check?))
+                      (if class?
+                        (ldb/get-case-page db original-page-name')
+                        (ldb/get-page db original-page-name')))
+        original-page-name' (or from-page (:block/title page-entity) original-page-name')
+        page (merge
+              {:block/name page-name
+               :block/title original-page-name'}
+              (when (and original-page-name
+                         (not= (string/lower-case original-page-name)
+                               (string/lower-case original-page-name')))
+
+                {:block.temp/original-page-name original-page-name})
+              (if (and class? page-entity (:db/ident page-entity))
+                {:block/uuid (:block/uuid page-entity)
+                 :db/ident (:db/ident page-entity)}
+                (let [new-uuid* (if (uuid? page-uuid)
+                                  page-uuid
+                                  (if journal-day
+                                    (common-uuid/gen-uuid :journal-page-uuid journal-day)
+                                    (common-uuid/gen-uuid)))
+                      new-uuid (if skip-existing-page-check?
+                                 new-uuid*
+                                 (or
+                                  (cond page-entity       (:block/uuid page-entity)
+                                        (uuid? page-uuid) page-uuid)
+                                  new-uuid*))]
+                  {:block/uuid new-uuid}))
+              (when namespace?
+                (let [namespace' (first (common-util/split-last "/" original-page-name))]
+                  (when-not (string/blank? namespace')
+                    {:block/namespace {:block/name (common-util/page-name-sanity-lc namespace')}})))
+              (when (and with-timestamp? (or skip-existing-page-check? (not page-entity))) ;; Only assign timestamp on creating new entity
+                (let [current-ms (common-util/time-ms)]
+                  {:block/created-at current-ms
+                   :block/updated-at current-ms}))
+              (if journal-day
+                (cond-> {:block/type "journal"
+                         :block/journal-day journal-day}
+                  db-based?
+                  (assoc :block/tags [:logseq.class/Journal]))
+                {}))]
+    [page page-entity]))
+
+(defn sanitize-hashtag-name
+  "This must be kept in sync with its reverse operation in logseq.db.frontend.content"
+  [s]
+  (string/replace s "#" "HashTag-"))
+
 ;; TODO: refactor
 (defn page-name->map
   "Create a page's map structure given a original page name (string).
@@ -306,67 +364,25 @@
     as there's no chance to introduce timestamps via editing in page
    `skip-existing-page-check?`: if true, allows pages to have the same name"
   [original-page-name db with-timestamp? date-formatter
-   & {:keys [page-uuid from-page class? skip-existing-page-check?]}]
+   & {:keys [page-uuid class?] :as options}]
   (when-not (and db (common-util/uuid-string? original-page-name)
                  (not (ldb/page? (d/entity db [:block/uuid (uuid original-page-name)]))))
-    (let [db-based? (ldb/db-based-graph? db)
-          original-page-name (string/trim original-page-name)
+    (let [original-page-name (-> (string/trim original-page-name)
+                                 sanitize-hashtag-name)
           [page _page-entity] (cond
-                               (and original-page-name (string? original-page-name))
-                               (let [original-page-name (common-util/remove-boundary-slashes original-page-name)
-                                     [original-page-name page-name journal-day] (convert-page-if-journal original-page-name date-formatter)
-                                     namespace? (and (not db-based?)
-                                                     (not (boolean (text/get-nested-page-name original-page-name)))
-                                                     (text/namespace-page? original-page-name))
-                                     page-entity (when (and db (not skip-existing-page-check?))
-                                                   (if class?
-                                                     (ldb/get-case-page db original-page-name)
-                                                     (ldb/get-page db original-page-name)))
-                                     original-page-name (or from-page (:block/title page-entity) original-page-name)
-                                     page (merge
-                                           {:block/name page-name
-                                            :block/title original-page-name}
-                                           (if (and class? page-entity (:db/ident page-entity))
-                                             {:block/uuid (:block/uuid page-entity)
-                                              :db/ident (:db/ident page-entity)}
-                                             (let [new-uuid* (if (uuid? page-uuid)
-                                                               page-uuid
-                                                               (if journal-day
-                                                                 (common-uuid/gen-uuid :journal-page-uuid journal-day)
-                                                                 (common-uuid/gen-uuid)))
-                                                   new-uuid (if skip-existing-page-check?
-                                                              new-uuid*
-                                                              (or
-                                                               (cond page-entity       (:block/uuid page-entity)
-                                                                     (uuid? page-uuid) page-uuid)
-                                                               new-uuid*))]
-                                               {:block/uuid new-uuid}))
-                                           (when namespace?
-                                             (let [namespace' (first (common-util/split-last "/" original-page-name))]
-                                               (when-not (string/blank? namespace')
-                                                 {:block/namespace {:block/name (common-util/page-name-sanity-lc namespace')}})))
-                                           (when (and with-timestamp? (or skip-existing-page-check? (not page-entity))) ;; Only assign timestamp on creating new entity
-                                             (let [current-ms (common-util/time-ms)]
-                                               {:block/created-at current-ms
-                                                :block/updated-at current-ms}))
-                                           (if journal-day
-                                             (cond-> {:block/type "journal"
-                                                      :block/journal-day journal-day}
-                                               db-based?
-                                               (assoc :block/tags [:logseq.class/Journal]))
-                                             {}))]
-                                 [page page-entity])
-
-                               :else
-                               (let [page (cond (and (map? original-page-name) (:block/uuid original-page-name))
-                                                original-page-name
-
-                                                (map? original-page-name)
-                                                (assoc original-page-name :block/uuid (or page-uuid (d/squuid)))
-
-                                                :else
-                                                nil)]
-                                 [page nil]))]
+                                (and original-page-name (string? original-page-name))
+                                (page-name-string->map original-page-name db date-formatter
+                                                       (assoc options :with-timestamp? with-timestamp?))
+                                :else
+                                (let [page (cond (and (map? original-page-name) (:block/uuid original-page-name))
+                                                 original-page-name
+
+                                                 (map? original-page-name)
+                                                 (assoc original-page-name :block/uuid (or page-uuid (d/squuid)))
+
+                                                 :else
+                                                 nil)]
+                                  [page nil]))]
       (when page
         (let [type (if class? "class" (or (:block/type page) "page"))]
           (assoc page :block/type type))))))
@@ -431,11 +447,14 @@
           refs (->> (ref->map-fn *refs false)
                     (remove nil?)
                     (map (fn [ref]
-                           (if-let [entity (ldb/get-case-page db (:block/title ref))]
-                             (if (= (:db/id parse-block) (:db/id entity))
-                               ref
-                               (select-keys entity [:block/uuid :block/title :block/name]))
-                             ref))))
+                           (let [ref' (if-let [entity (ldb/get-case-page db (:block/title ref))]
+                              (if (= (:db/id parse-block) (:db/id entity))
+                                ref
+                                (select-keys entity [:block/uuid :block/title :block/name]))
+                              ref)]
+                             (cond-> ref'
+                               (:block.temp/original-page-name ref)
+                               (assoc :block.temp/original-page-name (:block.temp/original-page-name ref)))))))
           tags (ref->map-fn *structured-tags true)]
       (assoc block
              :refs refs

+ 109 - 68
deps/graph-parser/src/logseq/graph_parser/exporter.cljs

@@ -26,7 +26,8 @@
             [logseq.db.frontend.db-ident :as db-ident]
             [logseq.db.frontend.property.build :as db-property-build]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
-            [logseq.graph-parser.property :as gp-property]))
+            [logseq.graph-parser.property :as gp-property]
+            [logseq.graph-parser.block :as gp-block]))
 
 (defn- add-missing-timestamps
   "Add updated-at or created-at timestamps if they doesn't exist"
@@ -75,7 +76,9 @@
                     (select-keys tag-block [:block/created-at :block/updated-at]))))))))
 
 (defn- get-page-uuid [page-names-to-uuids page-name]
-  (or (get page-names-to-uuids page-name)
+  (or (get page-names-to-uuids (if (string/includes? (str page-name) "#")
+                                 (string/lower-case (gp-block/sanitize-hashtag-name page-name))
+                                 page-name))
       (throw (ex-info (str "No uuid found for page name " (pr-str page-name))
                       {:page-name page-name}))))
 
@@ -94,7 +97,9 @@
         (update :block/tags
                 (fn [tags]
                   ;; Don't lazy load as this needs to build before the page does
-                  (vec (keep #(convert-tag-to-class db % page-names-to-uuids tag-classes all-idents) tags))))
+                  (vec (keep #(if (= % :logseq.class/Journal)
+                                %
+                                (convert-tag-to-class db % page-names-to-uuids tag-classes all-idents)) tags))))
         (seq page-tags)
         (merge {:logseq.property/page-tags page-tags})))
     block))
@@ -117,23 +122,27 @@
 
 (defn- update-block-tags
   [block db tag-classes page-names-to-uuids all-idents]
-  (if (seq (:block/tags block))
-    (let [original-tags (remove :block.temp/new-class (:block/tags block))]
-      (-> block
-          (update :block/title
-                  content-without-tags-ignore-case
-                  (->> original-tags
-                       (filter #(tag-classes (:block/name %)))
-                       (map :block/title)))
-          (update :block/title
-                  db-content/replace-tags-with-page-refs
-                  (->> original-tags
-                       (remove #(tag-classes (:block/name %)))
-                       (map #(add-uuid-to-page-map % page-names-to-uuids))))
-          (update :block/tags
-                  (fn [tags]
-                    (vec (keep #(convert-tag-to-class db % page-names-to-uuids tag-classes all-idents) tags))))))
-    block))
+  (let [block'
+        (if (seq (:block/tags block))
+          (let [original-tags (remove :block.temp/new-class (:block/tags block))]
+            (-> block
+                (update :block/title
+                        content-without-tags-ignore-case
+                        (->> original-tags
+                             (filter #(tag-classes (:block/name %)))
+                             (map :block/title)))
+                (update :block/title
+                        db-content/replace-tags-with-page-refs
+                        (->> original-tags
+                             (remove #(tag-classes (:block/name %)))
+                             (map #(add-uuid-to-page-map % page-names-to-uuids))))
+                (update :block/tags
+                        (fn [tags]
+                          (vec (keep #(convert-tag-to-class db % page-names-to-uuids tag-classes all-idents) tags))))))
+          block)]
+    (cond-> block'
+      (macro-util/query-macro? (:block/title block))
+      (update :block/tags (fnil conj []) :logseq.class/Query))))
 
 (defn- update-block-marker
   "If a block has a marker, convert it to a task object"
@@ -247,7 +256,7 @@
 
 (defn- get-property-schema [property-schemas kw]
   (or (get property-schemas kw)
-        (throw (ex-info (str "No property schema found for " (pr-str kw)) {}))))
+      (throw (ex-info (str "No property schema found for " (pr-str kw)) {}))))
 
 (defn- infer-property-schema-and-get-property-change
   "Infers a property's schema from the given _user_ property value and adds new ones to
@@ -295,8 +304,8 @@
 (def all-built-in-property-names
   "All built-in property names as a set of keywords"
   (-> built-in-property-name-to-idents keys set
-      ;; :filters is not in built-in-properties because it maps to 2 new properties
-      (conj :filters)))
+      ;; built-in-properties that map to new properties
+      (set/union #{:filters :query-table :query-properties :query-sort-by :query-sort-desc})))
 
 (def all-built-in-names
   "All built-in properties and classes as a set of keywords"
@@ -316,38 +325,66 @@
 (assert (set/subset? file-built-in-property-names all-built-in-property-names)
         "All file-built-in properties are used in db graph")
 
+(def query-table-special-keys
+  "Special keywords in previous query table"
+  {:page :block/title
+   :block :block/title
+   :created-at :block/created-at
+   :updated-at :block/updated-at})
+
+(defn- translate-query-properties [prop-value all-idents options]
+  (let [property-classes (set (map keyword (:property-classes options)))]
+    (try
+      (->> (edn/read-string prop-value)
+           (keep #(cond (get query-table-special-keys %)
+                        (get query-table-special-keys %)
+                        (property-classes %)
+                        :block/tags
+                        (= :tags %)
+                         ;; This could also be :logseq.property/page-tags
+                        :block/tags
+                        :else
+                        (get-ident @all-idents %)))
+           distinct
+           vec)
+      (catch :default e
+        (js/console.error "Translating query properties failed with:" e)
+        []))))
+
 (defn- update-built-in-property-values
   [props {:keys [ignored-properties all-idents]} {:block/keys [title name]} options]
-  (->> props
-       (keep (fn [[prop val]]
-               ;; FIXME: Migrate :filters to :logseq.property.linked-references/* properties
-               (if (#{:icon :filters} prop)
-                 (do (swap! ignored-properties
-                            conj
-                            {:property prop :value val :location (if name {:page name} {:block title})})
-                     nil)
-                 [(built-in-property-name-to-idents prop)
-                  (case prop
-                    :query-properties
-                    (let [property-classes (set (map keyword (:property-classes options)))]
-                      (try
-                        (mapv #(cond (#{:page :block :created-at :updated-at} %)
-                                     %
-                                     (property-classes %)
-                                     :block/tags
-                                     (= :tags %)
-                                     ;; This could also be :logseq.property/page-tags
-                                     :block/tags
-                                     :else
-                                     (get-ident @all-idents %))
-                              (edn/read-string val))
-                        (catch :default e
-                          (js/console.error "Translating query properties failed with:" e)
-                          [])))
-                    :query-sort-by
-                    (if (#{:page :block :created-at :updated-at} (keyword val)) (keyword val) (get-ident @all-idents (keyword val)))
-                    val)])))
-       (into {})))
+  (let [m
+        (->> props
+             (keep (fn [[prop prop-value]]
+                     ;; FIXME: Migrate :filters to :logseq.property.linked-references/* properties
+                     (if (#{:icon :filters} prop)
+                       (do (swap! ignored-properties
+                                  conj
+                                  {:property prop :value prop-value :location (if name {:page name} {:block title})})
+                           nil)
+                       (case prop
+                         :query-properties
+                         (when-let [cols (not-empty (translate-query-properties prop-value all-idents options))]
+                           [:logseq.property.table/ordered-columns cols])
+                         :query-table
+                         [:logseq.property.view/type
+                          (if prop-value :logseq.property.view/type.table :logseq.property.view/type.list)]
+                         :query-sort-by
+                         [:logseq.property.table/sorting
+                          [{:id (or (query-table-special-keys (keyword prop-value))
+                                    (get-ident @all-idents (keyword prop-value)))
+                            :asc? true}]]
+                         ;; ignore to handle below
+                         :query-sort-desc
+                         nil
+                         ;; else
+                         [(built-in-property-name-to-idents prop) prop-value]))))
+             (into {}))]
+    (cond-> m
+      (and (contains? props :query-sort-desc) (:query-sort-by props))
+      (update :logseq.property.table/sorting
+              (fn [v]
+                (assoc-in v [0 :asc?] (not (:query-sort-desc props))))))))
 
 (defn- update-page-or-date-values
   "Converts :node or :date names to entity values"
@@ -685,12 +722,12 @@
   data for subsequent steps"
   [conn pages blocks {:keys [tag-classes property-classes property-parent-classes notify-user import-state]
                       :as options}]
-  (let [all-pages (->> (extract/with-ref-pages pages blocks)
-                       ;; remove unused property pages unless the page has content
-                       (remove #(and (contains? (into property-classes property-parent-classes) (keyword (:block/name %)))
-                                     (not (:block/file %))))
-                       ;; remove file path relative
-                       (map #(dissoc % :block/file)))
+  (let [all-pages* (->> (extract/with-ref-pages pages blocks)
+                        ;; remove unused property pages unless the page has content
+                        (remove #(and (contains? (into property-classes property-parent-classes) (keyword (:block/name %)))
+                                      (not (:block/file %))))
+                        ;; remove file path relative
+                        (map #(dissoc % :block/file)))
         existing-pages (keep #(first
                                ;; don't fetch built-in as that would give the wrong entity if a user used
                                ;; a db-only built-in property name e.g. description
@@ -699,14 +736,16 @@
                                       :where [?b :block/name ?name] [(missing? $ ?b :logseq.property/built-in?)]]
                                     @conn
                                     (:block/name %)))
-                             all-pages)
+                             all-pages*)
         existing-page-names-to-uuids (into {} (map (juxt :block/name :block/uuid) existing-pages))
-        new-pages (->> all-pages
-                       (remove #(contains? existing-page-names-to-uuids (:block/name %)))
-                       ;; fix extract incorrectly assigning user pages built-in uuids
-                       (map #(if (contains? all-built-in-names (keyword (:block/name %)))
-                               (assoc % :block/uuid (d/squuid))
-                               %)))
+        existing-page? #(contains? existing-page-names-to-uuids (:block/name %))
+        ;; fix extract incorrectly assigning new user pages built-in uuids
+        all-pages (map #(if (and (not (existing-page? %))
+                                 (contains? all-built-in-names (keyword (:block/name %))))
+                          (assoc % :block/uuid (d/squuid))
+                          %)
+                       all-pages*)
+        new-pages (remove existing-page? all-pages)
         page-names-to-uuids (merge existing-page-names-to-uuids
                                    (into {} (map (juxt :block/name :block/uuid) new-pages)))
         all-pages-m (mapv #(handle-page-properties % @conn page-names-to-uuids all-pages options)
@@ -897,7 +936,9 @@
                                 extract-options
                                 {:db db})]
     (cond (contains? common-config/mldoc-support-formats format)
-          (extract/extract file content extract-options')
+          (-> (extract/extract file content extract-options')
+              (update :pages (fn [pages]
+                               (map #(dissoc % :block.temp/original-page-name) pages))))
 
           (common-config/whiteboard? file)
           (-> (extract/extract-whiteboard-edn file content extract-options')
@@ -1201,4 +1242,4 @@
        (export-favorites-from-config-edn conn repo-or-conn config {})
        (export-class-properties conn repo-or-conn)
        {:import-state (:import-state doc-options)
-        :files files}))))
+        :files files}))))

+ 10 - 5
deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

@@ -168,9 +168,13 @@
       ;; Counts
       ;; Includes journals as property values e.g. :logseq.task/deadline
       (is (= 18 (count (d/q '[:find ?b :where [?b :block/type "journal"]] @conn))))
+      (is (= 18 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Journal]] @conn))))
+
+      (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Task]] @conn))))
+      (is (= 1 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn))))
 
       ;; Don't count pages like url.md that have properties but no content
-      (is (= 7
+      (is (= 8
              (count (->> (d/q '[:find [(pull ?b [:block/title :block/type]) ...]
                                 :where [?b :block/title] [_ :block/page ?b]] @conn)
                          (filter #(= "page" (:block/type %))))))
@@ -285,9 +289,10 @@
       (is (= #{"gpt"}
              (:block/alias (readable-properties @conn (find-page-by-name @conn "chat-gpt")))))
 
-      (is (= {:logseq.property/query-sort-by :user.property/prop-num
-              :logseq.property/query-properties [:block :page :user.property/prop-string :user.property/prop-num]
-              :logseq.property/query-table true}
+      (is (= {:logseq.property.table/sorting [{:id :user.property/prop-num, :asc? false}]
+              :logseq.property.view/type "Table View"
+              :logseq.property.table/ordered-columns [:block/title :user.property/prop-string :user.property/prop-num]
+              :block/tags [:logseq.class/Query]}
              (readable-properties @conn (find-block-by-content @conn "{{query (property :prop-string)}}")))
           "query block has correct query properties"))
 
@@ -487,7 +492,7 @@
 
 (deftest-async export-files-with-property-parent-classes-option
   (p/let [file-graph-dir "test/resources/exporter-test-graph"
-          files (mapv #(node-path/join file-graph-dir %) ["pages/CreativeWork.md" "pages/Movie.md"])
+          files (mapv #(node-path/join file-graph-dir %) ["pages/CreativeWork.md" "pages/Movie.md" "pages/type.md"])
           conn (d/create-conn db-schema/schema-for-db-based-graph)
           _ (d/transact! conn (sqlite-create-graph/build-db-initial-data "{}"))
           _ (import-files-to-db files conn {:property-parent-classes ["parent"]})]

+ 2 - 1
deps/graph-parser/test/resources/exporter-test-graph/journals/2024_02_14.md

@@ -6,4 +6,5 @@
 - {{query (property :prop-string)}}
   query-table:: true
   query-properties:: [:block :page :prop-string :prop-num]
-  query-sort-by:: prop-num
+  query-sort-by:: prop-num
+  query-sort-desc:: true

+ 1 - 0
deps/graph-parser/test/resources/exporter-test-graph/pages/page with #tag.md

@@ -0,0 +1 @@
+- HA

+ 1 - 0
deps/graph-parser/test/resources/exporter-test-graph/pages/type.md

@@ -0,0 +1 @@
+url:: http://www.w3.org/1999/02/22-rdf-syntax-ns#type

+ 27 - 17
deps/outliner/src/logseq/outliner/core.cljs

@@ -22,6 +22,7 @@
             [logseq.db.frontend.order :as db-order]
             [logseq.outliner.pipeline :as outliner-pipeline]
             [logseq.graph-parser.text :as text]
+            [logseq.common.util.macro :as macro-util]
             [logseq.db.frontend.class :as db-class]))
 
 (def ^:private block-map
@@ -355,16 +356,14 @@
           eid (or db-id (when block-uuid [:block/uuid block-uuid]))
           block-entity (d/entity db eid)
           page? (ldb/page? block-entity)
-          page-title-changed? (and page? (:block/title m*)
-                                   (not= (:block/title m*) (:block/title block-entity)))
+          block-title (:block/title m*)
+          page-title-changed? (and page? block-title
+                                   (not= block-title (:block/title block-entity)))
+          _ (when (and db-based? page? block-title)
+              (outliner-validate/validate-page-title-characters block-title {:node m*}))
           m* (if (and db-based? page-title-changed?)
-               (let [page-name (common-util/page-name-sanity-lc (:block/title m*))]
-                 (when (string/blank? page-name)
-                   (throw (ex-info "Page title can't be blank"
-                                   {:type :notification
-                                    :payload {:message "Page title can't be blank"
-                                              :type :error}
-                                    :node m*})))
+               (let [_ (outliner-validate/validate-page-title (:block/title m*) {:node m*})
+                     page-name (common-util/page-name-sanity-lc (:block/title m*))]
                  (assoc m* :block/name page-name))
                m*)
           _ (when (and db-based?
@@ -428,6 +427,15 @@
           (when (seq tx-data)
             (swap! txs-state (fn [txs] (concat txs tx-data))))))
 
+      ;; Add Query class when a query macro is typed or pasted
+      (when (and db-based?
+                 (not= (:block/title m*) (:block/title block-entity))
+                 (macro-util/query-macro? (:block/title m*))
+                 (empty? (:block/tags block-entity)))
+        (swap! txs-state (fn [txs]
+                           (conj (vec txs)
+                                 [:db/add (:db/id block-entity) :block/tags :logseq.class/Query]))))
+
       this))
 
   (-del [this txs-state conn]
@@ -812,18 +820,20 @@
   [:pre [(seq blocks)]]
   (let [top-level-blocks (filter-top-level-blocks @conn blocks)
         non-consecutive? (and (> (count top-level-blocks) 1) (seq (ldb/get-non-consecutive-blocks @conn top-level-blocks)))
-        top-level-blocks (get-top-level-blocks top-level-blocks non-consecutive?)
+        top-level-blocks (->> (get-top-level-blocks top-level-blocks non-consecutive?)
+                              (remove ldb/page?))
         txs-state (ds/new-outliner-txs-state)
         block-ids (map (fn [b] [:block/uuid (:block/uuid b)]) top-level-blocks)
         start-block (first top-level-blocks)
         end-block (last top-level-blocks)]
-    (if (or
-         (= 1 (count top-level-blocks))
-         (= start-block end-block))
-      (delete-block conn txs-state start-block)
-      (doseq [id block-ids]
-        (let [node (d/entity @conn id)]
-          (otree/-del node txs-state conn))))
+    (when (seq top-level-blocks)
+      (if (or
+           (= 1 (count top-level-blocks))
+           (= start-block end-block))
+        (delete-block conn txs-state start-block)
+        (doseq [id block-ids]
+          (let [node (d/entity @conn id)]
+            (otree/-del node txs-state conn)))))
     {:tx-data @txs-state}))
 
 (defn- move-to-original-position?

+ 24 - 6
deps/outliner/src/logseq/outliner/property.cljs

@@ -19,8 +19,11 @@
             [malli.util :as mu]
             [clojure.set :as set]))
 
-;; schema -> type, cardinality, object's class
-;;           min, max -> string length, number range, cardinality size limit
+(defn- throw-error-if-read-only-property
+  [property-ident]
+  (when (db-property/read-only-properties property-ident)
+    (throw (ex-info "Read-only property value shouldn't be edited"
+                    {:property property-ident}))))
 
 (defn- build-property-value-tx-data
   ([block property-id value]
@@ -89,6 +92,8 @@
 (defn- update-property
   [conn db-ident property schema {:keys [property-name properties]}]
   (when (and (some? property-name) (not= property-name (:block/title property)))
+    (outliner-validate/validate-page-title property-name {:node property})
+    (outliner-validate/validate-page-title-characters property-name {:node property})
     (outliner-validate/validate-block-title @conn property-name property))
 
   (let [changed-property-attrs
@@ -167,6 +172,7 @@
 (defn- raw-set-block-property!
   "Adds the raw property pair (value not modified) to the given block if the property value is valid"
   [conn block property property-type new-value]
+  (throw-error-if-read-only-property (:db/ident property))
   (let [k-name (:block/title property)
         property-id (:db/ident property)
         schema (get-property-value-schema @conn property-type property)]
@@ -240,6 +246,7 @@
   can pass \"value\" instead of the property value entity. Also handle db
   attributes as properties"
   [conn block-eid property-id v]
+  (throw-error-if-read-only-property property-id)
   (let [block-eid (->eid block-eid)
         _ (assert (qualified-keyword? property-id) "property-id should be a keyword")
         block (d/entity @conn block-eid)
@@ -264,6 +271,7 @@
    NOTE: This fn only works for properties with cardinality equal to `one`."
   [conn block-ids property-id v]
   (assert property-id "property-id is nil")
+  (throw-error-if-read-only-property property-id)
   (let [block-eids (map ->eid block-ids)
         property (d/entity @conn property-id)
         _ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
@@ -285,6 +293,7 @@
 
 (defn batch-remove-property!
   [conn block-ids property-id]
+  (throw-error-if-read-only-property property-id)
   (let [block-eids (map ->eid block-ids)
         blocks (keep (fn [id] (d/entity @conn id)) block-eids)
         block-id-set (set (map :db/id blocks))]
@@ -316,12 +325,21 @@
 
 (defn remove-block-property!
   [conn eid property-id]
-  (let [eid (->eid eid)]
-    (if (contains? db-property/db-attribute-properties property-id)
-      (when-let [block (d/entity @conn eid)]
+  (throw-error-if-read-only-property property-id)
+  (let [eid (->eid eid)
+        block (d/entity @conn eid)]
+    (cond
+      (and (ldb/class? block) (= property-id :logseq.property/parent))
+      (ldb/transact! conn
+                     [[:db/add (:db/id block) :logseq.property/parent :logseq.class/Root]]
+                     {:outliner-op :save-block})
+
+      (contains? db-property/db-attribute-properties property-id)
+      (when block
         (ldb/transact! conn
                        [[:db/retract (:db/id block) property-id]]
                        {:outliner-op :save-block}))
+      :else
       (batch-remove-property! conn [eid] property-id))))
 
 (defn delete-property-value!
@@ -343,7 +361,7 @@
   (let [tags' (filter ldb/class? tags)
         result (map
                 (fn [id] (d/entity db id))
-                (set (mapcat ldb/get-class-parents tags')))]
+                (mapcat ldb/get-class-parents tags'))]
     (set result)))
 
 (defn ^:api get-class-properties

+ 23 - 2
deps/outliner/src/logseq/outliner/validate.cljs

@@ -1,8 +1,29 @@
 (ns logseq.outliner.validate
-  "Reusable validations from outliner level and above"
-  (:require [datascript.core :as d]
+  "Reusable validations from outliner level and above. Most validations throw
+  errors so the user action stops immediately to display a notification"
+  (:require [clojure.string :as string]
+            [datascript.core :as d]
             [logseq.db :as ldb]))
 
+(defn ^:api validate-page-title-characters
+  "Validates characters that must not be in a page title"
+  [page-title meta-m]
+  (when (string/includes? page-title "#")
+    (throw (ex-info "Page name can't include \"#\"."
+                    (merge meta-m
+                           {:type :notification
+                            :payload {:message "Page name can't include \"#\"."
+                                      :type :warning}})))))
+
+(defn ^:api validate-page-title
+  [page-title meta-m]
+  (when (string/blank? page-title)
+    (throw (ex-info "Page name can't be blank"
+                    (merge meta-m
+                           {:type :notification
+                            :payload {:message "Page name can't be blank."
+                                      :type :error}})))))
+
 (defn ^:api validate-built-in-pages
   "Validates built-in pages shouldn't be modified"
   [entity]

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

@@ -2,7 +2,6 @@
   "Table"
   (:require [logseq.shui.table.impl :as impl]
             [dommy.core :refer-macros [sel1]]
-            [cljs-bean.core :as bean]
             [rum.core :as rum]))
 
 (defn- get-head-container

+ 7 - 5
deps/shui/src/logseq/shui/table/impl.cljc

@@ -11,9 +11,9 @@
   (not (false? (get visible-columns (column-id column)))))
 
 (defn visible-columns
-  [columns visible-columns]
-  (if (seq visible-columns)
-    (filter #(column-visible? % visible-columns) columns)
+  [columns visible-columns']
+  (if (seq visible-columns')
+    (filter #(column-visible? % visible-columns') columns)
     columns))
 
 (defn sort-rows
@@ -58,7 +58,9 @@
   (sort-rows rows sorting columns))
 
 (defn rows
-  [{:keys [rows columns sorting row-filter]}]
-  (let [rows' (if row-filter (filter row-filter rows) rows)]
+  [{:keys [columns sorting row-filter]
+    :as opts}]
+  (let [rows' (:rows opts)
+        rows' (if row-filter (filter row-filter rows') rows')]
     (cond-> rows'
       (seq sorting) (sort-rows sorting columns))))

+ 0 - 2
gulpfile.js

@@ -4,7 +4,6 @@ const cp = require('child_process')
 const exec = utils.promisify(cp.exec)
 const path = require('path')
 const gulp = require('gulp')
-const cleanCSS = require('gulp-clean-css')
 const del = require('del')
 const ip = require('ip')
 
@@ -32,7 +31,6 @@ const css = {
 
   _optimizeCSSForRelease () {
     return gulp.src(path.join(outputPath, 'css', 'style.css'))
-      .pipe(cleanCSS())
       .pipe(gulp.dest(path.join(outputPath, 'css')))
   }
 }

+ 1 - 1
libs/src/LSPlugin.ts

@@ -815,7 +815,7 @@ export interface IEditorProxy extends Record<string, any> {
       hide: boolean
       public: boolean
     }>,
-    opts?: {}) => Promise<IEntityID>
+    opts?: { name?: string }) => Promise<IEntityID>
 
   // block property related APIs
   upsertBlockProperty: (

+ 2 - 2
package.json

@@ -17,14 +17,14 @@
         "del": "^6.0.0",
         "glob": "9.0.0",
         "gulp": "^4.0.2",
-        "gulp-clean-css": "^4.3.0",
+        "gulp-postcss": "^10.0.0",
         "ip": "1.1.9",
         "karma": "^6.4.4",
         "karma-chrome-launcher": "^3.2.0",
         "karma-cljs-test": "^0.1.0",
         "npm-run-all": "^4.1.5",
         "playwright": "=1.44.0",
-        "postcss": "8.4.17",
+        "postcss": "^8.4.47",
         "postcss-cli": "10.0.0",
         "postcss-functions": "^4.0.2",
         "postcss-import": "15.0.0",

+ 3 - 3
src/main/frontend/commands.cljs

@@ -25,7 +25,8 @@
             [logseq.common.util.page-ref :as page-ref]
             [promesa.core :as p]
             [frontend.handler.file-based.status :as file-based-status]
-            [frontend.handler.db-based.property :as db-property-handler]))
+            [frontend.handler.db-based.property :as db-property-handler]
+            [logseq.common.util.macro :as macro-util]))
 
 ;; TODO: move to frontend.handler.editor.commands
 
@@ -342,8 +343,7 @@
 
       ;; advanced
       [["Query"
-        [[:editor/input "{{query }}" {:backward-pos 2}]
-         [:editor/set-property :block/tags (:db/id (db/entity :logseq.class/Query))]
+        [[:editor/input (str macro-util/query-macro " }}") {:backward-pos 2}]
          [:editor/exit]]
         query-doc
         :icon/query

+ 1 - 1
src/main/frontend/common.css

@@ -132,7 +132,7 @@ body {
 
   iframe {
     width: 100%;
-    margin: 1rem 0;
+    margin: 0.25rem 0;
   }
 
   img,

+ 10 - 1
src/main/frontend/common/search_fuzzy.cljs

@@ -42,7 +42,16 @@
         ;; boost score if we have an exact match including punctuation
         (empty? q) (+ score'
                       (str-len-distance query s)
-                      (if (<= 0 (.indexOf ostr oquery)) MAX-STRING-LENGTH 0))
+                      (cond
+                        (<= 0 (.indexOf ostr oquery))
+                        MAX-STRING-LENGTH
+
+                        (<= 0 (.indexOf (string/lower-case ostr) (string/lower-case oquery)))
+                        (- MAX-STRING-LENGTH 0.1)
+
+                        :else
+                        0)
+                      (if (empty? s) 1 0))
         (empty? s) 0
         :else (if (= (first q) (first s))
                 (recur (rest q)

+ 67 - 47
src/main/frontend/components/block.cljs

@@ -328,11 +328,12 @@
                         (let [*local-selected? (atom local?)]
                           (-> (shui/dialog-confirm!
                                 [:div.text-xs.opacity-60.-my-2
-                                 [:label.flex.gap-1.items-center
-                                  (shui/checkbox
-                                    {:default-checked @*local-selected?
-                                     :on-checked-change #(reset! *local-selected? %)})
-                                  (t (if local? :asset/physical-delete ""))]]
+                                 (when local?
+                                   [:label.flex.gap-1.items-center
+                                    (shui/checkbox
+                                      {:default-checked @*local-selected?
+                                       :on-checked-change #(reset! *local-selected? %)})
+                                    (t :asset/physical-delete)])]
                                 {:title (t :asset/confirm-delete (.toLocaleLowerCase (t :text/image)))
                                  :outside-cancel? true})
                             (p/then (fn []
@@ -776,25 +777,21 @@
     [:span.warning.mr-1 {:title "Node ref invalid"}
    (->ref id)]))
 
-(rum/defcs page-cp < db-mixins/query rum/reactive
-  {:init (fn [state]
-           (let [page (last (:rum/args state))]
-             (assoc state ::entity
-                    (if (e/entity? page)
-                      page
-                      ;; Use uuid when available to uniquely identify case sensitive contexts
-                      (db/get-page (or (:block/uuid page)
-                                       (when-let [s (:block/name page)]
-                                         (let [s (string/trim s)
-                                               s (if (string/starts-with? s db-content/page-ref-special-chars)
-                                                   (common-util/safe-subs s 2)
-                                                   s)]
-                                           s))))))))}
+(rum/defcs page-cp-inner < db-mixins/query rum/reactive
   "Component for a page. `page` argument contains :block/name which can be (un)sanitized page name.
    Keys for `config`:
    - `: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?] :as config} page]
-  (let [entity (::entity state)]
+  (let [entity (if (e/entity? page)
+                 page
+                 ;; Use uuid when available to uniquely identify case sensitive contexts
+                 (db/get-page (or (:block/uuid page)
+                                  (when-let [s (:block/name page)]
+                                    (let [s (string/trim s)
+                                          s (if (string/starts-with? s db-content/page-ref-special-chars)
+                                              (common-util/safe-subs s 2)
+                                              s)]
+                                      s)))))]
     (let [entity (when entity (db/sub-block (:db/id entity)))]
       (cond
         entity
@@ -822,6 +819,11 @@
         :else
         nil))))
 
+(rum/defc page-cp
+  [config page]
+  (rum/with-key (page-cp-inner config page)
+    (or (str (:block/uuid page)) (:block/name page))))
+
 (rum/defc asset-reference
   [config title path]
   (let [repo (state/get-current-repo)
@@ -1021,7 +1023,7 @@
                                  [(breadcrumb config repo id {:indent? true})
                                   (blocks-container
                                     (assoc config :id (str id) :preview? true)
-                                    (db/get-block-and-children repo id))]])]
+                                    [(db/entity [:block/uuid id])])]])]
     (popup-preview-impl children
       {:visible? visible? :set-visible! set-visible!
        :*timer *timer :*timer1 *timer1
@@ -1033,7 +1035,8 @@
              (db-async/<get-block (state/get-current-repo) block-id :children? false))
            state)}
   [config id label]
-  (when (not= (:block/uuid (:block config)) id)
+  (if (= (:block/uuid (:block config)) id)
+    [:span.warning.text-sm "Self reference"]
     (if-let [block-id (if (uuid? id) id (parse-uuid id))]
       (if (state/sub-async-query-loading (str block-id))
         [:span "Loading..."]
@@ -1279,7 +1282,7 @@
     (show-link? config metadata s full_text)
     (media-link config url s label metadata full_text)
 
-    (util/electron?)
+    (or (util/electron?) (config/db-based-graph? (state/get-current-repo)))
     (let [path (cond
                  (string/starts-with? s "file://")
                  (string/replace s "file://" "")
@@ -1931,7 +1934,7 @@
      {:class (util/classnames [{:is-order-list order-list?
                                 :bullet-closed collapsed?
                                 :bullet-hidden (:hide-bullet? config)}])}
-     (when (or (not fold-button-right?) collapsable?)
+     (when (and (or (not fold-button-right?) collapsable?) (not (:table? config)))
        [:a.block-control
         {:id       (str "control-" uuid)
          :on-click (fn [event]
@@ -2534,10 +2537,10 @@
         mouse-down-key (if (util/ios?)
                          :on-click
                          :on-pointer-down) ; TODO: it seems that Safari doesn't work well with on-pointer-down
-
         attrs (cond->
                {:blockid       (str uuid)
-                :class (when (:property-block? config) "jtrigger")
+                :class (util/classnames [{:jtrigger (:property-block? config)
+                                          :!cursor-pointer (or (:property? config) (:page-title? config))}])
                 :containerid (:container-id config)
                 :data-type (name block-type)
                 :style {:width "100%"
@@ -2550,14 +2553,26 @@
 
                 (not block-ref?)
                 (assoc mouse-down-key (fn [e]
-                                        (cond (:from-journals? config)
-                                              (do
-                                                (.preventDefault e)
-                                                (route-handler/redirect-to-page! (:block/uuid block)))
-                                              (ldb/journal? block)
+                                        (let [journal-title? (:from-journals? config)]
+                                          (cond
+                                            (util/right-click? e)
+                                            nil
+
+                                            (and journal-title? (gobj/get e "shiftKey"))
+                                            (do
                                               (.preventDefault e)
-                                              :else
-                                              (block-content-on-pointer-down e block block-id content edit-input-id config)))))]
+                                              (state/sidebar-add-block! repo (:db/id block) :page))
+
+                                            journal-title?
+                                            (do
+                                              (.preventDefault e)
+                                              (route-handler/redirect-to-page! (:block/uuid block)))
+
+                                            (ldb/journal? block)
+                                            (.preventDefault e)
+
+                                            :else
+                                            (block-content-on-pointer-down e block block-id content edit-input-id config))))))]
     [:div.block-content.inline
      (cond-> {:id (str "block-content-" uuid)
               :on-pointer-up (fn [e]
@@ -2686,7 +2701,8 @@
       [:div.flex.flex-1.flex-row.gap-1.items-center
        (if (and edit? editor-box)
          [:div.editor-wrapper.flex.flex-1
-          {:id editor-id}
+          {:id editor-id
+           :class (util/classnames [{:opacity-50 (boolean (or (ldb/built-in? block) (ldb/journal? block)))}])}
           (ui/catch-error
            (ui/block-error "Something wrong in the editor" {})
            (editor-box {:block block
@@ -2728,14 +2744,14 @@
                                     (editor-handler/edit-block! block :max))}
                 svg/edit])])])
 
-       (when-not (or (:table? config) (:page-title? config))
+       (when-not (or (:table? config) (:property? config) (:page-title? config))
          (block-refs-count block refs-count *hide-block-refs?))
 
-       (when-not (or (:block-ref? config) (:table? config))
+       (when-not (or (:block-ref? config) (:table? config) (:property? config))
          (when (and db-based? (seq (:block/tags block)))
            (tags-cp (assoc config :block/uuid (:block/uuid block)) block)))]
 
-      (when (and (not (:table? config))
+      (when (and (not (or (:table? config) (:property? config)))
                  (not hide-block-refs?)
                  (> refs-count 0)
                  (not (:page-title? config)))
@@ -3089,6 +3105,7 @@
         editing? (or (state/sub-editing? [container-id (:block/uuid block)])
                      (state/sub-editing? [:unknown-container (:block/uuid block)]))
         table? (:table? config*)
+        property? (:property? config*)
         custom-query? (boolean (:custom-query? config*))
         ref-or-custom-query? (or ref? custom-query?)
         *navigating-block (get container-state ::navigating-block)
@@ -3123,9 +3140,12 @@
         db-based? (config/db-based-graph? repo)]
     [:div.ls-block
      (cond->
-      {:id (str "ls-block-" uuid)
+      {:id (str "ls-block-"
+                ;; container-id "-"
+                uuid)
        :blockid (str uuid)
        :containerid container-id
+       :data-is-property (ldb/property? block)
        :ref #(when (nil? @*ref) (reset! *ref %))
        :data-collapsed (and collapsed? has-child?)
        :class (str "id" uuid " "
@@ -3154,13 +3174,13 @@
        custom-query?
        (assoc :data-query true))
 
-     (when (and ref? breadcrumb-show? (not table?))
+     (when (and ref? breadcrumb-show? (not (or table? property?)))
        (breadcrumb config repo uuid {:show-page? false
                                      :indent? true
                                      :navigating-block *navigating-block}))
 
      ;; only render this for the first block in each container
-     (when (and top? (not table?))
+     (when (and top? (not (or table? property?)))
        (dnd-separator-wrapper block children block-id slide? true false))
 
      (when-not (:hide-title? config)
@@ -3180,7 +3200,7 @@
          :on-mouse-leave (fn [e]
                            (block-mouse-leave e *control-show? block-id doc-mode?))}
 
-        (when (and (not slide?) (not in-whiteboard?) (not table?))
+        (when (and (not slide?) (not in-whiteboard?) (not property?))
           (let [edit? (or editing?
                           (= uuid (:block/uuid (state/get-edit-block))))]
             (block-control (assoc config :hide-bullet? (:page-title? config))
@@ -3191,7 +3211,7 @@
                             :*control-show? *control-show?
                             :edit? edit?})))
 
-        (when (and @*show-left-menu? (not in-whiteboard?) (not table?))
+        (when (and @*show-left-menu? (not in-whiteboard?) (not (or table? property?)))
           (block-left-menu config block))
 
         [:div.flex.flex-col.w-full
@@ -3238,22 +3258,22 @@
                                          :edit? editing?
                                          :hide-block-refs-count? hide-block-refs-count?}))])]
 
-         (when (and db-based? (not collapsed?) (not table?))
+         (when (and db-based? (not collapsed?) (not (or table? property?)))
            (block-positioned-properties config block :block-below))]
 
-        (when (and @*show-right-menu? (not in-whiteboard?) (not table?))
+        (when (and @*show-right-menu? (not in-whiteboard?) (not (or table? property?)))
           (block-right-menu config block editing?))])
 
-     (when (and db-based? (not collapsed?) (not table?))
+     (when (and db-based? (not collapsed?) (not (or table? property?)))
        [:div (when-not (:page-title? config) {:style {:padding-left 45}})
         (db-properties-cp config block {:in-block-container? true})])
 
-     (when-not (or (:hide-children? config) in-whiteboard? table?)
+     (when-not (or (:hide-children? config) in-whiteboard? (or table? property?))
        (let [config' (-> (update config :level inc)
                          (dissoc :original-block :data))]
          (block-children config' block children collapsed?)))
 
-     (when-not (or in-whiteboard? table?) (dnd-separator-wrapper block children block-id slide? false false))]))
+     (when-not (or in-whiteboard? table? property?) (dnd-separator-wrapper block children block-id slide? false false))]))
 
 (defn- block-changed?
   [old-block new-block]

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

@@ -212,7 +212,7 @@
 }
 
 .block-title-wrap {
-  @apply inline;
+  @apply w-full inline;
 
   > .ui__checkbox {
     @apply relative top-[2px];
@@ -900,7 +900,7 @@ html.is-mac {
 }
 
 .block-tag a.tag {
-  @apply flex text-sm items-center opacity-70;
+  @apply flex text-sm font-normal items-center opacity-70;
 }
 
 .block-tag a.tag:hover {

+ 13 - 10
src/main/frontend/components/cmdk/core.cljs

@@ -48,14 +48,17 @@
 
 (def GROUP-LIMIT 5)
 
-(def search-actions
-  [{:filter {:group :current-page} :text "Search only current page" :info "Add filter to search" :icon-theme :gray :icon "page"}
-   {:filter {:group :nodes} :text "Search only nodes" :info "Add filter to search" :icon-theme :gray :icon "letter-n"}
-   {:filter {:group :commands} :text "Search only commands" :info "Add filter to search" :icon-theme :gray :icon "command"}
-   {:filter {:group :files} :text "Search only files" :info "Add filter to search" :icon-theme :gray :icon "file"}
-   {:filter {:group :themes} :text "Search only themes" :info "Add filter to search" :icon-theme :gray :icon "palette"}])
-
-(def filters search-actions)
+(defn filters
+  []
+  (let [current-page (state/get-current-page)]
+    (->>
+     [(when current-page
+        {:filter {:group :current-page} :text "Search only current page" :info "Add filter to search" :icon-theme :gray :icon "page"})
+      {:filter {:group :nodes} :text "Search only nodes" :info "Add filter to search" :icon-theme :gray :icon "letter-n"}
+      {:filter {:group :commands} :text "Search only commands" :info "Add filter to search" :icon-theme :gray :icon "command"}
+      {:filter {:group :files} :text "Search only files" :info "Add filter to search" :icon-theme :gray :icon "file"}
+      {:filter {:group :themes} :text "Search only themes" :info "Add filter to search" :icon-theme :gray :icon "palette"}]
+     (remove nil?))))
 
 ;; The results are separated into groups, and loaded/fetched/queried separately
 (def default-results
@@ -328,8 +331,8 @@
         input @!input
         q (or (get-filter-q input) "")
         matched-items (if (string/blank? q)
-                        filters
-                        (search/fuzzy-search filters q {:extract-fn :text}))]
+                        (filters)
+                        (search/fuzzy-search (filters) q {:extract-fn :text}))]
     (swap! !results update group merge {:status :success :items matched-items})))
 
 (defmethod load-results :current-page [group state]

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

@@ -610,7 +610,7 @@
                  (doseq [page pages]
                    (let [page (util/safe-page-name-sanity-lc page)
                          [db-id block-type] (if (= page "contents")
-                                              ["contents" :contents]
+                                              [(or (:db/id (db/get-page page)) "contents") :contents]
                                               [(:db/id (db/get-page page)) :page])]
                      (state/sidebar-add-block! current-repo db-id block-type)))
                  (reset! sidebar-inited? true))))

+ 18 - 15
src/main/frontend/components/editor.cljs

@@ -33,7 +33,8 @@
             [promesa.core :as p]
             [react-draggable]
             [rum.core :as rum]
-            [frontend.config :as config]))
+            [frontend.config :as config]
+            [frontend.date :as date]))
 
 (defn filter-commands
   [page? commands]
@@ -141,10 +142,14 @@
                        (when-not (string/blank? q)
                          (p/let [result (if db-tag?
                                           (editor-handler/get-matched-classes q)
-                                          (editor-handler/<get-matched-blocks q))]
+                                          (editor-handler/<get-matched-blocks q {:nlp-pages? true}))]
                            (set-matched-pages! result))))
                      [q])
-    (let [matched-pages (when-not (string/blank? q)
+    (let [matched-pages (if (string/blank? q)
+                          (->> (map (fn [title] {:block/title title
+                                                 :nlp-date? true})
+                                    date/nlp-pages)
+                               (take 10))
                           ;; reorder, shortest and starts-with first.
                           (let [matched-pages-with-new-page
                                 (fn [partial-matched-pages]
@@ -180,6 +185,9 @@
                             (when-not db-tag?
                               [:div.flex.items-center
                                (cond
+                                 (:nlp-date? block)
+                                 (ui/icon "calendar" {:size 14})
+
                                  (ldb/class? block)
                                  (ui/icon "hash" {:size 14})
 
@@ -193,7 +201,7 @@
                                  (ui/icon "page" {:extension? true})
 
                                  (or (string/starts-with? (str (:block/title block)) (t :new-tag))
-                                   (string/starts-with? (str (:block/title block)) (t :new-page)))
+                                     (string/starts-with? (str (:block/title block)) (t :new-page)))
                                  (ui/icon "plus" {:size 14})
 
                                  :else
@@ -698,12 +706,9 @@
     (shui-editor-popups id format action nil)))
 
 (defn- editor-on-hide
-  [state value* type e]
-  (let [repo (state/get-current-repo)
-        action (state/get-editor-action)
-        [opts _id config] (:rum/args state)
-        block (:block opts)
-        value (or value* "")]
+  [state type e]
+  (let [action (state/get-editor-action)
+        [_id config] (:rum/args state)]
     (cond
       (and (= type :esc) (exist-editor-commands-popup?))
       nil
@@ -726,9 +731,7 @@
         (when-let [container (gdom/getElement "app-container")]
           (dom/remove-class! container "blocks-selection-mode"))
         (p/do!
-         (editor-handler/save-block! repo (:block/uuid block) value)
-         (editor-handler/escape-editing {:select? select?
-                                         :save-block? false})
+         (editor-handler/escape-editing {:select? select?})
          (some-> config :on-escape-editing
                  (apply [(str uuid) (= type :esc)])))))))
 
@@ -747,7 +750,7 @@
       {:node @(::ref state)
        :on-hide (fn [_state e type]
                   (when-not (= type :esc)
-                    (editor-on-hide state (:value (editor-handler/get-state)) type e)))})))
+                    (editor-on-hide state type e)))})))
   (mixins/event-mixin setup-key-listener!)
   lifecycle/lifecycle
   [state {:keys [format block parent-block]} id config]
@@ -767,7 +770,7 @@
                                     (if-let [on-key-down (:on-key-down config)]
                                       (on-key-down e)
                                       (when (= (util/ekey e) "Escape")
-                                        (editor-on-hide state content :esc e))))
+                                        (editor-on-hide state :esc e))))
                :auto-focus true
                :class heading-class}
                (some? parent-block)

+ 85 - 0
src/main/frontend/components/file_based/query.cljs

@@ -0,0 +1,85 @@
+(ns frontend.components.file-based.query
+  (:require [frontend.components.query.result :as query-result]
+            [frontend.components.file-based.query-table :as query-table]
+            [frontend.db.query-dsl :as query-dsl]
+            [frontend.handler.property :as property-handler]
+            [frontend.state :as state]
+            [frontend.ui :as ui]
+            [frontend.util :as util]
+            [rum.core :as rum]))
+
+(rum/defc query-refresh-button
+  [query-time {:keys [on-pointer-down full-text-search?]}]
+  (ui/tippy
+   {:html  [:div
+            [:p
+             (if full-text-search?
+               [:span "Full-text search results will not be refreshed automatically."]
+               [:span (str "This query takes " (int query-time) "ms to finish, it's a bit slow so that auto refresh is disabled.")])]
+            [:p
+             "Click the refresh button instead if you want to see the latest result."]]
+    :interactive     true
+    :popperOptions   {:modifiers {:preventOverflow
+                                  {:enabled           true
+                                   :boundariesElement "viewport"}}}
+    :arrow true}
+   [:a.fade-link.flex
+    {:on-pointer-down on-pointer-down}
+    (ui/icon "refresh" {:style {:font-size 20}})]))
+
+;; Custom query header only used by file graphs
+(rum/defc custom-query-header
+  [{:keys [dsl-query?] :as config}
+   {:keys [title query] :as q}
+   {:keys [collapsed? result table? current-block view-f page-list? query-error-atom fulltext-query-result-atom]}]
+  (let [dsl-page-query? (and dsl-query?
+                             (false? (:blocks? (query-dsl/parse-query query))))
+        full-text-search? (and dsl-query?
+                               (string? query)
+                               (re-matches #"\".*\"" query))
+        query-time (:query-time (meta result))
+        current-block-uuid (:block/uuid current-block)]
+    [:div.th
+     {:title (str "Query: " query)}
+     (if dsl-query?
+       [:div.flex.flex-1.flex-row
+        (ui/icon "search" {:size 14})
+        [:div.ml-1 (str "Live query" (when dsl-page-query? " for pages"))]]
+       [:div {:style {:font-size "initial"}} title])
+
+     (when (or (not dsl-query?) (not collapsed?))
+       [:div.flex.flex-row.items-center.fade-in
+        (when (> (count result) 0)
+          [:span.results-count.pl-2
+           (let [result-count (if (and (not table?) (map? result))
+                                (apply + (map (comp count val) result))
+                                (count result))]
+             (str result-count (if (> result-count 1) " results" " result")))])
+
+        (when (and current-block (not view-f) (not page-list?))
+          (if table?
+            [:a.flex.ml-1.fade-link {:title "Switch to list view"
+                                     :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid
+                                                                                            :query-table
+                                                                                            false))}
+             (ui/icon "list" {:style {:font-size 20}})]
+            [:a.flex.ml-1.fade-link {:title "Switch to table view"
+                                     :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid
+                                                                                            :query-table
+                                                                                            true))}
+             (ui/icon "table" {:style {:font-size 20}})]))
+
+        [:a.flex.ml-1.fade-link
+         {:title "Setting properties"
+          :on-click (fn []
+                      (let [all-keys (query-table/get-all-columns-for-result result page-list?)]
+                        (state/pub-event! [:modal/set-query-properties current-block all-keys])))}
+         (ui/icon "settings" {:style {:font-size 20}})]
+
+        [:div.ml-1
+         (when (or full-text-search?
+                   (and query-time (> query-time 50)))
+           (query-refresh-button query-time {:full-text-search? full-text-search?
+                                             :on-pointer-down (fn [e]
+                                                                (util/stop e)
+                                                                (query-result/trigger-custom-query! config q query-error-atom fulltext-query-result-atom))}))]])]))

+ 1 - 1
src/main/frontend/components/file_based/query_table.cljs

@@ -241,4 +241,4 @@
           ;; as user needs to know if there result is sorted
           sort-state (get-sort-state current-block)
           sort-result' (sort-result result' (assoc sort-state :page? page?))]
-      (result-table-v1 config current-block sort-result' sort-state columns options map-inline page-cp ->elem inline-text))))
+      (result-table-v1 config current-block sort-result' sort-state columns options map-inline page-cp ->elem inline-text))))

+ 66 - 65
src/main/frontend/components/header.cljs

@@ -106,81 +106,82 @@
         login? (and (state/sub :auth/id-token) (user-handler/logged-in?))
         items (fn []
                 (->>
-                  [(when (state/enable-editing?)
-                     {:title (t :settings)
-                      :options {:on-click state/open-settings!}
-                      :icon (ui/icon "settings")})
+                 [(when (state/enable-editing?)
+                    {:title (t :settings)
+                     :options {:on-click state/open-settings!}
+                     :icon (ui/icon "settings")})
 
-                   (when config/lsp-enabled?
-                     {:title (t :plugins)
-                      :options {:on-click #(plugin-handler/goto-plugins-dashboard!)}
-                      :icon (ui/icon "apps")})
+                  (when config/lsp-enabled?
+                    {:title (t :plugins)
+                     :options {:on-click #(plugin-handler/goto-plugins-dashboard!)}
+                     :icon (ui/icon "apps")})
 
-                   {:title (t :appearance)
-                    :options {:on-click #(state/pub-event! [:modal/toggle-appearance-modal])}
-                    :icon (ui/icon "color-swatch")}
+                  {:title (t :appearance)
+                   :options {:on-click #(state/pub-event! [:modal/toggle-appearance-modal])}
+                   :icon (ui/icon "color-swatch")}
 
-                   (when current-repo
-                     {:title (t :export-graph)
-                      :options {:on-click #(shui/dialog-open! export/export)}
-                      :icon (ui/icon "database-export")})
+                  (when current-repo
+                    {:title (t :export-graph)
+                     :options {:on-click #(shui/dialog-open! export/export)}
+                     :icon (ui/icon "database-export")})
 
-                   (when (and current-repo (state/enable-editing?))
-                     {:title (t :import)
-                      :options {:href (rfe/href :import)}
-                      :icon (ui/icon "file-upload")})
+                  (when (and current-repo (state/enable-editing?))
+                    {:title (t :import)
+                     :options {:href (rfe/href :import)}
+                     :icon (ui/icon "file-upload")})
 
-                   (when config/publishing?
-                     {:title (t :toggle-theme)
-                      :options {:on-click #(state/toggle-theme!)}
-                      :icon (ui/icon "bulb")})
+                  (when config/publishing?
+                    {:title (t :toggle-theme)
+                     :options {:on-click #(state/toggle-theme!)}
+                     :icon (ui/icon "bulb")})
 
-                   (when (not login?)
-                     {:title (t :login)
-                      :options {:on-click #(state/pub-event! [:user/login])}
-                      :icon (ui/icon "user")})
+                  ;; Disable login on Web until RTC is ready
+                  (when (and (not login?) (not util/web-platform?))
+                    {:title (t :login)
+                     :options {:on-click #(state/pub-event! [:user/login])}
+                     :icon (ui/icon "user")})
 
-                   (when login? {:hr true})
-                   (when login?
-                     {:item [:span.flex.flex-col.relative.group.pt-1.w-full
-                             [:b.leading-none (user-handler/username)]
-                             [:small.opacity-70 (user-handler/email)]
-                             [:i.absolute.opacity-0.group-hover:opacity-100.text-red-rx-09
-                              {:class "right-1 top-3" :title (t :logout)}
-                              (ui/icon "logout")]]
-                      :options {:on-click #(user-handler/logout)
-                                :class "w-full"}})]
-                  (concat page-menu-and-hr)
-                  (remove nil?)))]
+                  (when login? {:hr true})
+                  (when login?
+                    {:item [:span.flex.flex-col.relative.group.pt-1.w-full
+                            [:b.leading-none (user-handler/username)]
+                            [:small.opacity-70 (user-handler/email)]
+                            [:i.absolute.opacity-0.group-hover:opacity-100.text-red-rx-09
+                             {:class "right-1 top-3" :title (t :logout)}
+                             (ui/icon "logout")]]
+                     :options {:on-click #(user-handler/logout)
+                               :class "w-full"}})]
+                 (concat page-menu-and-hr)
+                 (remove nil?)))]
     [:button.button.icon.toolbar-dots-btn
      {:title (t :header/more)
       :on-pointer-down (fn [^js e]
-                  (shui/popup-show! (.-target e)
-                    (fn [{:keys [id]}]
-                      (for [{:keys [hr item title options icon]} (items)]
-                        (let [on-click' (:on-click options)
-                              href (:href options)]
-                          (if hr
-                            (shui/dropdown-menu-separator)
-                            (shui/dropdown-menu-item
-                             (assoc options
-                                    :on-click (fn [^js e]
-                                                (when on-click'
-                                                  (when-not (false? (on-click' e))
-                                                    (shui/popup-hide! id)))))
-                             (or item
-                                 (if href
-                                   [:a.flex.items-center.w-full
-                                    {:href href :on-click #(shui/popup-hide! id)
-                                     :style {:color "inherit"}}
-                                    [:span.flex.items-center.gap-1.w-full
-                                     icon [:div title]]]
-                                   [:span.flex.items-center.gap-1.w-full
-                                    icon [:div title]])))))))
-                    {:align "end"
-                     :as-dropdown? true
-                     :content-props {:class "w-64"
-                                     :align-offset -32}}))}
+                         (shui/popup-show! (.-target e)
+                                           (fn [{:keys [id]}]
+                                             (for [{:keys [hr item title options icon]} (items)]
+                                               (let [on-click' (:on-click options)
+                                                     href (:href options)]
+                                                 (if hr
+                                                   (shui/dropdown-menu-separator)
+                                                   (shui/dropdown-menu-item
+                                                    (assoc options
+                                                           :on-click (fn [^js e]
+                                                                       (when on-click'
+                                                                         (when-not (false? (on-click' e))
+                                                                           (shui/popup-hide! id)))))
+                                                    (or item
+                                                        (if href
+                                                          [:a.flex.items-center.w-full
+                                                           {:href href :on-click #(shui/popup-hide! id)
+                                                            :style {:color "inherit"}}
+                                                           [:span.flex.items-center.gap-1.w-full
+                                                            icon [:div title]]]
+                                                          [:span.flex.items-center.gap-1.w-full
+                                                           icon [:div title]])))))))
+                                           {:align "end"
+                                            :as-dropdown? true
+                                            :content-props {:class "w-64"
+                                                            :align-offset -32}}))}
      (ui/icon "dots" {:size ui/icon-size})]))
 
 (rum/defc back-and-forward

+ 51 - 57
src/main/frontend/components/icon.cljs

@@ -110,10 +110,11 @@
                   (on-chosen e {:type :tabler-icon
                                 :id icon'
                                 :name icon'}))
-      :on-mouse-over #(reset! hover {:type :tabler-icon
-                                     :id icon'
-                                     :name icon'
-                                     :icon icon'})
+      :on-mouse-over #(some-> hover
+                        (reset! {:type :tabler-icon
+                                 :id icon'
+                                 :name icon'
+                                 :icon icon'}))
       :on-mouse-out #()})
    (ui/icon icon' {:size 24})])
 
@@ -282,7 +283,7 @@
     [:span.absolute.hidden {:ref *el-ref}]))
 
 (rum/defc color-picker
-  [*color]
+  [*color on-select!]
   (let [[color, set-color!] (rum/use-state @*color)
         *el (rum/use-ref nil)
         content-fn (fn []
@@ -291,7 +292,9 @@
                        [:div.color-picker-presets
                         (for [c colors]
                           (shui/button
-                            {:on-click (fn [] (set-color! c) (shui/popup-hide!))
+                            {:on-click (fn [] (set-color! c)
+                                         (some-> on-select! (apply [c]))
+                                         (shui/popup-hide!))
                              :size :sm :variant :outline
                              :class "it" :style {:background-color c}}
                             (if c "" (shui/tabler-icon "minus" {:class "scale-75 opacity-70"}))))]))]
@@ -317,25 +320,23 @@
   (rum/local nil ::result)
   (rum/local false ::select-mode?)
   (rum/local :all ::tab)
-  (rum/local nil ::hover)
   {:init (fn [s]
            (assoc s ::color (atom (storage/get :ls-icon-color-preset))))}
-  [state {:keys [on-chosen del-btn?] :as opts}]
+  [state {:keys [on-chosen del-btn? icon-value] :as opts}]
   (let [*q (::q state)
         *result (::result state)
         *tab (::tab state)
         *color (::color state)
-        *hover (::hover state)
         *input-ref (rum/create-ref)
         *result-ref (rum/create-ref)
         result @*result
-        opts (assoc opts :hover *hover
-                    :on-chosen (fn [e m]
-                                 (let [icon? (= (:type m) :tabler-icon)
-                                       m (if (and icon? (not (string/blank? @*color)))
-                                           (assoc m :color @*color) m)]
-                                   (and on-chosen (on-chosen e m))
-                                   (when (:type m) (add-used-item! m)))))
+        opts (assoc opts
+               :on-chosen (fn [e m]
+                            (let [icon? (= (:type m) :tabler-icon)
+                                  m (if (and icon? (not (string/blank? @*color)))
+                                      (assoc m :color @*color) m)]
+                              (and on-chosen (on-chosen e m))
+                              (when (:type m) (add-used-item! m)))))
         *select-mode? (::select-mode? state)
         reset-q! #(when-let [^js input (rum/deref *input-ref)]
                     (reset! *q "")
@@ -344,7 +345,8 @@
                     (set! (. input -value) "")
                     (util/schedule
                      (fn []
-                       (.focus input)
+                       (when (not= js/document.activeElement input)
+                         (.focus input))
                        (util/scroll-to (rum/deref *result-ref) 0 false))))]
     [:div.cp__emoji-icon-picker
      ;; header
@@ -387,8 +389,7 @@
      ;; body
      [:div.bd.bd-scroll
       {:ref *result-ref
-       :class (or (some-> @*tab (name)) "other")
-       :on-mouse-leave #(reset! *hover nil)}
+       :class (or (some-> @*tab (name)) "other")}
       [:div.content-pane
        (if (seq result)
          [:div.flex.flex-1.flex-col.gap-1.search-result
@@ -406,39 +407,31 @@
 
      ;; footer
      [:div.ft
-      (if-not @*hover
-        ;; tabs
-        [:<>
-         [:div.flex.flex-1.flex-row.items-center.gap-2
-          (let [tabs [[:all "All"] [:emoji "Emojis"] [:icon "Icons"]]]
-            (for [[id label] tabs
-                  :let [active? (= @*tab id)]]
-              (shui/button
-               {:variant :ghost
-                :size :sm
-                :class (util/classnames [{:active active?} "tab-item"])
-                :on-click #(reset! *tab id)}
-               label)))]
-
-         (when (not= :emoji @*tab)
-           (color-picker *color))
-
-         ;; action buttons
-         (when del-btn?
-           (shui/button {:variant :outline :size :sm :data-action "del"
-                         :on-click #(on-chosen nil)}
-                        (shui/tabler-icon "trash" {:size 17})))]
-
-        ;; preview
-        [:div.hover-preview
-         [:strong (:name @*hover)]
-         [:button
-          {:style {:font-size 28}
-           :key   (:id @*hover)
-           :title (:name @*hover)}
-          (if (= :tabler-icon (:type @*hover))
-            (ui/icon (:icon @*hover) {:size 28})
-            (:native (first (:skins @*hover))))]])]]))
+      ;; tabs
+      [:<>
+       [:div.flex.flex-1.flex-row.items-center.gap-2
+        (let [tabs [[:all "All"] [:emoji "Emojis"] [:icon "Icons"]]]
+          (for [[id label] tabs
+                :let [active? (= @*tab id)]]
+            (shui/button
+              {:variant :ghost
+               :size :sm
+               :class (util/classnames [{:active active?} "tab-item"])
+               :on-mouse-down (fn [e]
+                                (util/stop e)
+                                (reset! *tab id))}
+              label)))]
+
+       (when (not= :emoji @*tab)
+         (color-picker *color (fn [c]
+                                (when (= :tabler-icon (some-> icon-value :type))
+                                  (on-chosen nil (assoc icon-value :color c) true)))))
+
+       ;; action buttons
+       (when del-btn?
+         (shui/button {:variant :outline :size :sm :data-action "del"
+                       :on-click #(on-chosen nil)}
+           (shui/tabler-icon "trash" {:size 17})))]]]))
 
 (rum/defc icon-picker
   [icon-value {:keys [empty-label disabled? initial-open? del-btn? on-chosen icon-props popup-opts]}]
@@ -448,14 +441,15 @@
           (constantly [])
           (fn [{:keys [id]}]
             (icon-search
-             {:on-chosen (fn [e icon-value]
+             {:on-chosen (fn [e icon-value keep-popup?]
                            (on-chosen e icon-value)
-                           (shui/popup-hide! id))
+                           (when-not (true? keep-popup?) (shui/popup-hide! id)))
+              :icon-value icon-value
               :del-btn? del-btn?})))]
     (rum/use-effect!
-     (fn []
-       (when initial-open?
-         (js/setTimeout #(some-> (rum/deref *trigger-ref) (.click)) 32)))
+      (fn []
+        (when initial-open?
+          (js/setTimeout #(some-> (rum/deref *trigger-ref) (.click)) 32)))
      [initial-open?])
 
     ;; trigger

+ 18 - 16
src/main/frontend/components/objects.cljs

@@ -132,7 +132,7 @@
     (when (false? loading?)
       [:div.flex.flex-col.gap-2.mt-2
 
-       [:div.font-medium.opacity-50 "Tagged nodes"]
+       [:div.font-medium.opacity-50 "Tagged Nodes"]
 
        (views/view view-entity {:data data
                                 :set-data! set-data!
@@ -221,21 +221,23 @@
                                :add-new-object! #(add-new-property-object! property set-data!)
                                ;; TODO: Add support for adding column
                                :show-add-property? false
-                               :on-delete-rows (fn [table selected-rows]
-                                                 (let [pages (filter ldb/page? selected-rows)
-                                                       blocks (remove ldb/page? selected-rows)]
-                                                   (p/do!
-                                                    (ui-outliner-tx/transact!
-                                                     {:outliner-op :delete-blocks}
-                                                     (when (seq blocks)
-                                                       (outliner-op/delete-blocks! blocks nil))
-                                                     (let [page-ids (map :db/id pages)
-                                                           tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)]
-                                                       (when (seq tx-data)
-                                                         (outliner-op/transact! tx-data {:outliner-op :save-block}))))
-                                                    (set-data! (get-property-related-objects (state/get-current-repo) property))
-                                                    (when-let [f (get-in table [:data-fns :set-row-selection!])]
-                                                      (f {})))))}))))
+                               :on-delete-rows (when-not (contains? #{:logseq.property/built-in? :logseq.property/parent}
+                                                                    (:db/ident property))
+                                                 (fn [table selected-rows]
+                                                   (let [pages (filter ldb/page? selected-rows)
+                                                         blocks (remove ldb/page? selected-rows)]
+                                                     (p/do!
+                                                      (ui-outliner-tx/transact!
+                                                       {:outliner-op :delete-blocks}
+                                                       (when (seq blocks)
+                                                         (outliner-op/delete-blocks! blocks nil))
+                                                       (let [page-ids (map :db/id pages)
+                                                             tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)]
+                                                         (when (seq tx-data)
+                                                           (outliner-op/transact! tx-data {:outliner-op :save-block}))))
+                                                      (set-data! (get-property-related-objects (state/get-current-repo) property))
+                                                      (when-let [f (get-in table [:data-fns :set-row-selection!])]
+                                                        (f {}))))))}))))
 
 ;; Show all nodes containing the given property
 (rum/defcs property-related-objects < rum/reactive db-mixins/query mixins/container-id

+ 11 - 27
src/main/frontend/components/page.cljs

@@ -422,7 +422,7 @@
 
 (rum/defcs db-page-title < rum/reactive
   (rum/local false ::hover?)
-  [state repo page whiteboard-page? sidebar? container-id]
+  [state page whiteboard-page? sidebar? container-id]
   (let [*hover? (::hover? state)
         hover? (rum/react *hover?)]
     [:div.ls-page-title.flex.flex-1.w-full.content.items-start
@@ -430,12 +430,7 @@
       :on-pointer-down (fn [e]
                          (when (util/right-click? e)
                            (state/set-state! :page-title/context {:page (:block/title page)
-                                                                  :page-entity page})))
-      :on-click (fn [e]
-                  (when-not (= (.-nodeName (.-target e)) "INPUT")
-                    (when (gobj/get e "shiftKey")
-                      (.preventDefault e)
-                      (state/sidebar-add-block! repo (:db/id page) :page))))}
+                                                                  :page-entity page})))}
 
      [:div.w-full.relative {:on-mouse-over #(reset! *hover? true)
                             :on-mouse-leave (fn []
@@ -598,7 +593,7 @@
                   (page-blocks-collapse-control title *control-show? *all-collapsed?)])
                (when (and (not whiteboard?) (ldb/page? page))
                  (if db-based?
-                   (db-page-title repo page whiteboard-page? sidebar? (:container-id state))
+                   (db-page-title page whiteboard-page? sidebar? (:container-id state))
                    (page-title-cp page {:journal? journal?
                                         :fmt-journal? fmt-journal?
                                         :preview? preview?})))
@@ -679,17 +674,12 @@
   [state option]
   (rum/with-key
     (page-aux option)
-    (or (:page-name option)
-      (get-page-name state))))
-
-(rum/defc contents-page < rum/reactive
-                          {:init (fn [state]
-                                   (db-async/<get-block (state/get-current-repo) "contents")
-                                   state)}
-  [page]
-  (when-let [repo (state/get-current-repo)]
-    (when-not (state/sub-async-query-loading "contents")
-      (page-blocks-cp repo page {:sidebar? true}))))
+    (str
+     (state/get-current-repo)
+     "-"
+     (or (:page-name option)
+         (get-page-name state)))))
+
 
 (defonce layout (atom [js/window.innerWidth js/window.innerHeight]))
 
@@ -986,13 +976,7 @@
                     (seq focus-nodes)
                     (not (:orphan-pages? settings)))
                 (graph-handler/n-hops graph focus-nodes n-hops)
-                graph)
-        graph (update graph :links (fn [links]
-                                     (let [nodes (set (map :id (:nodes graph)))]
-                                       (remove (fn [link]
-                                                 (and (not (nodes (:source link)))
-                                                   (not (nodes (:target link)))))
-                                         links))))]
+                graph)]
     [:div.relative#global-graph
      (graph/graph-2d {:nodes (:nodes graph)
                       :links (:links graph)
@@ -1013,7 +997,7 @@
   [nodes filters]
   (if (seq filters)
     (let [filter-patterns (map #(re-pattern (str "(?i)" (util/regex-escape %))) filters)]
-      (filter (fn [node] (some #(re-find % (:id node)) filter-patterns)) nodes))
+      (filter (fn [node] (some #(re-find % (:label node)) filter-patterns)) nodes))
     nodes))
 
 (rum/defcs global-graph < rum/reactive

+ 14 - 21
src/main/frontend/components/page_menu.cljs

@@ -1,27 +1,26 @@
 (ns frontend.components.page-menu
-  (:require [frontend.commands :as commands]
+  (:require [electron.ipc :as ipc]
+            [frontend.commands :as commands]
             [frontend.components.export :as export]
+            [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
-            [logseq.db :as ldb]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.page :as page-handler]
             [frontend.handler.common.developer :as dev-common-handler]
-            [frontend.handler.route :as route-handler]
             [frontend.handler.db-based.page :as db-page-handler]
+            [frontend.handler.file-sync :as file-sync-handler]
+            [frontend.handler.notification :as notification]
+            [frontend.handler.page :as page-handler]
+            [frontend.handler.property.util :as pu]
+            [frontend.handler.shell :as shell]
+            [frontend.handler.user :as user-handler]
+            [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
-            [logseq.shui.ui :as shui]
-            [promesa.core :as p]
             [frontend.util :as util]
             [frontend.util.page :as page-util]
-            [frontend.handler.shell :as shell]
-            [frontend.mobile.util :as mobile-util]
-            [electron.ipc :as ipc]
-            [frontend.config :as config]
-            [frontend.handler.user :as user-handler]
-            [frontend.handler.file-sync :as file-sync-handler]
             [logseq.common.path :as path]
-            [frontend.handler.property.util :as pu]))
+            [logseq.db :as ldb]
+            [logseq.shui.ui :as shui]
+            [promesa.core :as p]))
 
 (defn- delete-page!
   [page]
@@ -69,13 +68,7 @@
                                     (file-sync-handler/get-current-graph-uuid))]
       (when (not block?)
         (->>
-         [(when (not= (state/get-current-page) (str (:block/uuid page)))
-            {:title   (t :page/go-to-page)
-             :options {:on-click
-                       (fn []
-                         (route-handler/redirect-to-page! (:block/uuid page)))}})
-
-          (when-not config/publishing?
+         [(when-not config/publishing?
             {:title   (if favorited?
                         (t :page/unfavorite)
                         (t :page/add-to-favorites))

+ 51 - 43
src/main/frontend/components/property.cljs

@@ -42,8 +42,7 @@
     ((comp string/capitalize name) property-type)))
 
 (defn- <add-property-from-dropdown
-  "Adds an existing or new property from dropdown. Used from a block or page context.
-   For pages, used to add both schema properties or properties for a page"
+  "Adds an existing or new property from dropdown. Used from a block or page context."
   [entity property-uuid-or-name schema {:keys [class-schema?]}]
   (p/let [repo (state/get-current-repo)
           ;; Both conditions necessary so that a class can add its own page properties
@@ -87,7 +86,7 @@
                           (map (fn [type]
                                  {:label (property-type-label type)
                                   :value type})))]
-    [:div {:class "flex items-center col-span-2"}
+    [:div {:class "flex items-center col-span-1"}
      (shui/select
       (cond->
        {:default-open (boolean default-open?)
@@ -178,6 +177,8 @@
         icon (cond
                (= ident :block/tags)
                "hash"
+               (string/starts-with? (str ident) ":plugin.")
+               "puzzle"
                :else
                (case type
                  :number "number"
@@ -227,28 +228,53 @@
             (p/do!
              (reset! *show-new-property-config? false))))))))
 
-(rum/defcs property-key-cp <
-  (rum/local false ::hover?)
-  [state block property {:keys [other-position? class-schema?]}]
-  (let [*hover? (::hover? state)
-        icon (:logseq.property/icon property)]
+(rum/defc property-key-title
+  [block property class-schema?]
+  (let [block-container (state/get-component :block/container)]
+    (shui/trigger-as
+     :a
+     {:tabIndex 0
+      :title (:block/title property)
+      :class "property-k flex select-none jtrigger w-full"
+      :on-pointer-down (fn [^js e]
+                         (when (util/meta-key? e)
+                           (route-handler/redirect-to-page! (:block/uuid property))
+                           (.preventDefault e)))
+      :on-click (fn [^js/MouseEvent e]
+                  (shui/popup-show! (.-target e)
+                                    (fn []
+                                      (property-config/dropdown-editor property block {:debug? (.-altKey e)
+                                                                                       :class-schema? class-schema?}))
+                                    {:content-props
+                                     {:class "ls-property-dropdown-editor as-root"
+                                      :onEscapeKeyDown (fn [e]
+                                                         (util/stop e)
+                                                         (shui/popup-hide!)
+                                                         (when-let [input (state/get-input)]
+                                                           (.focus input)))}
+                                     :align "start"
+                                     :as-dropdown? true}))}
+     (block-container {:property? true} property))))
+
+(rum/defc property-key-cp < rum/static
+  [block property {:keys [other-position? class-schema?]}]
+  (let [icon (:logseq.property/icon property)]
     [:div.property-key-inner.jtrigger-view
-     {:on-mouse-over   #(reset! *hover? true)
-      :on-mouse-leave  #(reset! *hover? false)}
      ;; icon picker
      (when-not other-position?
        (let [content-fn (fn [{:keys [id]}]
                           (icon-component/icon-search
-                           {:on-chosen
-                            (fn [_e icon]
-                              (if icon
-                                (db-property-handler/upsert-property! (:db/ident property)
-                                                                      (:block/schema property)
-                                                                      {:properties {:logseq.property/icon icon}})
-                                (db-property-handler/remove-block-property! (:db/id property)
-                                                                            (pu/get-pid :logseq.property/icon)))
-                              (shui/popup-hide! id))
-                            :del-btn? (boolean icon)}))]
+                            {:on-chosen
+                             (fn [_e icon]
+                               (if icon
+                                 (db-property-handler/upsert-property! (:db/ident property)
+                                   (:block/schema property)
+                                   {:properties {:logseq.property/icon icon}})
+                                 (db-property-handler/remove-block-property! (:db/id property)
+                                   (pu/get-pid :logseq.property/icon)))
+                               (shui/popup-hide! id))
+                             :icon-value icon
+                             :del-btn? (boolean icon)}))]
 
          (shui/trigger-as
           :button.property-m
@@ -266,25 +292,7 @@
        [:a.property-k.flex.select-none.jtrigger
         {:on-click #(route-handler/redirect-to-page! (:block/uuid property))}
         (:block/title property)]
-
-       (shui/trigger-as :a
-         {:tabIndex 0
-          :title (:block/title property)
-          :class "property-k flex select-none jtrigger w-full"
-          :on-pointer-down (fn [^js e]
-                             (when (util/meta-key? e)
-                               (route-handler/redirect-to-page! (:block/uuid property))
-                               (.preventDefault e)))
-          :on-click (fn [^js/MouseEvent e]
-                      (shui/popup-show! (.-target e)
-                        (fn []
-                          (property-config/dropdown-editor property block {:debug? (.-altKey e)
-                                                                           :class-schema? class-schema?}))
-                        {:content-props
-                         {:class "ls-property-dropdown-editor as-root"}
-                         :align "start"
-                         :as-dropdown? true}))}
-         (:block/title property)))]))
+       (property-key-title block property class-schema?))]))
 
 (rum/defcs property-input < rum/reactive
   (rum/local nil ::ref)
@@ -343,8 +351,8 @@
     [:div.ls-property-input.flex.flex-1.flex-row.items-center.flex-wrap.gap-1
      {:ref #(reset! *ref %)}
      (if property-key
-       [:div.ls-property-add.grid.grid-cols-5.gap-1.flex.flex-1.flex-row.items-center
-        [:div.flex.flex-row.items-center.col-span-2.property-key.gap-1
+       [:div.ls-property-add.grid.grid-cols-4.gap-1.flex.flex-1.flex-row.items-center
+        [:div.flex.flex-row.items-center.col-span-1.property-key.gap-1
          (when-not (:db/id property) (property-icon property (:type @*property-schema)))
          (if (:db/id property)                              ; property exists already
            (property-key-cp block property opts)
@@ -440,8 +448,8 @@
                         :else
                         "property-pair items-start")}
          (if (seq sortable-opts)
-           (dnd/sortable-item (assoc sortable-opts :class "property-key col-span-2") property-key-cp')
-           [:div.property-key.col-span-2 property-key-cp'])
+           (dnd/sortable-item (assoc sortable-opts :class "property-key col-span-1") property-key-cp')
+           [:div.property-key.col-span-1 property-key-cp'])
 
          (let [class-properties? (= (:db/ident property) :logseq.property.class/properties)
                property-desc (when-not (= (:db/ident property) :logseq.property/description)

+ 1 - 1
src/main/frontend/components/property.css

@@ -47,7 +47,7 @@
   @apply grid;
 
   .property-pair {
-    @apply grid grid-cols-5 gap-1;
+    @apply grid grid-cols-4 gap-1;
 
     .jtrigger {
       @apply relative;

+ 23 - 22
src/main/frontend/components/property/config.cljs

@@ -185,10 +185,6 @@
         (shui/button {:size "sm" :disabled (or saving? (not dirty?))
                       :variant (if dirty? :default :secondary)
                       :on-click (fn []
-                                  (when (string/blank? title)
-                                    (some-> (rum/deref *input-ref) (.focus))
-                                    (throw (js/Error. "property name is empty")))
-
                                   (set-saving! true)
                                   (-> [(db-property-handler/upsert-property!
                                          (:db/ident property)
@@ -453,7 +449,7 @@
                      (property-handler/remove-block-property! repo (:block/uuid block) (:db/ident property))))]
     (if (and class? class-schema?)
       (-> (shui/dialog-confirm!
-           [:p (str "Are you sure you want to delete this property?")]
+           [:p (str "Are you sure you want to delete the property from this tag?")]
            {:id :delete-property-from-class
             :data-reminder :ok})
           (p/then remove!))
@@ -550,23 +546,26 @@
                                         (update-cardinality-fn))))}))
 
      (shui/dropdown-menu-separator)
-     (when (and (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
-                (not
-                 (and (= :default (get-in property [:block/schema :type]))
-                      (empty? (:property/closed-values property))
-                      (contains? #{nil :properties} (:position property-schema)))))
-       (let [position (:position property-schema)]
-         (dropdown-editor-menuitem {:icon :float-left :title "UI position" :desc (some->> position (get position-labels) (:title))
-                                    :item-props {:class "ui__position-trigger-item"}
-                                    :submenu-content (fn [ops] (ui-position-sub-pane property (assoc ops :position position)))})))
-
-     (when (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
-      (dropdown-editor-menuitem {:icon :eye-off :title "Hide by default" :toggle-checked? (boolean (:hide? property-schema))
-                                 :on-toggle-checked-change #(db-property-handler/upsert-property! (:db/ident property)
-                                                                                                  (assoc property-schema :hide? %) {})}))
 
-     (when owner-block
-       (shui/dropdown-menu-separator))
+     (let [group' (->> [:<>
+                        (when (and (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
+                                (not
+                                  (and (= :default (get-in property [:block/schema :type]))
+                                    (empty? (:property/closed-values property))
+                                    (contains? #{nil :properties} (:position property-schema)))))
+                          (let [position (:position property-schema)]
+                            (dropdown-editor-menuitem {:icon :float-left :title "UI position" :desc (some->> position (get position-labels) (:title))
+                                                       :item-props {:class "ui__position-trigger-item"}
+                                                       :submenu-content (fn [ops] (ui-position-sub-pane property (assoc ops :position position)))})))
+
+                        (when (not (contains? #{:logseq.property/parent :logseq.property.class/properties} (:db/ident property)))
+                          (dropdown-editor-menuitem {:icon :eye-off :title "Hide by default" :toggle-checked? (boolean (:hide? property-schema))
+                                                     :on-toggle-checked-change #(db-property-handler/upsert-property! (:db/ident property)
+                                                                                  (assoc property-schema :hide? %) {})}))]
+                    (remove nil?)
+                    (into []))]
+       (when (and owner-block (> (count group') 1))
+         (conj group' (shui/dropdown-menu-separator))))
 
      (when owner-block
        (dropdown-editor-menuitem
@@ -581,7 +580,9 @@
                       (ldb/class? owner-block)
                       (contains? #{:logseq.property/parent} (:db/ident property)))))
        (dropdown-editor-menuitem
-        {:id :delete-property :icon :x :title "Delete property" :desc "" :disabled? false
+        {:id :delete-property :icon :x
+         :title (if class-schema? "Delete property from tag" "Delete from from node")
+         :desc "" :disabled? false
          :item-props {:class "opacity-60 focus:!text-red-rx-09 focus:opacity-100"
                       :on-select (fn [^js e]
                                    (util/stop e)

+ 7 - 2
src/main/frontend/components/property/value.cljs

@@ -80,7 +80,10 @@
                 (when-let [^js target (some-> (.querySelector container (str "#ls-block-" (str (:block/uuid block))))
                                         (.querySelector ".block-main-container"))]
                   (shui/popup-show! target
-                    #(icon-component/icon-search {:on-chosen on-chosen! :del-btn? (some? icon)})
+                    #(icon-component/icon-search
+                       {:on-chosen on-chosen!
+                        :icon-value icon
+                        :del-btn? (some? icon)})
                     {:id :ls-icon-picker
                      :align :start})))))))
       [editing?])
@@ -642,7 +645,9 @@
                      :property-block? true}]
          (if (set? value-block)
            (blocks-container config (ldb/sort-by-order value-block))
-           (block-container config value-block)))]
+           (rum/with-key
+             (block-container config value-block)
+             (str (:db/id property) "-" (:block/uuid value-block)))))]
       [:div
        {:tabIndex 0
         :on-click (fn [] (<create-new-block! block property ""))}

+ 29 - 86
src/main/frontend/components/query.cljs

@@ -1,21 +1,19 @@
 (ns frontend.components.query
   (:require [clojure.string :as string]
             [frontend.components.file-based.query-table :as query-table]
+            [frontend.components.file-based.query :as file-query]
             [frontend.components.query.result :as query-result]
             [frontend.components.query.view :as query-view]
             [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
-            [frontend.db.query-dsl :as query-dsl]
             [frontend.extensions.sci :as sci]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.property :as property-handler]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [lambdaisland.glogi :as log]
             [rum.core :as rum]
-            [frontend.handler.property.util :as pu]
             [frontend.config :as config]))
 
 (defn built-in-custom-query?
@@ -24,26 +22,8 @@
     (when (seq queries)
       (boolean (some #(= % title) (map :title queries))))))
 
-(rum/defc query-refresh-button
-  [query-time {:keys [on-pointer-down full-text-search?]}]
-  (ui/tippy
-   {:html  [:div
-            [:p
-             (if full-text-search?
-               [:span "Full-text search results will not be refreshed automatically."]
-               [:span (str "This query takes " (int query-time) "ms to finish, it's a bit slow so that auto refresh is disabled.")])]
-            [:p
-             "Click the refresh button instead if you want to see the latest result."]]
-    :interactive     true
-    :popperOptions   {:modifiers {:preventOverflow
-                                  {:enabled           true
-                                   :boundariesElement "viewport"}}}
-    :arrow true}
-   [:a.fade-link.flex
-    {:on-pointer-down on-pointer-down}
-    (ui/icon "refresh" {:style {:font-size 20}})]))
-
-(rum/defcs custom-query-inner < rum/reactive
+;; TODO: Split this into file and DB graph versions. DB graph needlessly coupled too file graph args
+(rum/defcs custom-query-inner < rum/static
   [state config {:keys [query breadcrumb-show?]}
    {:keys [query-error-atom
            current-block
@@ -80,8 +60,7 @@
            (util/hiccup-keywordize result))
 
          (and (config/db-based-graph? (state/get-current-repo))
-              (not (:built-in? config))
-              (or page-list? only-blocks? blocks-grouped-by-page? table?))
+              (not (:built-in? config)))
          (query-view/query-result (assoc config :id (:db/id current-block))
                                   current-block result)
 
@@ -137,7 +116,8 @@
        [:span.opacity-60.text-sm.ml-2.results-count
         (str result-count (if (> result-count 1) " results" " result"))])]))
 
-(rum/defcs ^:large-vars/cleanup-todo custom-query* < rum/reactive rum/static db-mixins/query
+(rum/defcs custom-query* < rum/reactive rum/static db-mixins/query
+  (rum/local nil ::query-result-atom)
   {:init (fn [state]
            (let [[config {:keys [title collapsed?]}] (:rum/args state)
                  built-in? (built-in-custom-query? title)
@@ -149,8 +129,9 @@
                  (editor-handler/collapse-block! current-block-uuid))))
            (assoc state :query-error (atom nil)
                   :fulltext-query-result (atom nil)))}
-  [state config {:keys [title builder query view collapsed? table-view?] :as q}]
-  (let [*query-error (:query-error state)
+  [state config {:keys [title builder query view collapsed?] :as q}]
+  (let [*query-result-atom (::query-result-atom state)
+        *query-error (:query-error state)
         *fulltext-query-result (:fulltext-query-result state)
         built-in? (built-in-custom-query? title)
         config (assoc config :built-in? built-in?)
@@ -165,20 +146,17 @@
                        collapsed?
                        (:block/collapsed? current-block)))
         built-in-collapsed? (and collapsed? built-in?)
-        query-table? (pu/get-block-property-value current-block :logseq.property/query-table)
-        table? (or table-view?
-                   query-table?
-                   (and (string? query) (string/ends-with? (string/trim query) "table")))
+        db-based? (config/db-based-graph? (state/get-current-repo))
+        table? (when-not db-based?
+                 (or (get-in current-block [:block/properties :query-table])
+                     (and (string? query) (string/ends-with? (string/trim query) "table"))))
         view-fn (if (keyword? view) (get-in (state/sub-config) [:query/views view]) view)
         view-f (and view-fn (sci/eval-string (pr-str view-fn)))
-        dsl-page-query? (and dsl-query?
-                             (false? (:blocks? (query-dsl/parse-query query))))
-        full-text-search? (and dsl-query?
-                               (string? query)
-                               (re-matches #"\".*\"" query))
         result (when (or built-in-collapsed? (not collapsed?'))
-                 (query-result/get-query-result config q *query-error *fulltext-query-result current-block-uuid {:table? table?}))
-        query-time (:query-time (meta result))
+                 (or @*query-result-atom
+                     (let [result (query-result/get-query-result config q *query-error *fulltext-query-result current-block-uuid {:table? table?})]
+                       (reset! *query-result-atom result)
+                       result)))
         page-list? (and (seq result)
                         (some? (:block/name (first result))))
         opts {:query-error-atom *query-error
@@ -188,59 +166,24 @@
               :view-f view-f
               :page-list? page-list?
               :result result
-              :group-by-page? (query-result/get-group-by-page q {:table? table?})}
-        db-based? (config/db-based-graph? (state/get-current-repo))]
+              :group-by-page? (query-result/get-group-by-page q {:table? table?})}]
     (if (:custom-query? config)
       [:code (if dsl-query?
                (util/format "{{query %s}}" query)
                "{{query hidden}}")]
       (when-not (and built-in? (empty? result))
         [:div.custom-query (get config :attr {})
-         (when-not (or built-in? db-based?)
-           [:div.th
-            {:title (str "Query: " query)}
-            (if dsl-query?
-              [:div.flex.flex-1.flex-row
-               (ui/icon "search" {:size 14})
-               [:div.ml-1 (str "Live query" (when dsl-page-query? " for pages"))]]
-              [:div {:style {:font-size "initial"}} title])
-
-            (when (or (not dsl-query?) (not collapsed?'))
-              [:div.flex.flex-row.items-center.fade-in
-               (when (> (count result) 0)
-                 [:span.results-count.pl-2
-                  (let [result-count (if (and (not table?) (map? result))
-                                       (apply + (map (comp count val) result))
-                                       (count result))]
-                    (str result-count (if (> result-count 1) " results" " result")))])
-
-               (when (and current-block (not view-f) (nil? table-view?) (not page-list?))
-                 (if table?
-                   [:a.flex.ml-1.fade-link {:title "Switch to list view"
-                                            :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid
-                                                                                                   (pu/get-pid :logseq.property/query-table)
-                                                                                                   false))}
-                    (ui/icon "list" {:style {:font-size 20}})]
-                   [:a.flex.ml-1.fade-link {:title "Switch to table view"
-                                            :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid
-                                                                                                   (pu/get-pid :logseq.property/query-table)
-                                                                                                   true))}
-                    (ui/icon "table" {:style {:font-size 20}})]))
-
-               [:a.flex.ml-1.fade-link
-                {:title "Setting properties"
-                 :on-click (fn []
-                             (let [all-keys (query-table/get-all-columns-for-result result page-list?)]
-                               (state/pub-event! [:modal/set-query-properties current-block all-keys])))}
-                (ui/icon "settings" {:style {:font-size 20}})]
-
-               [:div.ml-1
-                (when (or full-text-search?
-                          (and query-time (> query-time 50)))
-                  (query-refresh-button query-time {:full-text-search? full-text-search?
-                                                    :on-pointer-down (fn [e]
-                                                                       (util/stop e)
-                                                                       (query-result/trigger-custom-query! config q *query-error *fulltext-query-result))}))]])])
+         (when (and (not db-based?) (not built-in?))
+           (file-query/custom-query-header config
+                                           q
+                                           {:query-error-atom *query-error
+                                            :fulltext-query-result-atom *fulltext-query-result
+                                            :current-block current-block
+                                            :table? table?
+                                            :view-f view-f
+                                            :page-list? page-list?
+                                            :result result
+                                            :collapsed? collapsed?'}))
 
          (when dsl-query? builder)
 

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

@@ -211,7 +211,7 @@
     [:div
      (case @*mode
        "namespace"
-       (let [items (sort (db-model/get-all-namespace-parents repo))]
+       (let [items (sort (map :block/title (db-model/get-all-namespace-parents repo)))]
          (select items
                  (fn [{:keys [value]}]
                    (append-tree! *tree opts loc [:namespace value]))))
@@ -276,7 +276,7 @@
 
        "full text search"
        (search (fn [v] (append-tree! *tree opts loc v))
-               (:toggle-fn opts))
+         (:toggle-fn opts))
 
        "between"
        (between (merge opts
@@ -364,7 +364,8 @@
         (str "#" (second (second clause))))
 
       (contains? #{:property :page-property} (keyword f))
-      (str (if (config/db-based-graph? (state/get-current-repo))
+      (str (if (and (config/db-based-graph? (state/get-current-repo))
+                    (qualified-keyword? (second clause)))
              (:block/title (db/entity (second clause)))
              (name (second clause)))
            ": "

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

@@ -396,7 +396,8 @@
   "Returns boolean indicating if DB graph name is invalid. Must be kept in sync with invalid-graph-name-warning"
   [graph-name]
   (or (fs-util/include-reserved-chars? graph-name)
-    (string/includes? graph-name "+")))
+    (string/includes? graph-name "+")
+    (string/includes? graph-name "/")))
 
 (rum/defcs new-db-graph < rum/reactive
                           (rum/local "" ::graph-name)

+ 3 - 10
src/main/frontend/components/right_sidebar.cljs

@@ -9,7 +9,6 @@
             [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
             [frontend.db :as db]
-            [frontend.db-mixins :as db-mixins]
             [frontend.extensions.slide :as slide]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.ui :as ui-handler]
@@ -45,14 +44,8 @@
 (rum/defc page-cp < rum/reactive
   [repo page-name]
   (page/page-cp {:parameters {:path {:name page-name}}
-              :sidebar?   true
-              :repo       repo}))
-
-(rum/defc contents < rum/reactive db-mixins/query
-  []
-  [:div.contents.flex-col.flex.ml-3
-   (when-let [contents-page (db/get-page "contents")]
-     (page/contents-page contents-page))])
+                 :sidebar?   true
+                 :repo       repo}))
 
 (rum/defc shortcut-settings
   []
@@ -74,7 +67,7 @@
   (case (keyword block-type)
     :contents
     [[:.flex.items-center (ui/icon "list-details" {:class "text-md mr-2"}) (t :right-side-bar/contents)]
-     (contents)]
+     (page-cp repo "contents")]
 
     :help
     [[:.flex.items-center (ui/icon "help" {:class "text-md mr-2"}) (t :right-side-bar/help)] (onboarding/help)]

+ 5 - 3
src/main/frontend/components/settings.cljs

@@ -550,8 +550,10 @@
       (string/blank? value)
       (let [home (get (state/get-config) :default-home {})
             new-home (dissoc home :page)]
-        (config-handler/set-config! :default-home new-home)
-        (notification/show! "Home default page updated successfully!" :success))
+        (p/do!
+         (config-handler/set-config! :default-home new-home)
+         (config-handler/set-config! :feature/enable-journals? true)
+         (notification/show! "Journals enabled" :success)))
 
       ;; FIXME: home page should be db id instead of page name
       (ldb/get-page (db/get-db) value)
@@ -1120,7 +1122,7 @@
        (http-server-switcher-row))
      (when-not db-based? (flashcards-switcher-row enable-flashcards?))
      (when-not db-based? (zotero-settings-row))
-     (when (config/db-based-graph? current-repo)
+     (when (and config/dev? (config/db-based-graph? current-repo))
        ;; FIXME: Wire this up again to RTC init calls
        (rtc-switcher-row (state/enable-rtc? current-repo)))
      (when-not web-platform?

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

@@ -1,12 +1,14 @@
 (ns frontend.components.title
   (:require [clojure.string :as string]
-            [frontend.db :as db]))
+            [frontend.db :as db]
+            [logseq.db :as ldb]))
 
 (defn block-unique-title
   "Multiple pages/objects may have the same `:block/title`.
    Notice: this doesn't prevent for pages/objects that have the same tag or created by different clients."
   [block]
-  (if (seq (:block/tags block))
+  (if (and (seq (:block/tags block))
+           (not (ldb/journal? block)))
     (str (:block/title block)
          " "
          (string/join

+ 79 - 83
src/main/frontend/components/views.cljs

@@ -104,94 +104,77 @@
 
 (rum/defc block-title < rum/static
   [config row]
-  (let [[show-open? set-show-open!] (rum/use-state false)
-        block-container (state/get-component :block/container)]
+  (let [block-container (state/get-component :block/container)]
     [:div.relative.w-full
-     {:on-mouse-over #(set-show-open! true)
-      :on-mouse-out #(set-show-open! false)}
-     (block-container (assoc config :table? true) row)
-     [:div.absolute.-top-1.right-0.transition-opacity
-      {:class (if show-open? "opacity-100" "opacity-0")}
-      (shui/button
-       {:variant :ghost
-        :size :sm
-        :class "!px-2 !py-0 text-muted-foreground"
-        :on-click (fn [e]
-                    (util/stop e)
-                    (let [page? (db/page? row)]
-                      (state/sidebar-add-block!
-                       (state/get-current-repo)
-                       (:db/id row)
-                       (if page? :page :block))))}
-       [:span.text-xs "Open"]
-       (ui/icon "layout-sidebar-right" {:size 14}))]]))
+     (block-container (assoc config :table? true) row)]))
 
 (defn build-columns
   [config properties & {:keys [with-object-name?]
                         :or {with-object-name? true}}]
   (->> (concat
-          [{:id :select
-            :name "Select"
-            :header (fn [table _column] (header-checkbox table))
-            :cell (fn [table row column]
-                    (row-checkbox table row column))
-            :column-list? false}
-           (when with-object-name?
-             {:id :block/title
-              :name "Name"
-              :type :string
-              :header header-cp
-              :cell (fn [_table row _column]
-                      (block-title config row))
-              :disable-hide? true})]
-          (map
-           (fn [property]
-             (let [ident (or (:db/ident property) (:id property))
-                   property (if (de/entity? property)
-                              property
-                              (or (db/entity ident) property))
-                   get-value (or (:get-value property)
-                                 (when (de/entity? property)
-                                   (fn [row] (get-property-value-for-search row property))))
-                   closed-values (seq (:property/closed-values property))
-                   closed-value->sort-number (when closed-values
-                                               (->> (zipmap (map :db/id closed-values) (range 0 (count closed-values)))
-                                                    (into {})))
-                   get-value-for-sort (fn [row]
-                                        (cond
-                                          (= (:db/ident property) :logseq.task/deadline)
-                                          (:block/journal-day (get row :logseq.task/deadline))
-                                          closed-values
-                                          (closed-value->sort-number (:db/id (get row (:db/ident property))))
-                                          :else
-                                          (if (fn? get-value)
-                                            (get-value row)
-                                            (get row ident))))]
-               {:id ident
-                :name (or (:name property)
-                          (:block/title property))
-                :header (or (:header property)
-                            header-cp)
-                :cell (or (:cell property)
-                          (when (de/entity? property)
-                            (fn [_table row _column]
-                              (pv/property-value row property (get row (:db/ident property)) {}))))
-                :get-value get-value
-                :get-value-for-sort get-value-for-sort
-                :type (:type property)}))
-           properties)
-
-          [{:id :block/created-at
-            :name (t :page/created-at)
-            :type :date-time
-            :header header-cp
-            :cell timestamp-cell-cp}
-           {:id :block/updated-at
-            :name (t :page/updated-at)
-            :type :date-time
+        [{:id :select
+          :name "Select"
+          :header (fn [table _column] (header-checkbox table))
+          :cell (fn [table row column]
+                  (row-checkbox table row column))
+          :column-list? false}
+         (when with-object-name?
+           {:id :block/title
+            :name "Name"
+            :type :string
             :header header-cp
-            :cell timestamp-cell-cp}])
-         (remove nil?)))
+            :cell (fn [_table row _column]
+                    (block-title config row))
+            :disable-hide? true})]
+        (keep
+         (fn [property]
+           (let [ident (or (:db/ident property) (:id property))]
+             (when-not (contains? #{:logseq.property/built-in?} ident)
+               (let [property (if (de/entity? property)
+                                property
+                                (or (db/entity ident) property))
+                     get-value (or (:get-value property)
+                                   (when (de/entity? property)
+                                     (fn [row] (get-property-value-for-search row property))))
+                     closed-values (seq (:property/closed-values property))
+                     closed-value->sort-number (when closed-values
+                                                 (->> (zipmap (map :db/id closed-values) (range 0 (count closed-values)))
+                                                      (into {})))
+                     get-value-for-sort (fn [row]
+                                          (cond
+                                            (= (:db/ident property) :logseq.task/deadline)
+                                            (:block/journal-day (get row :logseq.task/deadline))
+                                            closed-values
+                                            (closed-value->sort-number (:db/id (get row (:db/ident property))))
+                                            :else
+                                            (if (fn? get-value)
+                                              (get-value row)
+                                              (get row ident))))]
+                 {:id ident
+                  :name (or (:name property)
+                            (:block/title property))
+                  :header (or (:header property)
+                              header-cp)
+                  :cell (or (:cell property)
+                            (when (de/entity? property)
+                              (fn [_table row _column]
+                                (pv/property-value row property (get row (:db/ident property)) {}))))
+                  :get-value get-value
+                  :get-value-for-sort get-value-for-sort
+                  :type (:type property)}))))
+         properties)
+
+        [{:id :block/created-at
+          :name (t :page/created-at)
+          :type :date-time
+          :header header-cp
+          :cell timestamp-cell-cp}
+         {:id :block/updated-at
+          :name (t :page/updated-at)
+          :type :date-time
+          :header header-cp
+          :cell timestamp-cell-cp}])
+       (remove nil?)))
 
 (defn- sort-columns
   [columns ordered-column-ids]
@@ -695,7 +678,7 @@
             (->> (map (fn [v] [:div (get-property-value-content v)]) value)
                  (interpose [:div "or"]))
             :else
-            "Empty")])))
+            "All")])))
      (shui/dropdown-menu-content
       {:align "start"}
       (select/select option)))))
@@ -1050,7 +1033,20 @@
 
        (table-view table option row-selection add-new-object! ready?))]))
 
-(rum/defc view < rum/reactive
+(rum/defc view
+  "Provides a view for data like query results and tagged objects, multiple
+   layouts such as table and list are supported. Args:
+   * view-entity: a db Entity
+   * option:
+     * title-key: dict key defaults to `:views.table/default-title`
+     * data: a collections of entities
+     * set-data!: `fn` to update `data`
+     * columns: view columns including properties and db attributes, which could be built by `build-columns`
+     * add-new-object!: `fn` to create a new object (or row)
+     * show-add-property?: whether to show `Add property`
+     * add-property!: `fn` to add a new property (or column)
+     * on-delete-rows: `fn` to trigger when deleting selected objects"
+  < rum/reactive
   [view-entity option]
   (let [view-entity' (db/sub-block (:db/id view-entity))]
     (rum/with-key (view-inner view-entity' option)

+ 37 - 0
src/main/frontend/date.cljs

@@ -179,6 +179,43 @@
     (goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))
     :else d))
 
+(def nlp-pages
+  ["Today"
+   "Tomorrow"
+   "Yesterday"
+   "Next week"
+   "This week"
+   "Last week"
+   "Next month"
+   "This month"
+   "Last month"
+   "Next year"
+   "This year"
+   "Last year"
+   "Last Monday"
+   "Last Tuesday"
+   "Last Wednesday"
+   "Last Thursday"
+   "Last Friday"
+   "Last Saturday"
+   "Last Sunday"
+   "This Monday"
+   "This Tuesday"
+   "This Wednesday"
+   "This Thursday"
+   "This Friday"
+   "This Saturday"
+   "This Sunday"
+   "Next Monday"
+   "Next Tuesday"
+   "Next Wednesday"
+   "Next Thursday"
+   "Next Friday"
+   "Next Saturday"
+   "Next Sunday"])
+
+
+
 (comment
   (def default-formatter (tf/formatter "MMM do, yyyy"))
   (def zh-formatter (tf/formatter "YYYY年MM月dd日"))

+ 33 - 108
src/main/frontend/db/model.cljs

@@ -18,8 +18,7 @@
             [logseq.common.util :as common-util]
             [logseq.common.util.date-time :as date-time-util]
             [frontend.config :as config]
-            [logseq.db :as ldb]
-            [logseq.graph-parser.text :as text]))
+            [logseq.db :as ldb]))
 
 ;; TODO: extract to specific models and move data transform logic to the
 ;; corresponding handlers.
@@ -30,11 +29,9 @@
 
 (defn get-all-tagged-pages
   [repo]
-  (d/q '[:find ?page-name ?tag
+  (d/q '[:find ?page ?tag
          :where
-         [?page :block/tags ?e]
-         [?e :block/title ?tag]
-         [?page :block/name ?page-name]]
+         [?page :block/tags ?tag]]
        (conn/get-db repo)))
 
 (defn get-all-pages
@@ -47,18 +44,6 @@
   (->> (get-all-pages repo)
        (map :block/title)))
 
-(defn get-page-alias
-  [repo page-id]
-  (when-let [db (and repo (conn/get-db repo))]
-    (some->> (d/q '[:find ?alias
-                    :in $ ?page-id
-                    :where
-                    [?page-id :block/alias ?alias]]
-                  db
-                  page-id)
-             db-utils/seq-flatten
-             distinct)))
-
 (defn get-alias-source-page
   "return the source page of an alias"
   [repo alias-id]
@@ -553,43 +538,36 @@ independent of format as format specific heading characters are stripped"
   (when-let [db (conn/get-db repo)]
     (let [pages (page-alias-set repo page-id)
           ref-pages (d/q
-                     '[:find ?ref-page ?ref-page-name
+                     '[:find [?ref-page ...]
                        :in $ ?pages
                        :where
                        [(untuple ?pages) [?page ...]]
                        [?block :block/page ?page]
-                       [?block :block/refs ?ref-page]
-                       [?ref-page :block/name ?ref-page-name]]
+                       [?block :block/refs ?ref-page]]
                      db
                      pages)]
-      (mapv (fn [[ref-page ref-page-name]]
-              [ref-page-name (get-page-alias repo ref-page)])
-            ref-pages))))
+      ref-pages)))
 
 ;; get pages who mentioned this page
 (defn get-pages-that-mentioned-page
   [repo page-id include-journals?]
   (when (conn/get-db repo)
     (let [pages (page-alias-set repo page-id)
-          mentioned-pages (->> (react/q repo [:frontend.worker.react/page<-pages page-id]
-                                        {:query-fn (fn [_]
-                                                     (->>
-                                                      (mapcat
-                                                       (fn [id]
-                                                         (let [page (db-utils/entity repo id)]
-                                                           (->> (:block/_refs page)
-                                                                (keep (fn [ref]
-                                                                        (:block/page ref)))
-                                                                (util/distinct-by :db/id))))
-                                                       pages)))}
-                                        {:use-cache? false})
-                               react)]
-      (->> mentioned-pages
-           (keep (fn [page]
-                   (when-not (and (not include-journals?) (ldb/journal? page))
-                     page)))
-           (mapv (fn [page]
-                   [(:block/name page) (get-page-alias-names repo (:db/id page))]))))))
+          mentioned-pages (->>
+                           (mapcat
+                            (fn [id]
+                              (let [page (db-utils/entity repo id)]
+                                (->> (:block/_refs page)
+                                     (keep (fn [ref]
+                                             (if (ldb/page? ref)
+                                               page
+                                               (:block/page ref)))))))
+                            pages)
+                           (util/distinct-by :db/id))]
+      (keep (fn [page]
+              (when-not (and (not include-journals?) (ldb/journal? page))
+                (:db/id page)))
+            mentioned-pages))))
 
 (defn get-page-referenced-blocks-full
   ([page-id]
@@ -822,87 +800,34 @@ independent of format as format specific heading characters are stripped"
 
 (defn get-all-namespace-relation
   [repo]
-  (d/q '[:find ?page-name ?parent
+  (d/q '[:find ?page ?parent
          :where
-         [?page :block/name ?page-name]
-         [?page :block/namespace ?e]
-         [?e :block/title ?parent]]
+         [?page :block/namespace ?parent]]
     (conn/get-db repo)))
 
 (defn get-all-namespace-parents
   [repo]
-  (->> (get-all-namespace-relation repo)
-       (map second)))
-
-(def ns-char "/")
-(def ns-re #"/")
-
-(defn- get-parents-namespace-list
-  "Return list of parents namespace"
-  [page-namespace & nested-found]
-  (if (text/namespace-page? page-namespace)
-    (let [pre-nested-vec (drop-last (string/split page-namespace ns-re))
-          my-nested-found (if (nil? nested-found)
-                            []
-                            nested-found)]
-      (if (= (count pre-nested-vec) 1)
-        (conj my-nested-found (nth pre-nested-vec 0))
-        (let [pre-nested-str (string/join ns-char pre-nested-vec)]
-          (recur pre-nested-str (conj my-nested-found pre-nested-str)))))
-    []))
-
-(defn- get-unnecessary-namespaces-name
-  "Return unnecessary namespace from a list of page's name"
-  [pages-list]
-  (->> pages-list
-       (remove nil?)
-       (mapcat get-parents-namespace-list)
-       distinct))
-
-(defn- remove-nested-namespaces-link
-  "Remove relations between pages and their nested namespace"
-  [pages-relations]
-  (let [pages-relations-to-return (distinct (mapcat
-                                             identity
-                                             (for [item (for [a-link-from (mapv (fn [a-rel] (first a-rel)) pages-relations)]
-                                                          [a-link-from (mapv
-                                                                        (fn [a-rel] (second a-rel))
-                                                                        (filterv
-                                                                         (fn [link-target] (=  a-link-from (first link-target)))
-                                                                         pages-relations))])
-                                                   :let [list-to (get item 1)
-                                                         page (get item 0)
-                                                         namespaces-to-remove (get-unnecessary-namespaces-name list-to)
-                                                         list-to-without-nested-ns (filterv (fn [elem] (not (some #{elem} namespaces-to-remove))) list-to)
-                                                         node-links (for [item-ok list-to-without-nested-ns]
-                                                                      [page item-ok])]]
-                                               (seq node-links))))]
-    pages-relations-to-return))
+  (let [db (conn/get-db repo)]
+    (->> (get-all-namespace-relation repo)
+        (map (fn [[_ ?parent]]
+               (db-utils/entity db ?parent))))))
 
 ;; Ignore files with empty blocks for now
 (defn get-pages-relation
   [repo with-journal?]
   (when-let [db (conn/get-db repo)]
     (let [q (if with-journal?
-              '[:find ?page ?ref-page-name
+              '[:find ?p ?ref-page
                 :where
-                [?p :block/name ?page]
                 [?block :block/page ?p]
-                [?block :block/refs ?ref-page]
-                [?ref-page :block/name ?ref-page-name]]
-              '[:find ?page ?ref-page-name
+                [?block :block/refs ?ref-page]]
+              '[:find ?p ?ref-page
                 :where
-                [?p :block/name ?page]
+                [?block :block/page ?p]
                 [(get-else $ ?p :block/type "N/A") ?type]
                 [(not= ?type "journal")]
-                [?block :block/page ?p]
-                [?block :block/refs ?ref-page]
-                [?ref-page :block/name ?ref-page-name]])]
-      (->>
-       (d/q q db)
-       (map (fn [[page ref-page-name]]
-              [page ref-page-name]))
-       (remove-nested-namespaces-link)))))
+                [?block :block/refs ?ref-page]])]
+      (d/q q db))))
 
 (defn get-namespace-pages
   "Accepts both sanitized and unsanitized namespaces"

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

@@ -19,7 +19,6 @@
             [frontend.util.text :as text-util]
             [frontend.util :as util]
             [frontend.config :as config]
-            [logseq.db.frontend.property :as db-property]
             [frontend.state :as state]))
 
 
@@ -284,7 +283,14 @@
   [property-name]
   (if (qualified-keyword? property-name)
     property-name
-    (keyword db-property/default-user-namespace (name property-name))))
+    (or (some->> (name property-name)
+                 (db-utils/q '[:find [(pull ?b [:db/ident]) ...]
+                               :in $ ?title
+                               :where [?b :block/type "property"] [?b :block/title ?title]])
+                 first
+                 :db/ident)
+        ;; Don't return nil as that incorrectly matches all properties
+        ::no-property-found)))
 
 (defn- build-property-two-arg
   [e {:keys [db-graph?]}]

+ 11 - 4
src/main/frontend/extensions/code.cljs

@@ -395,14 +395,17 @@
      (let [block (db/pull [:block/uuid block-id])]
        (editor-handler/edit-block! block :max)))))
 
-(defn render!
+(defn ^:large-vars/cleanup-todo render!
   [state]
   (let [[config id attr _code theme user-options] (:rum/args state)
+        config-file? (= (:file-path config) "logseq/config.edn")
+        edit-block (state/get-edit-block)
         default-open? (and (:editor/code-mode? @state/state)
-                           (= (:block/uuid (state/get-edit-block))
+                           (= (:block/uuid edit-block)
                               (get-in config [:block :block/uuid])))
         _ (state/set-state! :editor/code-mode? false)
         original-mode (get attr :data-lang)
+        *editor-ref (get attr :editor-ref)
         mode (if (:file? config)
                (text->cm-mode original-mode :ext) ;; ref: src/main/frontend/components/file.cljs
                (text->cm-mode original-mode :name))
@@ -418,7 +421,9 @@
                             :matchBrackets lisp-like?
                             :styleActiveLine true}
         cm-options (merge default-cm-options
-                          (extra-codemirror-options)
+                          (cond-> (extra-codemirror-options)
+                            config-file?
+                            (dissoc :readOnly))
                           {:mode mode
                            :tabIndex -1 ;; do not accept TAB-in, since TAB is bind globally
                            :extraKeys (merge {"Esc" (fn [cm]
@@ -435,7 +440,9 @@
                             {:hintOptions {}})
                           user-options)
         editor (when textarea
-                 (from-textarea textarea (clj->js cm-options)))]
+                 (from-textarea textarea (clj->js cm-options)))
+        _ (when (and editor *editor-ref)
+            (reset! *editor-ref editor))]
     (when editor
       (let [textarea-ref (rum/ref-node state textarea-ref-name)
             element (.getWrapperElement editor)]

+ 4 - 1
src/main/frontend/extensions/graph.cljs

@@ -3,6 +3,7 @@
             [frontend.extensions.graph.pixi :as pixi]
             [frontend.handler.route :as route-handler]
             [frontend.colors :as colors]
+            [frontend.db :as db]
             [goog.object :as gobj]
             [rum.core :as rum]))
 
@@ -42,7 +43,9 @@
       (highlight-edges! graph node dark?))
     (when-not drag?
       (.unhoverNode ^js graph node)
-      (route-handler/redirect-to-page! node))))
+      (when-let [page (and (string? node)
+                           (some-> (js/parseInt node) db/entity))]
+        (route-handler/redirect-to-page! (:block/uuid page))))))
 
 (rum/defcs graph-2d <
   (rum/local nil :ref)

+ 26 - 26
src/main/frontend/extensions/graph/pixi.cljs

@@ -40,7 +40,7 @@
                         (let [v (js/Math.abs (hash parent))]
                           (nth colors (mod v (count colors)))))
                       (.-color node)))
-          :label  {:content  (fn [node] (.-id node))
+          :label  {:content  (fn [node] (.-label node))
                    :type     (.-TEXT (.-TextType Pixi-Graph))
                    :fontSize 12
                    :color (if dark? "rgba(255, 255, 255, 0.8)" "rgba(0, 0, 0, 0.8)")
@@ -201,39 +201,39 @@
       (clear-nodes! (:graph @*graph-instance))
       (destroy-instance!))
     (let [{:keys [nodes links style hover-style height register-handlers-fn dark? link-dist charge-strength charge-range]} (first (:rum/args state))
-          style                                                                     (or style (default-style dark?))
-          hover-style                                                               (or hover-style (default-hover-style dark?))
-          graph                                                                     (Graph.)
-          nodes-set                                                                 (set (map :id nodes))
-          links                                                                     (->>
-                                                                                     (filter
-                                                                                      (fn [link]
-                                                                                        (and (nodes-set (:source link)) (nodes-set (:target link))))
-                                                                                      links)
-                                                                                     (distinct)) ;; #3331 (@zhaohui0923) seems caused by duplicated links. Why distinct doesn't work?
-          nodes                                                                     (remove nil? nodes)
-          links                                                                     (remove (fn [{:keys [source target]}] (or (nil? source) (nil? target))) links)
-          nodes-js                                                                  (bean/->js nodes)
-          links-js                                                                  (bean/->js links)
-          simulation                                                                (layout! nodes-js links-js link-dist charge-strength charge-range)]
+          style       (or style (default-style dark?))
+          hover-style (or hover-style (default-hover-style dark?))
+          graph       (Graph.)
+          nodes-set   (set (map :id nodes))
+          links       (->>
+                       (filter
+                        (fn [link]
+                          (and (nodes-set (:source link)) (nodes-set (:target link))))
+                        links)
+                       (distinct)) ;; #3331 (@zhaohui0923) seems caused by duplicated links. Why distinct doesn't work?
+          nodes       (remove nil? nodes)
+          links       (remove (fn [{:keys [source target]}] (or (nil? source) (nil? target))) links)
+          nodes-js    (bean/->js nodes)
+          links-js    (bean/->js links)
+          simulation  (layout! nodes-js links-js link-dist charge-strength charge-range)]
       (doseq [node nodes-js]
         (try (.addNode graph (.-id node) node)
-          (catch :default e
-            (js/console.error e))))
+             (catch :default e
+               (js/console.error e))))
       (doseq [link links-js]
         (let [source (.-id (.-source link))
               target (.-id (.-target link))]
           (try (.addEdge graph source target link)
-            (catch :default e
-              (js/console.error e)))))
+               (catch :default e
+                 (js/console.error e)))))
       (when-let [container-ref (:ref state)]
         (let [pixi-graph (new (.-PixiGraph Pixi-Graph)
-                           (bean/->js
-                            {:container  @container-ref
-                             :graph      graph
-                             :style      style
-                             :hoverStyle hover-style
-                             :height     height}))]
+                              (bean/->js
+                               {:container  @container-ref
+                                :graph      graph
+                                :style      style
+                                :hoverStyle hover-style
+                                :height     height}))]
           (reset! *graph-instance
                   {:graph graph
                    :pixi  pixi-graph})

+ 1 - 1
src/main/frontend/format/block.cljs

@@ -29,7 +29,7 @@ and handles unexpected failure."
                                              :db-graph-mode? (config/db-based-graph? repo)})]
         (if (config/db-based-graph? repo)
           (map (fn [block]
-                (cond-> (dissoc block :block/properties)
+                (cond-> (dissoc block :block/properties :block/macros :block/properties-order)
                   (:block/properties block)
                   (merge (update-keys (:block/properties block)
                                       (fn [k]

+ 2 - 2
src/main/frontend/handler/code.cljs

@@ -28,8 +28,8 @@
           (cond
             ;; save block content
             (:block/uuid config)
-            (let [block (db/pull [:block/uuid (:block/uuid config)])
-                  content (:block/title block)
+            (let [block (db/entity [:block/uuid (:block/uuid config)])
+                  content (:block/raw-title block)
                   {:keys [start_pos end_pos]} (:pos_meta @(:code-options state))
                   offset (if (:block/pre-block? block) 0 2)
                   raw-content (utf8/encode content) ;; NOTE: :pos_meta is based on byte position

+ 55 - 38
src/main/frontend/handler/common/page.cljs

@@ -21,7 +21,8 @@
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.handler.db-based.editor :as db-editor-handler]
             [logseq.db.frontend.content :as db-content]
-            [logseq.common.util.page-ref :as page-ref]))
+            [logseq.common.util.page-ref :as page-ref]
+            [frontend.handler.notification :as notification]))
 
 (defn- wrap-tags
   "Tags might have multiple words"
@@ -42,34 +43,41 @@
   ([title {:keys [redirect?]
            :or   {redirect? true}
            :as options}]
-   (p/let [repo (state/get-current-repo)
-           conn (db/get-db repo false)
-           db-based? (config/db-based-graph? repo)
-           title (if (and db-based? (string/includes? title " #")) ; tagged page
-                   (wrap-tags title)
-                   title)
-           parsed-result (when db-based? (db-editor-handler/wrap-parse-block {:block/title title}))
-           title' (if (and db-based? (seq (:block/tags parsed-result)))
-                    (string/trim (first
-                                  (or (common-util/split-first (str "#" db-content/page-ref-special-chars) (:block/title parsed-result))
-                                      (common-util/split-first (str "#" page-ref/left-brackets db-content/page-ref-special-chars) (:block/title parsed-result)))))
-                    title)
-           options' (if db-based?
-                      (cond->
-                        (update options :tags concat (:block/tags parsed-result))
-                        (nil? (:split-namespace? options))
-                        (assoc :split-namespace? true))
-                      options)
-           result (ui-outliner-tx/transact!
-                   {:outliner-op :create-page}
-                   (outliner-op/create-page! title' options'))
-           [_page-name page-uuid] (ldb/read-transit-str result)]
-     (when redirect?
-       (route-handler/redirect-to-page! page-uuid))
-     (let [page (db/get-page (or page-uuid title'))]
-       (when-let [first-block (ldb/get-first-child @conn (:db/id page))]
-         (block-handler/edit-block! first-block :max {:container-id :unknown-container}))
-       page))))
+   (when (string? title)
+     (p/let [repo (state/get-current-repo)
+             conn (db/get-db repo false)
+             db-based? (config/db-based-graph? repo)
+             title (if (and db-based? (string/includes? title " #")) ; tagged page
+                     (wrap-tags title)
+                     title)
+             parsed-result (when db-based? (db-editor-handler/wrap-parse-block {:block/title title}))
+             has-tags? (and db-based? (seq (:block/tags parsed-result)))
+             title' (if has-tags?
+                      (some-> (first
+                               (or (common-util/split-first (str "#" db-content/page-ref-special-chars) (:block/title parsed-result))
+                                   (common-util/split-first (str "#" page-ref/left-brackets db-content/page-ref-special-chars) (:block/title parsed-result))))
+                              string/trim)
+                      title)]
+       (if (and has-tags? (nil? title'))
+         (notification/show! "Page name can't include \"#\"." :warning)
+         (when-not (string/blank? title')
+           (p/let [options' (if db-based?
+                              (cond->
+                               (update options :tags concat (:block/tags parsed-result))
+                                (nil? (:split-namespace? options))
+                                (assoc :split-namespace? true))
+                              options)
+                   result (ui-outliner-tx/transact!
+                           {:outliner-op :create-page}
+                           (outliner-op/create-page! title' options'))
+                   [_page-name page-uuid] (ldb/read-transit-str result)]
+             (when redirect?
+               (route-handler/redirect-to-page! page-uuid))
+             (let [page (db/get-page (or page-uuid title'))]
+               (when-let [first-block (ldb/get-first-child @conn (:db/id page))]
+                 (block-handler/edit-block! first-block :max {:container-id :unknown-container}))
+               page))))))))
+
 
 ;; favorite fns
 ;; ============
@@ -154,15 +162,24 @@
     (when-let [page-uuid (or (and (uuid? page-uuid-or-name) page-uuid-or-name)
                              (:block/uuid (db/get-page page-uuid-or-name)))]
       (when @state/*db-worker
-        (-> (p/let [res (ui-outliner-tx/transact!
-                         {:outliner-op :delete-page}
-                         (outliner-op/delete-page! page-uuid))
-                    res' (ldb/read-transit-str res)]
-              (if res'
-                (when ok-handler (ok-handler))
-                (when error-handler (error-handler))))
-            (p/catch (fn [error]
-                       (js/console.error error))))))))
+        (let [page (db/entity [:block/uuid page-uuid])
+              default-home (state/get-default-home)
+              home-page? (= (:block/title page) (:page default-home))]
+          (p/do!
+           (when home-page?
+             (p/do!
+              (config-handler/set-config! :default-home (dissoc default-home :page))
+              (config-handler/set-config! :feature/enable-journals? true)
+              (notification/show! "Journals enabled" :success)))
+           (-> (p/let [res (ui-outliner-tx/transact!
+                            {:outliner-op :delete-page}
+                            (outliner-op/delete-page! page-uuid))
+                       res' (ldb/read-transit-str res)]
+                 (if res'
+                   (when ok-handler (ok-handler))
+                   (when error-handler (error-handler))))
+               (p/catch (fn [error]
+                          (js/console.error error))))))))))
 
 ;; other fns
 ;; =========

+ 2 - 1
src/main/frontend/handler/db_based/editor.cljs

@@ -43,7 +43,8 @@
         title->ref (zipmap (map :block/title cached-refs) cached-refs)]
     (map (fn [x]
            (if-let [ref (and (map? x) (title->ref (:block/title x)))]
-             ref
+             (assoc ref :block.temp/original-page-name
+                    (:block.temp/original-page-name x))
              x))
          refs)))
 

+ 7 - 5
src/main/frontend/handler/db_based/page.cljs

@@ -45,12 +45,14 @@
 
 (defn convert-to-tag!
   [page-entity]
-  (let [class (db-class/build-new-class (db/get-db)
-                                        {:db/id (:db/id page-entity)
-                                         :block/title (:block/title page-entity)
-                                         :block/created-at (:block/created-at page-entity)})]
+  (if (db/page-exists? (:block/title page-entity) "class")
+    (notification/show! (str "A tag with the name \"" (:block/title page-entity) "\" already exists.") :warning false)
+    (let [class (db-class/build-new-class (db/get-db)
+                                         {:db/id (:db/id page-entity)
+                                          :block/title (:block/title page-entity)
+                                          :block/created-at (:block/created-at page-entity)})]
 
-    (db/transact! (state/get-current-repo) [class] {:outliner-op :save-block})))
+     (db/transact! (state/get-current-repo) [class] {:outliner-op :save-block}))))
 
 (defn <create-class!
   "Creates a class page and provides class-specific error handling"

+ 56 - 63
src/main/frontend/handler/editor.cljs

@@ -893,31 +893,6 @@
                                                           "")]
            (when edit-block-f (edit-block-f))))))))
 
-(defn set-block-query-properties!
-  [block-id all-properties key add?]
-  (when-let [block (db/entity [:block/uuid block-id])]
-    (let [query-properties (get block (pu/get-pid :logseq.property/query-properties))
-          repo (state/get-current-repo)
-          db-based? (config/db-based-graph? repo)
-          query-properties (if db-based?
-                             query-properties
-                             (some-> query-properties
-                                     (common-handler/safe-read-string "Parsing query properties failed")))
-          query-properties (if (seq query-properties)
-                             query-properties
-                             all-properties)
-          query-properties (if add?
-                             (distinct (conj query-properties key))
-                             (remove #{key} query-properties))
-          query-properties (vec query-properties)]
-      (if (seq query-properties)
-        (property-handler/set-block-property! repo block-id
-                                              (pu/get-pid :logseq.property/query-properties)
-                                              (if db-based?
-                                                query-properties
-                                                (str query-properties)))
-        (property-handler/remove-block-property! repo block-id (pu/get-pid :logseq.property/query-properties))))))
-
 (defn set-block-timestamp!
   [block-id key value]
   (let [key (string/lower-case (str key))
@@ -1305,7 +1280,7 @@
     (and (state/selection?) (= direction (state/get-selection-direction)))
     (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed-skip)
           first-last (if (= :up direction) first last)
-          element (f (first-last (state/get-selection-blocks)))]
+          element (f (first-last (state/get-selection-blocks)) {:up-down? true})]
       (when element
         (util/scroll-to-block element)
         (state/conj-selection-block! element direction)))
@@ -1314,7 +1289,7 @@
     (state/selection?)
     (let [f (if (= :up direction) util/get-prev-block-non-collapsed util/get-next-block-non-collapsed)
           last-first (if (= :up direction) last first)
-          element (f (last-first (state/get-selection-blocks)))]
+          element (f (last-first (state/get-selection-blocks)) {:up-down? true})]
       (when element
         (util/scroll-to-block element)
         (state/drop-last-selection-block!))))
@@ -1328,7 +1303,7 @@
 (defn save-block-aux!
   [block value opts]
   (let [entity (db/entity [:block/uuid (:block/uuid block)])]
-    (when (and (:db/id entity) (not (ldb/property? entity)))
+    (when (and (:db/id entity) (not (ldb/built-in? entity)))
       (let [value (string/trim value)]
         ;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever
         ;; maybe we shouldn't save the block/file in "will-unmount" event?
@@ -1677,28 +1652,20 @@
 
 (defn <get-matched-blocks
   "Return matched blocks that are not built-in"
-  [q]
+  [q & [{:keys [nlp-pages?]}]]
   (p/let [block (state/get-edit-block)
-          editing-page-id (and block
-                               (when-let [page-id (:db/id (:block/page block))]
-                                 (:block/uuid (db/entity page-id))))
-          block-parents (when block
-                          (set (->> (db/get-block-parents (state/get-current-repo)
-                                                          (:block/uuid block)
-                                                          {:depth 99})
-                                    (map :block/uuid))))
-          current-and-parents (set/union #{(:block/uuid block)} block-parents)
-          pages (search/block-search (state/get-current-repo) q {:built-in? false
-                                                                 :enable-snippet? false})]
-    (->> (if editing-page-id
-           ;; To prevent self references
-           (remove (fn [b]
-                     (or (= editing-page-id (:block/uuid b))
-                         (contains? current-and-parents (:block/uuid b)))) pages)
-           pages)
-         (keep (fn [b]
-                 (when-let [id (:block/uuid b)]
-                   (db/entity [:block/uuid id])))))))
+          nodes (search/block-search (state/get-current-repo) q {:built-in? false
+                                                                 :enable-snippet? false})
+          matched (keep (fn [b]
+                          (when-let [id (:block/uuid b)]
+                            (when-not (= id (:block/uuid block)) ; avoid block self-reference
+                              (db/entity [:block/uuid id]))))
+                        nodes)]
+    (-> (concat matched
+                (when nlp-pages?
+                  (map (fn [title] {:block/title title :nlp-date? true})
+                       date/nlp-pages)))
+        (search/fuzzy-search q {:extract-fn :block/title :limit 50}))))
 
 (defn <get-matched-templates
   [q]
@@ -2583,7 +2550,7 @@
         f (case direction
             :up util/get-prev-block-non-collapsed
             :down util/get-next-block-non-collapsed)
-        sibling-block (f selected)]
+        sibling-block (f selected {:up-down? true})]
     (when (and sibling-block (dom/attr sibling-block "blockid"))
       (util/scroll-to-block sibling-block)
       (state/exit-editing-and-set-selected-blocks! [sibling-block]))))
@@ -2597,7 +2564,7 @@
               :up util/get-prev-block-non-collapsed
               :down util/get-next-block-non-collapsed)
           current-block (util/rec-get-node input "ls-block")
-          sibling-block (f current-block)
+          sibling-block (f current-block {:up-down? true})
           {:block/keys [uuid title format]} (state/get-edit-block)]
       (if sibling-block
         (when-let [sibling-block-id (dom/attr sibling-block "blockid")]
@@ -2667,12 +2634,26 @@
         selected-start (util/get-selection-start input)
         selected-end (util/get-selection-end input)
         left? (= direction :left)
-        right? (= direction :right)]
-    (when (= input element)
+        right? (= direction :right)
+        block (some-> (state/get-edit-block) :db/id db/entity)
+        property? (ldb/property? block)]
+    (cond
+      (and input (not= input element))
+      (.focus input)
+
+      (= input element)
       (cond
+        (and property? right? (cursor/end? input) (not= (get-in block [:block/schema :type]) :default))
+        (let [pair (util/rec-get-node input "property-pair")
+              jtrigger (when pair (dom/sel1 pair ".property-value-container .jtrigger"))]
+          (when jtrigger
+            (.focus jtrigger)))
+
         (not= selected-start selected-end)
-        (if left?
+        (cond
+          left?
           (cursor/move-cursor-to input selected-start)
+          :else
           (cursor/move-cursor-to input selected-end))
 
         (or (and left? (cursor/start? input))
@@ -2682,7 +2663,10 @@
         :else
         (if left?
           (cursor/move-cursor-backward input)
-          (cursor/move-cursor-forward input))))))
+          (cursor/move-cursor-forward input)))
+
+      :else
+      nil)))
 
 (defn- delete-and-update [^js input start end]
   (util/safe-set-range-text! input "" start end)
@@ -2879,6 +2863,11 @@
         (state/set-state! :editor/start-pos pos))
 
       (cond
+        (and (= :page-search (state/get-editor-action))
+             (= key commands/hashtag))
+        (do
+          (util/stop e)
+          (notification/show! "Page name can't include \"#\"." :warning))
         ;; stop accepting edits if the new block is not created yet
         (some? @(:editor/async-unsaved-chars @state/state))
         (do
@@ -3516,11 +3505,13 @@
 (defn collapse-block! [block-id]
   (when (collapsable? block-id)
     (when-not (skip-collapsing-in-db?)
-      (set-blocks-collapsed! [block-id] true))))
+      (set-blocks-collapsed! [block-id] true))
+    (state/set-collapsed-block! block-id true)))
 
 (defn expand-block! [block-id]
   (when-not (skip-collapsing-in-db?)
-    (set-blocks-collapsed! [block-id] false)))
+    (set-blocks-collapsed! [block-id] false))
+  (state/set-collapsed-block! block-id false))
 
 (defn expand!
   ([e] (expand! e false))
@@ -3758,12 +3749,14 @@
 (defn escape-editing
   [& {:keys [select? save-block?]
       :or {save-block? true}}]
-  (p/do!
-   (when save-block? (save-current-block!))
-   (if select?
-     (when-let [node (some-> (state/get-input) (util/rec-get-node "ls-block"))]
-       (state/exit-editing-and-set-selected-blocks! [node]))
-     (state/clear-edit!))))
+  (let [edit-block (state/get-edit-block)]
+    (p/do!
+     (when save-block? (save-current-block!))
+     (if select?
+       (when-let [node (some-> (state/get-input) (util/rec-get-node "ls-block"))]
+         (state/exit-editing-and-set-selected-blocks! [node]))
+       (when (= (:db/id edit-block) (:db/id (state/get-edit-block)))
+         (state/clear-edit!))))))
 
 (defn replace-block-reference-with-content-at-point
   []

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

@@ -8,7 +8,6 @@
             [cljs-bean.core :as bean]
             [clojure.core.async :as async]
             [clojure.core.async.interop :refer [p->c]]
-            [clojure.set :as set]
             [clojure.string :as string]
             [frontend.commands :as commands]
             [frontend.components.cmdk.core :as cmdk]
@@ -38,7 +37,6 @@
             [frontend.fs.nfs :as nfs]
             [frontend.fs.sync :as sync]
             [frontend.fs.watcher-handler :as fs-watcher]
-            [frontend.handler.common :as common-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.file :as file-handler]
             [frontend.handler.file-sync :as file-sync-handler]
@@ -53,9 +51,6 @@
             [frontend.handler.shell :as shell-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
-            [frontend.handler.property.util :as pu]
-            [frontend.handler.db-based.property.util :as db-pu]
-            [frontend.handler.property :as property-handler]
             [frontend.handler.file-based.nfs :as nfs-handler]
             [frontend.handler.code :as code-handler]
             [frontend.handler.db-based.rtc :as rtc-handler]
@@ -293,64 +288,6 @@
     (some-> (ask-permission repo)
       (shui/dialog-open! {:align :top}))))
 
-(defonce *query-properties (atom {}))
-(rum/defc query-properties-settings-inner < rum/reactive
-  {:will-unmount (fn [state]
-                   (reset! *query-properties {})
-                   state)}
-  [block shown-properties all-properties]
-  (let [query-properties (rum/react *query-properties)
-        db-graph? (config/db-based-graph? (state/get-current-repo))]
-    [:div
-     [:h1.font-semibold.-mt-2.mb-2.text-lg (t :query/config-property-settings)]
-     [:a.flex
-      {:title "Refresh list of columns"
-       :on-click
-       (fn []
-         (reset! *query-properties {})
-         (let [k (pu/get-pid :logseq.property/query-properties)]
-           (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block) k)))}
-      (ui/icon "refresh")]
-     (for [property all-properties]
-       (let [property-value (get query-properties property)
-             shown? (if (nil? property-value)
-                      (contains? shown-properties property)
-                      property-value)]
-         [:div.flex.flex-row.my-2.justify-between.align-items
-          [:div (if (and db-graph? (qualified-keyword? property))
-                  (db-pu/get-property-name property)
-                  (name property))]
-          [:div.mt-1 (ui/toggle shown?
-                                (fn []
-                                  (let [value (not shown?)]
-                                    (swap! *query-properties assoc property value)
-                                    (editor-handler/set-block-query-properties!
-                                     (:block/uuid block)
-                                     all-properties
-                                     property
-                                     value)))
-                                true)]]))]))
-
-(defn query-properties-settings
-  [block shown-properties all-properties]
-  (fn [_close-fn]
-    (query-properties-settings-inner block shown-properties all-properties)))
-
-(defmethod handle :modal/set-query-properties [[_ block all-properties]]
-  (let [properties (:block/properties block)
-        query-properties (pu/lookup properties :logseq.property/query-properties)
-        block-properties (if (config/db-based-graph? (state/get-current-repo))
-                           query-properties
-                           (some-> query-properties
-                             (common-handler/safe-read-string "Parsing query properties failed")))
-        shown-properties (if (seq block-properties)
-                           (set block-properties)
-                           (set all-properties))
-        shown-properties (set/intersection (set all-properties) shown-properties)]
-    (shui/dialog-open!
-      (query-properties-settings block shown-properties all-properties)
-      {})))
-
 (defmethod handle :modal/show-cards [_]
   (shui/dialog-open!
     srs/global-cards

+ 77 - 1
src/main/frontend/handler/file_based/events.cljs

@@ -1,17 +1,22 @@
 (ns frontend.handler.file-based.events
   "Events that are only for file graphs"
   (:require [clojure.core.async :as async]
+            [clojure.set :as set]
             [frontend.context.i18n :refer [t]]
+            [frontend.db :as db]
             [frontend.handler.events :as events]
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.file-based.nfs :as nfs-handler]
+            [frontend.handler.common :as common-handler]
+            [frontend.handler.property :as property-handler]
             [frontend.fs.sync :as sync]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.config :as config]
-            [logseq.shui.ui :as shui]))
+            [logseq.shui.ui :as shui]
+            [rum.core :as rum]))
 
 (defmethod events/handle :graph/ask-for-re-index [[_ *multiple-windows? ui]]
   ;; *multiple-windows? - if the graph is opened in multiple windows, boolean atom
@@ -44,3 +49,74 @@
       nfs-handler/rebuild-index!
       #(do (page-handler/create-today-journal!)
            (events/file-sync-restart!))))))
+
+(defn set-block-query-properties!
+  [block-id all-properties key add?]
+  (when-let [block (db/entity [:block/uuid block-id])]
+    (let [query-properties (get-in block [:block/properties :query-properties])
+          repo (state/get-current-repo)
+          query-properties (some-> query-properties
+                                   (common-handler/safe-read-string "Parsing query properties failed"))
+          query-properties (if (seq query-properties)
+                             query-properties
+                             all-properties)
+          query-properties (if add?
+                             (distinct (conj query-properties key))
+                             (remove #{key} query-properties))
+          query-properties (vec query-properties)]
+      (if (seq query-properties)
+        (property-handler/set-block-property! repo block-id
+                                              :query-properties
+                                              (str query-properties))
+        (property-handler/remove-block-property! repo block-id :query-properties)))))
+
+(defonce *query-properties (atom {}))
+(rum/defc query-properties-settings-inner < rum/reactive
+  {:will-unmount (fn [state]
+                   (reset! *query-properties {})
+                   state)}
+  [block shown-properties all-properties]
+  (let [query-properties (rum/react *query-properties)]
+    [:div
+     [:h1.font-semibold.-mt-2.mb-2.text-lg (t :query/config-property-settings)]
+     [:a.flex
+      {:title "Refresh list of columns"
+       :on-click
+       (fn []
+         (reset! *query-properties {})
+         (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block) :query-properties))}
+      (ui/icon "refresh")]
+     (for [property all-properties]
+       (let [property-value (get query-properties property)
+             shown? (if (nil? property-value)
+                      (contains? shown-properties property)
+                      property-value)]
+         [:div.flex.flex-row.my-2.justify-between.align-items
+          [:div (name property)]
+          [:div.mt-1 (ui/toggle shown?
+                                (fn []
+                                  (let [value (not shown?)]
+                                    (swap! *query-properties assoc property value)
+                                    (set-block-query-properties!
+                                     (:block/uuid block)
+                                     all-properties
+                                     property
+                                     value)))
+                                true)]]))]))
+
+(defn query-properties-settings
+  [block shown-properties all-properties]
+  (fn [_close-fn]
+    (query-properties-settings-inner block shown-properties all-properties)))
+
+(defmethod events/handle :modal/set-query-properties [[_ block all-properties]]
+  (let [query-properties (get-in block [:block/properties :query-properties])
+        block-properties (some-> query-properties
+                                 (common-handler/safe-read-string "Parsing query properties failed"))
+        shown-properties (if (seq block-properties)
+                           (set block-properties)
+                           (set all-properties))
+        shown-properties (set/intersection (set all-properties) shown-properties)]
+    (shui/dialog-open!
+      (query-properties-settings block shown-properties all-properties)
+      {})))

+ 87 - 120
src/main/frontend/handler/graph.cljs

@@ -11,82 +11,70 @@
             [frontend.storage :as storage]
             [logseq.graph-parser.db :as gp-db]
             [logseq.db.sqlite.create-graph :as sqlite-create-graph]
-            [logseq.common.util :as common-util]
             [logseq.db :as ldb]
             [frontend.components.title :as title]))
 
 (defn- build-links
   [links]
-  (map (fn [[from to]]
-         {:source from
-          :target to})
-       links))
+  (keep (fn [[from to]]
+          (when (and from to)
+            {:source (str from)
+             :target (str to)}))
+        links))
 
 (defn- build-nodes
   [dark? current-page page-links tags nodes namespaces]
   (let [parents (set (map last namespaces))
         current-page (or current-page "")
-        pages (set (flatten nodes))]
+        pages (util/distinct-by :db/id nodes)]
     (->>
      pages
      (remove nil?)
      (mapv (fn [p]
-             (let [p (str p)
-                   current-page? (= p current-page)
+             (let [page-title (:block/title p)
+                   current-page? (= page-title current-page)
                    color (case [dark? current-page?] ; FIXME: Put it into CSS
                            [false false] "#999"
                            [false true]  "#045591"
                            [true false]  "#93a1a1"
                            [true true]   "#ffffff")
-                   color (if (contains? tags p)
+                   color (if (contains? tags (:db/id p))
                            (if dark? "orange" "green")
                            color)
-                   n (get page-links p 1)
+                   n (get page-links page-title 1)
                    size (int (* 8 (max 1.0 (js/Math.cbrt n))))]
-                (cond->
-                  {:id p
-                   :label p
-                   :size size
-                   :color color}
-                  (contains? parents p)
-                  (assoc :parent true))))))))
+               (cond->
+                {:id (str (:db/id p))
+                 :label (title/block-unique-title p)
+                 :size size
+                 :color color
+                 :block/created-at (:block/created-at p)}
+                 (contains? parents (:db/id p))
+                 (assoc :parent true))))))))
 
                   ;; slow
 (defn- uuid-or-asset?
-  [id]
-  (or (util/uuid-string? id)
-      (string/starts-with? id "../assets/")
-      (= id "..")
-      (string/starts-with? id "assets/")
-      (string/ends-with? id ".gif")
-      (string/ends-with? id ".jpg")
-      (string/ends-with? id ".png")))
+  [label]
+  (or (util/uuid-string? label)
+      (string/starts-with? label "../assets/")
+      (= label "..")
+      (string/starts-with? label "assets/")
+      (string/ends-with? label ".gif")
+      (string/ends-with? label ".jpg")
+      (string/ends-with? label ".png")))
 
 (defn- remove-uuids-and-files!
   [nodes]
   (remove
-   (fn [node] (uuid-or-asset? (:id node)))
+   (fn [node] (uuid-or-asset? (:label node)))
    nodes))
 
 (defn- normalize-page-name
-  [{:keys [nodes links page-name->title]}]
-  (let [links (->>
-               (map
-                 (fn [{:keys [source target]}]
-                   (let [source (get page-name->title source)
-                         target (get page-name->title target)]
-                     (when (and source target)
-                       {:source source :target target})))
-                 links)
-               (remove nil?))
-        nodes (->> (remove-uuids-and-files! nodes)
+  [{:keys [nodes links]}]
+  (let [nodes' (->> (remove-uuids-and-files! nodes)
                    (util/distinct-by (fn [node] (:id node)))
-                   (map (fn [node]
-                          (if-let [title (get page-name->title (:id node))]
-                            (assoc node :id title :label title)
-                            nil)))
                    (remove nil?))]
-    {:nodes nodes
+    {:nodes nodes'
      :links links}))
 
 (defn build-global-graph
@@ -95,13 +83,11 @@
         current-page (or (:block/name (db/get-current-page)) "")]
     (when-let [repo (state/get-current-repo)]
       (let [relation (db/get-pages-relation repo journal?)
-            tagged-pages (map (fn [[x y]] [x (common-util/page-name-sanity-lc y)]) (db-model/get-all-tagged-pages repo))
-            namespaces (map (fn [[x y]] [x (common-util/page-name-sanity-lc y)]) (db/get-all-namespace-relation repo))
+            tagged-pages (db-model/get-all-tagged-pages repo)
+            namespaces (db/get-all-namespace-relation repo)
             tags (set (map second tagged-pages))
             full-pages (db/get-all-pages repo)
-            full-pages-map (into {} (map (juxt :block/name identity) full-pages))
-            all-pages (map title/block-unique-title full-pages)
-            page-name->title (zipmap (map :block/name full-pages) all-pages)
+            db-based? (config/db-based-graph? repo)
             created-ats (map :block/created-at full-pages)
 
             ;; build up nodes
@@ -113,26 +99,23 @@
               (remove ldb/journal?)
               (not excluded-pages?)
               (remove (fn [p] (true? (pu/get-block-property-value p :logseq.property/exclude-from-graph-view)))))
-            links (concat (seq relation)
-                          (seq tagged-pages)
-                          (seq namespaces))
-            linked (set (flatten links))
-            build-in-pages (->> (if (config/db-based-graph? repo) sqlite-create-graph/built-in-pages-names gp-db/built-in-pages-names)
+            links (concat relation tagged-pages namespaces)
+            linked (set (mapcat identity links))
+            build-in-pages (->> (if db-based? sqlite-create-graph/built-in-pages-names gp-db/built-in-pages-names)
                                 (map string/lower-case)
                                 set)
-            nodes (cond->> (map :block/name full-pages')
+            nodes (cond->> full-pages'
                     (not builtin-pages?)
-                    (remove (fn [p] (contains? build-in-pages (string/lower-case p))))
+                    (remove #(contains? build-in-pages (:block/name %)))
                     (not orphan-pages?)
-                    (filter #(contains? linked (string/lower-case %))))
-
+                    (filter #(contains? linked (:db/id %))))
+            links (map (fn [[x y]] [(str x) (str y)]) links)
             page-links (reduce (fn [m [k v]] (-> (update m k inc)
                                                  (update v inc))) {} links)
-            links (build-links (remove (fn [[_ to]] (nil? to)) links))
+            links (build-links links)
             nodes (build-nodes dark? (string/lower-case current-page) page-links tags nodes namespaces)]
-        (-> {:nodes (map #(assoc % :block/created-at (get-in full-pages-map [(:id %) :block/created-at])) nodes)
-             :links links
-             :page-name->title page-name->title}
+        (-> {:nodes nodes
+             :links links}
             normalize-page-name
             (assoc :all-pages
                    {:created-at-min (apply min created-ats)
@@ -144,95 +127,79 @@
     (when-let [repo (state/get-current-repo)]
       (let [page-entity (db/get-page page)
             page-id (:db/id page-entity)
-            tags (if (config/db-based-graph? repo)
-                   (set (map #(:block/name (db/entity repo (:db/id %)))
-                             (:block/tags page-entity)))
-                   (:tags (:block/properties page-entity)))
-            tags (remove #(= page %) tags)
+            tags (when (config/db-based-graph? repo)
+                   (set (map :db/id (:block/tags page-entity))))
+            tags (set (remove #(= page-id %) tags))
             ref-pages (db/get-page-referenced-pages repo page-id)
             mentioned-pages (db/get-pages-that-mentioned-page repo page-id show-journal)
-            namespaces (map (fn [[x y]] [x (common-util/page-name-sanity-lc y)]) (db/get-all-namespace-relation repo))
+            namespaces (db/get-all-namespace-relation repo)
             links (concat
                    namespaces
-                   (map (fn [[p _aliases]]
-                          [page p]) ref-pages)
-                   (map (fn [[p _aliases]]
-                          [p page]) mentioned-pages)
+                   (map (fn [ref-page]
+                          [page-id ref-page]) ref-pages)
+                   (map (fn [page]
+                          [page-id page]) mentioned-pages)
                    (map (fn [tag]
-                          [page tag])
+                          [page-id tag])
                         tags))
-            other-pages (->> (concat (map first ref-pages)
-                                     (map first mentioned-pages))
+            other-pages (->> (concat ref-pages mentioned-pages)
                              (remove nil?)
                              (set))
             other-pages-links (mapcat
-                               (fn [page]
-                                 (let [page-id (:db/id (db/get-page page))
-                                       ref-pages (-> (map first (db/get-page-referenced-pages repo page-id))
+                               (fn [page-id]
+                                 (let [ref-pages (-> (db/get-page-referenced-pages repo page-id)
                                                      (set)
                                                      (set/intersection other-pages))
-                                       mentioned-pages (-> (map first (db/get-pages-that-mentioned-page repo page-id show-journal))
+                                       mentioned-pages (-> (db/get-pages-that-mentioned-page repo page-id show-journal)
                                                            (set)
                                                            (set/intersection other-pages))]
                                    (concat
-                                    (map (fn [p] [page p]) ref-pages)
-                                    (map (fn [p] [p page]) mentioned-pages))))
+                                    (map (fn [p] [page-id p]) ref-pages)
+                                    (map (fn [p] [p page-id]) mentioned-pages))))
                                other-pages)
             links (->> (concat links other-pages-links)
                        (remove nil?)
                        (distinct)
                        (build-links))
             nodes (->> (concat
-                        [page]
-                        (map first ref-pages)
-                        (map first mentioned-pages)
+                        [page-id]
+                        ref-pages
+                        mentioned-pages
                         tags)
                        (remove nil?)
-                       (distinct))
-            nodes (build-nodes dark? page links (set tags) nodes namespaces)
-            full-pages (db/get-all-pages repo)
-            all-pages (map common-util/get-page-title full-pages)
-            page-name->title (zipmap (map :block/name full-pages) all-pages)]
+                       (map db/entity)
+                       (util/distinct-by :db/id))
+            nodes (build-nodes dark? page links tags nodes namespaces)]
         (normalize-page-name
          {:nodes nodes
-          :links links
-          :page-name->title page-name->title})))))
+          :links links})))))
 
 (defn build-block-graph
   "Builds a citation/reference graph for a given block uuid."
-  [block theme]
+  [block-uuid theme]
   (when-let [repo (state/get-current-repo)]
-    (let [dark? (= "dark" theme)
-          ref-blocks (db/get-block-referenced-blocks block)
-          namespaces (map (fn [[x y]] [x (common-util/page-name-sanity-lc y)]) (db/get-all-namespace-relation repo))
-          other-blocks (->> (concat (map first ref-blocks))
-                            (remove nil?)
-                            (set))
-          other-blocks-links (mapcat
-                              (fn [block]
-                                (let [ref-blocks (-> (map first (db/get-block-referenced-blocks block))
-                                                     (set)
-                                                     (set/intersection other-blocks))]
-                                  (concat
-                                   (map (fn [p] [block p]) ref-blocks))))
-                              other-blocks)
-          links (concat
-                 (->> other-blocks-links
-                      (remove nil?)
-                      (distinct)
-                      (build-links))
-                 namespaces)
-          nodes (->> (concat
-                      [block]
-                      (map first ref-blocks))
-                     (remove nil?)
-                     (distinct)
+    (when-let [block (and (uuid? block-uuid) (db/entity [:block/uuid block-uuid]))]
+      (let [dark? (= "dark" theme)
+            ref-blocks (->> (concat (:block/_refs block) (:block/refs block))
+                            (map (fn [b]
+                                   (if (ldb/page? b) b (:block/page b))))
+                            (remove (fn [node] (= (:db/id block) (:db/id node))))
+                            (util/distinct-by :db/id))
+            namespaces (db/get-all-namespace-relation repo)
+            links (->> (concat
+                        namespaces
+                        (map (fn [p] [(:db/id block) (:db/id p)]) ref-blocks))
+                       (remove nil?)
+                       (distinct)
+                       (build-links))
+            nodes (->> (cons block ref-blocks)
+                       distinct
                        ;; FIXME: get block tags
-                     )
-          nodes (build-nodes dark? block links #{} nodes namespaces)]
-      (normalize-page-name
-       {:nodes nodes
-        :links links}))))
+                       )
+            nodes (build-nodes dark? block links #{} nodes namespaces)]
+        (normalize-page-name
+         {:nodes nodes
+          :links links})))))
 
 (defn n-hops
   "Get all nodes that are n hops from nodes (a collection of node ids)"

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

@@ -353,6 +353,13 @@
                             chosen-result)
             chosen (:block/title chosen-result)
             chosen' (string/replace-first chosen (str (t :new-page) " ") "")
+            [chosen' chosen-result] (or (when (and (:nlp-date? chosen-result) (not (de/entity? chosen-result)))
+                                          (when-let [result (date/nld-parse chosen')]
+                                            (let [d (doto (goog.date.DateTime.) (.setTime (.getTime result)))
+                                                  gd (goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))
+                                                  page (date/js-date->journal-title gd)]
+                                              [page (db/get-page page)])))
+                                        [chosen' chosen-result])
             ref-text (if (and (de/entity? chosen-result) (not (ldb/page? chosen-result)))
                        (cond
                          db-based?

+ 29 - 23
src/main/frontend/handler/route.cljs

@@ -15,7 +15,9 @@
             [reitit.frontend.easy :as rfe]
             [frontend.context.i18n :refer [t]]
             [clojure.string :as string]
-            [logseq.common.util :as common-util]))
+            [logseq.common.util :as common-util]
+            [frontend.handler.notification :as notification]
+            [logseq.db :as ldb]))
 
 (defn redirect!
   "If `push` is truthy, previous page will be left in history."
@@ -80,29 +82,33 @@
              (and (string? page-name) (not (string/blank? page-name))))
      (let [page (db/get-page page-name)
            whiteboard? (db/whiteboard-page? page)]
-       (if-let [source (db/get-alias-source-page (state/get-current-repo) (:db/id page))]
-         (redirect-to-page! (:block/uuid source) opts)
-         (do
+       (if (and (not config/dev?)
+                (or (ldb/hidden? page)
+                    (and (ldb/built-in? page) (ldb/private-built-in-page? page))))
+         (notification/show! "Cannot go to an internal page." :warning)
+         (if-let [source (db/get-alias-source-page (state/get-current-repo) (:db/id page))]
+           (redirect-to-page! (:block/uuid source) opts)
+           (do
            ;; Always skip onboarding when loading an existing whiteboard
-           (when-not new-whiteboard? (state/set-onboarding-whiteboard! true))
-           (when-let [db-id (:db/id page)]
-             (recent-handler/add-page-to-recent! db-id click-from-recent?))
-           (if (and whiteboard?  (= (str page-name) (state/get-current-page)) block-id)
-             (state/focus-whiteboard-shape block-id)
-             (let [m (cond->
-                      (default-page-route (str page-name))
-
-                       block-id
-                       (assoc :query-params (if whiteboard?
-                                              {:block-id block-id}
-                                              {:anchor (str "ls-block-" block-id)}))
-
-                       anchor
-                       (assoc :query-params {:anchor anchor})
-
-                       (boolean? push)
-                       (assoc :push push))]
-               (redirect! m)))))))))
+             (when-not new-whiteboard? (state/set-onboarding-whiteboard! true))
+             (when-let [db-id (:db/id page)]
+               (recent-handler/add-page-to-recent! db-id click-from-recent?))
+             (if (and whiteboard?  (= (str page-name) (state/get-current-page)) block-id)
+               (state/focus-whiteboard-shape block-id)
+               (let [m (cond->
+                        (default-page-route (str page-name))
+
+                         block-id
+                         (assoc :query-params (if whiteboard?
+                                                {:block-id block-id}
+                                                {:anchor (str "ls-block-" block-id)}))
+
+                         anchor
+                         (assoc :query-params {:anchor anchor})
+
+                         (boolean? push)
+                         (assoc :push push))]
+                 (redirect! m))))))))))
 
 (defn get-title
   [name path-params]

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

@@ -24,7 +24,8 @@
             [logseq.shui.ui :as shui]
             [clojure.set :as set]
             [frontend.db.conn-state :as db-conn-state]
-            [datascript.core :as d]))
+            [datascript.core :as d]
+            [logseq.db :as ldb]))
 
 (defonce *profile-state
   (atom {}))
@@ -1281,19 +1282,6 @@ Similar to re-frame subscriptions"
   []
   (swap! state assoc :ui/sidebar-open? false))
 
-(defn sidebar-add-block!
-  [repo db-id block-type]
-  (when (not (util/sm-breakpoint?))
-    (when db-id
-      (update-state! :sidebar/blocks (fn [blocks]
-                                       (->> (remove #(= (second %) db-id) blocks)
-                                            (cons [repo db-id block-type])
-                                            (distinct))))
-      (set-state! [:ui/sidebar-collapsed-blocks db-id] false)
-      (open-right-sidebar!)
-      (when-let [elem (gdom/getElementByClass "sidebar-item-list")]
-        (util/scroll-to elem 0)))))
-
 (defn sidebar-move-block!
   [from to]
   (update-state! :sidebar/blocks (fn [blocks]
@@ -1942,6 +1930,28 @@ Similar to re-frame subscriptions"
     (async/put! chan [payload d])
     d))
 
+(defn sidebar-add-block!
+  [repo db-id block-type]
+  (when (not (util/sm-breakpoint?))
+    (let [page (and (sqlite-util/db-based-graph? repo)
+                    (= :page block-type)
+                    (some-> (db-conn-state/get-conn repo) deref (d/entity db-id)))]
+      (if (and page
+               ;; TODO: Use config/dev? when it's not a circular dep
+               (not goog.DEBUG)
+               (or (ldb/hidden? page)
+                   (and (ldb/built-in? page) (ldb/private-built-in-page? page))))
+        (pub-event! [:notification/show {:content "Cannot open an internal page." :status :warning}])
+        (when db-id
+          (update-state! :sidebar/blocks (fn [blocks]
+                                           (->> (remove #(= (second %) db-id) blocks)
+                                                (cons [repo db-id block-type])
+                                                (distinct))))
+          (set-state! [:ui/sidebar-collapsed-blocks db-id] false)
+          (open-right-sidebar!)
+          (when-let [elem (gdom/getElementByClass "sidebar-item-list")]
+            (util/scroll-to elem 0)))))))
+
 (defn get-export-block-text-indent-style []
   (:copy/export-block-text-indent-style @state))
 

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

@@ -521,35 +521,34 @@
        [:div#ui__ac-inner.hide-scrollbar
         (when header header)
         (for [[idx item] (medley/indexed matched)]
-          [:<>
-           {:key idx}
-           (let [item-cp
-                 [:div.menu-link-wrap
-                  {:key            idx
+          (let [react-key (str idx)
+                item-cp
+                [:div.menu-link-wrap
+                 {:key react-key
                    ;; mouse-move event to indicate that cursor moved by user
-                   :on-mouse-move  #(reset! *current-idx idx)}
-                  (let [chosen? (= @*current-idx idx)]
-                    (menu-link
-                     {:id (str "ac-" idx)
-                      :tab-index "0"
-                      :class (when chosen? "chosen")
+                  :on-mouse-move  #(reset! *current-idx idx)}
+                 (let [chosen? (= @*current-idx idx)]
+                   (menu-link
+                    {:id (str "ac-" react-key)
+                     :tab-index "0"
+                     :class (when chosen? "chosen")
                        ;; TODO: should have more tests on touch devices
                        ;:on-pointer-down #(util/stop %)
-                      :on-click (fn [e]
-                                  (util/stop e)
-                                  (if (and (gobj/get e "shiftKey") on-shift-chosen)
-                                    (on-shift-chosen item)
-                                    (on-chosen item e)))}
-                     (if item-render (item-render item chosen?) item)))]]
-
-             (let [group-name (and (fn? get-group-name) (get-group-name item))]
-               (if (and group-name (not (contains? @*groups group-name)))
-                 (do
-                   (swap! *groups conj group-name)
-                   [:div
-                    [:div.ui__ac-group-name group-name]
-                    item-cp])
-                 item-cp)))])]
+                     :on-click (fn [e]
+                                 (util/stop e)
+                                 (if (and (gobj/get e "shiftKey") on-shift-chosen)
+                                   (on-shift-chosen item)
+                                   (on-chosen item e)))}
+                    (if item-render (item-render item chosen?) item)))]]
+
+            (let [group-name (and (fn? get-group-name) (get-group-name item))]
+              (if (and group-name (not (contains? @*groups group-name)))
+                (do
+                  (swap! *groups conj group-name)
+                  [:div
+                   [:div.ui__ac-group-name group-name]
+                   item-cp])
+                item-cp))))]
        (when empty-placeholder
          empty-placeholder))]))
 
@@ -1202,6 +1201,7 @@
      :style {:width "initial"
              :tab-index -1}
      :auto-complete (if (util/chrome?) "chrome-off" "off")
+     :on-mouse-down util/stop-propagation
      :on-key-down (fn [e]
                     (when (= "Enter" (util/ekey e))
                       (let [value (util/evalue e)]

+ 30 - 18
src/main/frontend/util.cljc

@@ -876,51 +876,63 @@
            (gdom/getElement section "id"))))))
 
 #?(:cljs
-   (defn get-elem-idx
-     [nodes node]
-     (let [equal? (fn [^js a ^js b]
-                    (or (some-> b (= a))
-                        (and a b (= (.-id a) (.-id b)))))]
-       (first (filter number? (map-indexed (fn [idx b] (when (equal? b node) idx)) nodes))))))
+   (defn- skip-same-top-blocks
+     [blocks block]
+     (let [property? (= (d/attr block "data-is-property") "true")
+           properties-area (rec-get-node block "ls-properties-area")]
+       (remove (fn [b]
+                 (and
+                  (not= b block)
+                  (or (= (when b (.-top (.getBoundingClientRect b)))
+                         (when block (.-top (.getBoundingClientRect block))))
+                      (when property?
+                        (and (not= (d/attr b "data-is-property") "true")
+                             (gdom/contains properties-area b)))))) blocks))))
 
 #?(:cljs
    (defn get-prev-block-non-collapsed
      "Gets previous non-collapsed block. If given a container
       looks up blocks in that container e.g. for embed"
      ([block] (get-prev-block-non-collapsed block {}))
-     ([block {:keys [container]}]
+     ([block {:keys [container up-down?]}]
       (when-let [blocks (if container
                           (get-blocks-noncollapse container)
                           (get-blocks-noncollapse))]
-        (when-let [index (get-elem-idx blocks block)]
-          (let [idx (dec index)]
-            (when (>= idx 0)
-              (nth-safe blocks idx))))))))
+        (let [blocks (if up-down?
+                       (skip-same-top-blocks blocks block)
+                       blocks)]
+          (when-let [index (.indexOf blocks block)]
+            (let [idx (dec index)]
+              (when (>= idx 0)
+                (nth-safe blocks idx)))))))))
 
 #?(:cljs
    (defn get-prev-block-non-collapsed-non-embed
      [block]
      (when-let [blocks (->> (get-blocks-noncollapse)
                             remove-embedded-blocks)]
-       (when-let [index (get-elem-idx blocks block)]
+       (when-let [index (.indexOf blocks block)]
            (let [idx (dec index)]
              (when (>= idx 0)
                (nth-safe blocks idx)))))))
 
 #?(:cljs
    (defn get-next-block-non-collapsed
-     [block]
+     [block {:keys [up-down?]}]
      (when-let [blocks (and block (get-blocks-noncollapse))]
-       (when-let [index (get-elem-idx blocks block)]
-         (let [idx (inc index)]
-           (when (>= (count blocks) idx)
-             (nth-safe blocks idx)))))))
+       (let [blocks (if up-down?
+                      (skip-same-top-blocks blocks block)
+                      blocks)]
+         (when-let [index (.indexOf blocks block)]
+           (let [idx (inc index)]
+             (when (>= (count blocks) idx)
+               (nth-safe blocks idx))))))))
 
 #?(:cljs
    (defn get-next-block-non-collapsed-skip
      [block]
      (when-let [blocks (get-blocks-noncollapse)]
-       (when-let [index (get-elem-idx blocks block)]
+       (when-let [index (.indexOf blocks block)]
          (loop [idx (inc index)]
            (when (>= (count blocks) idx)
              (let [block (nth-safe blocks idx)

+ 9 - 3
src/main/frontend/util/cursor.cljs

@@ -125,9 +125,15 @@
   [input]
   (move-cursor-to input (line-end-pos input)))
 
-;; (defn move-cursor-to-line-beginning
-;;   [input]
-;;   (move-cursor-to input (line-beginning-pos input)))
+(comment
+  (defn move-cursor-to-line-beginning
+    [input]
+    (move-cursor-to input (line-beginning-pos input))))
+
+(comment
+  (defn move-cursor-to-start
+    [input]
+    (move-cursor-to input 0)))
 
 (defn move-cursor-to-end
   [input]

+ 1 - 1
src/main/frontend/worker/db_worker.cljs

@@ -627,7 +627,7 @@
                {:keys [type payload]} (when (map? data) data)]
            (case type
              :notification
-             (worker-util/post-message type [[:div [:p (:message payload)]] (:type payload)])
+             (worker-util/post-message type [(:message payload) (:type payload)])
              (throw e)))))))
 
   (file-writes-finished?

+ 6 - 1
src/main/frontend/worker/handler/page.cljs

@@ -88,8 +88,13 @@
                                  [[:db.fn/retractEntity [:file/path file-path]]])
                 delete-page-tx (concat (db-refs->page repo page)
                                        [[:db.fn/retractEntity (:db/id page)]])
-
+                restore-class-parent-tx (when db-based?
+                                          (->> (filter (fn [p] (ldb/class? p)) (:logseq.property/_parent page))
+                                               (map (fn [p]
+                                                      {:db/id (:db/id p)
+                                                       :logseq.property/parent :logseq.class/Root}))))
                 tx-data (concat truncate-blocks-tx-data
+                                restore-class-parent-tx
                                 delete-page-tx
                                 delete-file-tx)]
 

+ 2 - 2
src/main/frontend/worker/search.cljs

@@ -300,9 +300,9 @@ DROP TRIGGER IF EXISTS blocks_au;
                                             true
                                             (if built-in?
                                               (or (not (ldb/built-in? block))
-                                                  (ldb/class? block)
+                                                  ;; (ldb/class? block)
                                                   (ldb/public-built-in-property? block))
-                                              (not (ldb/built-in? block))))
+                                              (not (ldb/private-built-in-page? block))))
                                       {:db/id (:db/id block)
                                        :block/uuid block-id
                                        :block/title (or snippet title)

+ 11 - 7
src/main/logseq/api.cljs

@@ -863,7 +863,7 @@
         property-name' (convert?to-built-in-property-name property-name')]
     (if (qualified-keyword? property-name')
       property-name'
-      (db-property/create-user-property-ident-from-name property-name))))
+      (db-property/create-user-property-ident-from-name property-name "plugin.property"))))
 
 ;; properties (db only)
 (defn ^:export get_property
@@ -883,7 +883,9 @@
   "
   [k ^js schema ^js opts]
   (when-let [k' (and (string? k) (keyword k))]
-    (p/let [k (if (qualified-keyword? k') k'
+    (p/let [opts (or (some-> opts (bean/->clj)) {})
+            name (or (:name opts) (some-> (str k) (string/trim)))
+            k (if (qualified-keyword? k') k'
                 (get-db-ident-for-property-name k))
             schema (or (some-> schema (bean/->clj)
                          (update-keys #(if (contains? #{:hide :public} %)
@@ -893,21 +895,23 @@
                      (update :cardinality keyword)
                      (string? (:type schema))
                      (update :type keyword))
-            opts (or (and opts (bean/->clj opts)) {})
-            p (db-property-handler/upsert-property! k schema opts)]
+            p (db-property-handler/upsert-property! k schema
+                (cond-> opts
+                  name
+                  (assoc :property-name name)))]
       (bean/->js (sdk-utils/normalize-keyword-for-json p)))))
 
 ;; block properties
 (def ^:export upsert_block_property
-  (fn [block-uuid key value]
+  (fn [block-uuid keyname value]
     (p/let [block-uuid (sdk-utils/uuid-or-throw-error block-uuid)
             repo (state/get-current-repo)
             _ (db-async/<get-block repo block-uuid :children? false)
             db? (config/db-based-graph? repo)
-            key (-> (if (keyword? key) (name key) key) (util/safe-lower-case))
+            key (-> (if (keyword? key) (name keyname) keyname) (util/safe-lower-case))
             key (if db? (get-db-ident-for-property-name key) key)
             _ (when (and db? (not (db-utils/entity key)))
-                (db-property-handler/upsert-property! key {} {}))]
+                (db-property-handler/upsert-property! key {} {:property-name keyname}))]
       (property-handler/set-block-property! repo block-uuid key value))))
 
 (def ^:export remove_block_property

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

@@ -147,7 +147,6 @@
  :page/make-private "Make it private"
  :page/delete "Delete page"
  :page/add-to-favorites "Add to Favorites"
- :page/go-to-page "Go to this page"
  :page/unfavorite "Unfavorite page"
  :block/name "Page name"
  :page/copy-page-url "Copy page URL"

File diff suppressed because it is too large
+ 0 - 0
src/rtc_e2e_test/example.cljs


+ 1 - 1
src/test/frontend/db/db_based_model_test.cljs

@@ -24,7 +24,7 @@
   (let [opts {:redirect? false :create-first-block? false :class? true}
         _ (test-helper/create-page! "class1" opts)
         _ (test-helper/create-page! "class2" opts)]
-    (is (= ["Card" "Journal" "Query" "Root tag" "Task" "class1" "class2"] (sort (map :block/title (model/get-all-classes repo)))))))
+    (is (= ["Card" "Journal" "Query" "Root Tag" "Task" "class1" "class2"] (sort (map :block/title (model/get-all-classes repo)))))))
 
 (deftest ^:fix-me get-class-objects-test
   (let [opts {:redirect? false :create-first-block? false :class? true}

+ 8 - 28
src/test/frontend/db/model_test.cljs

@@ -73,30 +73,10 @@
       2 (count a-ref-blocks)
       #{"ab" "ac"} (set alias-names))))
 
-(deftest remove-links-for-each-level-of-the-namespaces
-  (load-test-files [{:file/path "pages/generic page.md"
-                     :file/content "tags:: [[one/two/tree]], one/two
-- link to ns [[one]]
-- link to page one [[page ONE]]"}])
-
-  (is (= '("one/two/tree" "tags" "page one")
-         (map second (model/get-pages-relation test-helper/test-db true)))
-      "(get-pages-relation) Must be only ns one/two/tree")
-
-  (is (= '("one/two/tree" "page one")
-         (map second (#'model/remove-nested-namespaces-link [["generic page" "one/two/tree"]
-                                                           ["generic page" "one/two"]
-                                                           ["generic page" "one"]
-                                                           ["generic page" "page one"]])))
-      "(model/remove-nested-namespaces-link) Must be only ns one/two/tree")
-
-  (is (= '("one/two/tree" "one/two" "one")
-         (#'model/get-parents-namespace-list "one/two/tree/four"))
-      "Must be one/two/tree one/two one")
-
-  (is (= '("one/two" "one")
-         (#'model/get-unnecessary-namespaces-name '("one/two/tree" "one" "one/two" "non nested tag" "non nested link")))
-      "Must be  one/two one"))
+(defn- page-mention-check?
+  [page-name include-journals?]
+  (->> (model/get-pages-that-mentioned-page test-helper/test-db (:db/id (db/entity [:block/name page-name])) include-journals?)
+       (map (fn [id] (:block/name (db/entity id))))))
 
 (deftest get-pages-that-mentioned-page-with-show-journal
   (load-test-files [{:file/path "journals/2020_08_15.md"
@@ -112,19 +92,19 @@
                      :file/content "- link to page one [[page ONE]]"}])
 
   (is (= '("sep 18th, 2020" "aug 15th, 2020" "generic page")
-         (map first (model/get-pages-that-mentioned-page test-helper/test-db (:db/id (db/entity [:block/name "page one"])) true)))
+         (page-mention-check? "page one" true))
       "Must be 'generic page' + 2 journals")
 
   (is (= '("generic page")
-         (map first (model/get-pages-that-mentioned-page test-helper/test-db (:db/id (db/entity [:block/name "page one"])) false)))
+         (page-mention-check? "page one" false))
       "Must be only 'generic page'")
 
   (is (= '("aug 15th, 2020")
-         (map first (model/get-pages-that-mentioned-page test-helper/test-db (:db/id (db/entity [:block/name "generic page"])) true)))
+         (page-mention-check? "generic page" true))
       "Must show only 'aug 15th, 2020'")
 
   (is (= '()
-         (map first (model/get-pages-that-mentioned-page test-helper/test-db (:db/id (db/entity [:block/name "generic page"])) false)))
+         (page-mention-check? "generic page" false))
       "Must be empty"))
 
 (deftest entity-query-should-return-nil-if-id-not-exists

+ 15 - 7
src/test/frontend/db/query_dsl_test.cljs

@@ -150,22 +150,30 @@ prop-d:: [[nada]]"}])
     (test-helper/with-config {}
       (block-property-queries-test))))
 
-
 (when js/process.env.DB_GRAPH
  (deftest db-only-block-property-queries
    (load-test-files-for-db-graph
-    [{:page {:block/title "page1"}
-      :blocks [{:block/title "b1"
-                :build/properties {:Foo "bar"}}
-               {:block/title "b2"
-                :build/properties {:foo "bar"}}]}])
+    {:properties
+       {:zzz {:block/schema {:type :default}
+              :block/title "zzz name!"}}
+       :pages-and-blocks
+       [{:page {:block/title "page1"}
+         :blocks [{:block/title "b1"
+                   :build/properties {:Foo "bar"}}
+                  {:block/title "b2"
+                   :build/properties {:foo "bar"}}
+                  {:block/title "b3"
+                   :build/properties {:zzz "bar"}}]}]})
 
    (is (= ["b1"]
           (map :block/title (dsl-query "(property Foo)")))
        "filter is case sensitive")
    (is (= ["b2"]
           (map :block/title (dsl-query "(property :user.property/foo)")))
-       "filter can handle qualified keyword properties")))
+       "filter can handle qualified keyword properties")
+   (is (= ["b3"]
+          (map :block/title (dsl-query "(property \"zzz name!\")")))
+       "filter can handle property name")))
 
 (deftest block-property-query-performance
   (let [pages (->> (repeat 10 {:tags ["tag1" "tag2"]})

+ 6 - 3
src/test/frontend/test/helper.cljs

@@ -153,9 +153,12 @@
 (defn load-test-files-for-db-graph
   [options*]
   (let [;; Builds options from markdown :file/content unless given explicit build-blocks config
-        options (if (:page (first options*))
-                  {:pages-and-blocks options* :auto-create-ontology? true}
-                  (build-blocks-tx-options options*))
+        options (cond (:page (first options*))
+                      {:pages-and-blocks options* :auto-create-ontology? true}
+                      (:pages-and-blocks options*)
+                      (assoc options* :auto-create-ontology? true)
+                      :else
+                      (build-blocks-tx-options options*))
         {:keys [init-tx block-props-tx]} (sqlite-build/build-blocks-tx options)]
     (db/transact! test-db init-tx)
     (when (seq block-props-tx)

+ 1 - 4
tailwind.all.css

@@ -1,13 +1,10 @@
 @charset "utf-8";
-@import "tailwindcss/base";
-@import "tailwindcss/components";
-@import "tailwindcss/utilities";
 
 @import "packages/ui/src/radix.css";
 @import "packages/ui/src/radix-hsl.css";
 @import "packages/ui/src/vars-classic.css";
 @import "packages/ui/src/colors.css";
-@import "packages/ui/src/index.css";
+@import "packages/ui/src/index.css";  /* Tailwind base imported here */
 @import "resources/css/shui.css";
 @import "inter-ui/inter.css";
 @import "reveal.js/dist/reveal.css";

+ 70 - 43
yarn.lock

@@ -2002,13 +2002,6 @@ [email protected], classnames@^2.2.5:
   resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
   integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
 
[email protected]:
-  version "4.2.3"
-  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
-  integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
-  dependencies:
-    source-map "~0.6.0"
-
 clean-stack@^2.0.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@@ -3267,6 +3260,13 @@ fancy-log@^1.3.2:
     parse-node-version "^1.0.0"
     time-stamp "^1.0.0"
 
+fancy-log@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-2.0.0.tgz#cad207b8396d69ae4796d74d17dff5f68b2f7343"
+  integrity sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==
+  dependencies:
+    color-support "^1.1.3"
+
 fast-deep-equal@^3.1.1:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -3907,16 +3907,6 @@ [email protected]:
     events "^3.3.0"
     obliterator "^1.6.1"
 
-gulp-clean-css@^4.3.0:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/gulp-clean-css/-/gulp-clean-css-4.3.0.tgz#5b1e73f2fca46703eb636014cdd4553cea65146d"
-  integrity sha512-mGyeT3qqFXTy61j0zOIciS4MkYziF2U594t2Vs9rUnpkEHqfu6aDITMp8xOvZcvdX61Uz3y1mVERRYmjzQF5fg==
-  dependencies:
-    clean-css "4.2.3"
-    plugin-error "1.0.1"
-    through2 "3.0.1"
-    vinyl-sourcemaps-apply "0.2.1"
-
 gulp-cli@^2.2.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f"
@@ -3941,6 +3931,16 @@ gulp-cli@^2.2.0:
     v8flags "^3.2.0"
     yargs "^7.1.0"
 
+gulp-postcss@^10.0.0:
+  version "10.0.0"
+  resolved "https://registry.yarnpkg.com/gulp-postcss/-/gulp-postcss-10.0.0.tgz#a88d7c6602f8a8c94aaa9f28ac3a68def00c7ada"
+  integrity sha512-z1RF2RJEX/BvFsKN11PXai8lRmihZTiHnlJf7Zu8uHaA/Q7Om4IeN8z1NtMAW5OiLwUY02H0DIFl9tHl0CNSgA==
+  dependencies:
+    fancy-log "^2.0.0"
+    plugin-error "^2.0.1"
+    postcss-load-config "^5.0.0"
+    vinyl-sourcemaps-apply "^0.2.1"
+
 gulp@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa"
@@ -4951,6 +4951,11 @@ lilconfig@^2.0.3, lilconfig@^2.0.5, lilconfig@^2.1.0:
   resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
   integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
 
+lilconfig@^3.1.1:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb"
+  integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==
+
 lines-and-columns@^1.1.6:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
@@ -6124,6 +6129,11 @@ picocolors@^1.0.0:
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
   integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
 
+picocolors@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
+  integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
+
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -6271,15 +6281,12 @@ plist@^3.0.5, plist@^3.0.6:
     base64-js "^1.5.1"
     xmlbuilder "^15.1.1"
 
-plugin-error@1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c"
-  integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==
+plugin-error@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-2.0.1.tgz#f2ac92bac8c85e3e23492d76d0c3ca12f30eb00b"
+  integrity sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==
   dependencies:
     ansi-colors "^1.0.1"
-    arr-diff "^4.0.0"
-    arr-union "^3.1.0"
-    extend-shallow "^3.0.2"
 
 popper.js@^1.11.1:
   version "1.16.1"
@@ -6418,6 +6425,14 @@ postcss-load-config@^4.0.0, postcss-load-config@^4.0.1:
     lilconfig "^2.0.5"
     yaml "^2.1.1"
 
+postcss-load-config@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-5.1.0.tgz#4ded23410da973e05edae9d41fa99bb5c1d5477f"
+  integrity sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==
+  dependencies:
+    lilconfig "^3.1.1"
+    yaml "^2.4.2"
+
 postcss-media-query-parser@^0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
@@ -6649,15 +6664,6 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
 
[email protected], postcss@^8.2.1:
-  version "8.4.17"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.17.tgz#f87863ec7cd353f81f7ab2dec5d67d861bbb1be5"
-  integrity sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==
-  dependencies:
-    nanoid "^3.3.4"
-    picocolors "^1.0.0"
-    source-map-js "^1.0.2"
-
 postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0.6:
   version "7.0.39"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
@@ -6666,6 +6672,15 @@ postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.
     picocolors "^0.2.1"
     source-map "^0.6.1"
 
+postcss@^8.2.1:
+  version "8.4.17"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.17.tgz#f87863ec7cd353f81f7ab2dec5d67d861bbb1be5"
+  integrity sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==
+  dependencies:
+    nanoid "^3.3.4"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
 postcss@^8.4.23:
   version "8.4.33"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
@@ -6675,6 +6690,15 @@ postcss@^8.4.23:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
+postcss@^8.4.47:
+  version "8.4.47"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
+  integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
+  dependencies:
+    nanoid "^3.3.7"
+    picocolors "^1.1.0"
+    source-map-js "^1.2.1"
+
 [email protected]:
   version "1.10.2"
   resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.10.2.tgz#74d6c84f9675b65dfd4ff6f4051ed8d3cb974076"
@@ -7012,7 +7036,7 @@ read-pkg@^5.2.0:
     parse-json "^5.0.0"
     type-fest "^0.6.0"
 
-"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.6.0, readable-stream@^3.6.2:
+readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.6.0, readable-stream@^3.6.2:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
   integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@@ -7671,6 +7695,11 @@ source-map-js@^1.0.2:
   resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
   integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
 
+source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
 source-map-resolve@^0.5.0:
   version "0.5.3"
   resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -7699,7 +7728,7 @@ source-map@^0.5.1, source-map@^0.5.6:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
 
-source-map@^0.6.1, source-map@~0.6.0:
+source-map@^0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@@ -8207,13 +8236,6 @@ through2-filter@^3.0.0:
     through2 "~2.0.0"
     xtend "~4.0.0"
 
[email protected]:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a"
-  integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==
-  dependencies:
-    readable-stream "2 || 3"
-
 through2@^2.0.0, through2@^2.0.3, through2@~2.0.0:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
@@ -8774,7 +8796,7 @@ vinyl-sourcemap@^1.1.0:
     remove-bom-buffer "^3.0.0"
     vinyl "^2.0.0"
 
[email protected]:
+vinyl-sourcemaps-apply@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705"
   integrity sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==
@@ -8969,6 +8991,11 @@ yaml@^2.1.1:
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144"
   integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==
 
+yaml@^2.4.2:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.1.tgz#c9772aacf62cb7494a95b0c4f1fb065b563db130"
+  integrity sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==
+
 [email protected]:
   version "20.2.4"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"

Some files were not shown because too many files changed in this diff