浏览代码

Merge branch 'feat/db' into refactor/single-block-type

Tienson Qin 1 年之前
父节点
当前提交
4cb9cb63a6
共有 56 个文件被更改,包括 497 次插入514 次删除
  1. 3 7
      deps/db/src/logseq/db/frontend/malli_schema.cljs
  2. 9 5
      deps/db/src/logseq/db/frontend/property.cljs
  3. 3 6
      deps/db/src/logseq/db/frontend/property/build.cljs
  4. 2 4
      deps/db/src/logseq/db/frontend/rules.cljc
  5. 1 1
      deps/db/src/logseq/db/frontend/schema.cljs
  6. 3 3
      deps/db/src/logseq/db/sqlite/build.cljs
  7. 26 28
      deps/db/src/logseq/db/sqlite/common_db.cljs
  8. 1 1
      deps/db/src/logseq/db/sqlite/util.cljs
  9. 1 1
      deps/db/test/logseq/db/frontend/rules_test.cljs
  10. 1 1
      deps/graph-parser/package.json
  11. 3 1
      deps/graph-parser/src/logseq/graph_parser/mldoc.cljc
  12. 6 6
      deps/graph-parser/test/logseq/graph_parser/block_test.cljs
  13. 1 1
      deps/graph-parser/test/logseq/graph_parser/cli_test.cljs
  14. 4 4
      deps/graph-parser/yarn.lock
  15. 1 1
      deps/outliner/package.json
  16. 31 26
      deps/outliner/src/logseq/outliner/property.cljs
  17. 1 1
      deps/outliner/test/logseq/outliner/property_test.cljs
  18. 4 4
      deps/outliner/yarn.lock
  19. 1 1
      deps/publishing/package.json
  20. 4 4
      deps/publishing/yarn.lock
  21. 1 1
      package.json
  22. 1 1
      scripts/package.json
  23. 25 17
      scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs
  24. 3 1
      scripts/src/logseq/tasks/dev/db_and_file_graphs.clj
  25. 4 4
      scripts/yarn.lock
  26. 1 1
      src/main/frontend/common.css
  27. 1 1
      src/main/frontend/components/block.cljs
  28. 18 0
      src/main/frontend/components/block.css
  29. 4 4
      src/main/frontend/components/container.cljs
  30. 80 250
      src/main/frontend/components/editor.cljs
  31. 3 3
      src/main/frontend/components/editor.css
  32. 1 1
      src/main/frontend/components/file_based/block.cljs
  33. 91 12
      src/main/frontend/components/objects.cljs
  34. 7 3
      src/main/frontend/components/page.cljs
  35. 16 4
      src/main/frontend/components/property.cljs
  36. 1 1
      src/main/frontend/components/property/closed_value.cljs
  37. 16 8
      src/main/frontend/components/property/value.cljs
  38. 4 17
      src/main/frontend/components/repo.css
  39. 9 9
      src/main/frontend/components/views.cljs
  40. 9 0
      src/main/frontend/db/async.cljs
  41. 9 0
      src/main/frontend/db/model.cljs
  42. 1 1
      src/main/frontend/db/query_dsl.cljs
  43. 1 1
      src/main/frontend/handler/editor.cljs
  44. 0 14
      src/main/frontend/handler/events.cljs
  45. 4 2
      src/main/frontend/handler/page.cljs
  46. 1 1
      src/main/frontend/handler/paste.cljs
  47. 9 1
      src/main/frontend/handler/plugin.cljs
  48. 0 5
      src/main/frontend/search.cljs
  49. 22 1
      src/main/frontend/worker/db/migrate.cljs
  50. 7 6
      src/main/frontend/worker/rtc/client.cljs
  51. 1 1
      src/main/frontend/worker/rtc/client_op.cljs
  52. 2 2
      src/test/frontend/db/db_based_model_test.cljs
  53. 12 12
      src/test/frontend/db/query_dsl_test.cljs
  54. 1 1
      src/test/frontend/handler/paste_test.cljs
  55. 22 18
      src/test/logseq/api_test.cljs
  56. 4 4
      yarn.lock

+ 3 - 7
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -79,7 +79,7 @@
   validate-fn varies by property type"
   [db validate-fn [{:block/keys [schema] :as property} property-val] & {:keys [new-closed-value?]}]
   ;; For debugging
-  ;; (when (not= "logseq.property" (namespace (:db/ident property))) (prn :validate-val (dissoc property :property/closed-values) property-val))
+  ;; (when (not (string/starts-with? (namespace (:db/ident property)) "logseq.")) (prn :validate-val (dissoc property :property/closed-values) property-val))
   (let [validate-fn' (if (db-property-type/property-types-with-db (:type schema))
                        (fn [value]
                          (validate-fn db value {:new-closed-value? new-closed-value?}))
@@ -264,8 +264,7 @@
 
 (def property-common-schema-attrs
   "Property :schema attributes common to all properties"
-  [[:hide? {:optional true} :boolean]
-   [:description {:optional true} :string]])
+  [[:hide? {:optional true} :boolean]])
 
 (def internal-property
   (vec
@@ -371,10 +370,7 @@
      [:db/ident {:optional true} logseq-property-ident]
      [:block/title {:optional true} :string]
      [:property.value/content {:optional true} [:or :string :double]]
-     [:block/closed-value-property {:optional true} [:set :int]]
-     [:block/schema {:optional true}
-      [:map
-       [:description {:optional true} :string]]]]
+     [:block/closed-value-property {:optional true} [:set :int]] ]
     (remove #(#{:block/title} (first %)) block-attrs)
     page-or-block-attrs)))
 

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

@@ -150,23 +150,27 @@
                                               :hide? true
                                               :view-context :page
                                               :public? true}}
-   :logseq.property/table-sorting {:schema
+   :logseq.property/description {:schema
+                                 {:type :default
+                                  :public? true}}
+
+   :logseq.property.table/sorting {:schema
                                    {:type :coll
                                     :hide? true
                                     :public? false}}
 
-   :logseq.property/table-filters {:schema
+   :logseq.property.table/filters {:schema
                                    {:type :coll
                                     :hide? true
                                     :public? false}}
 
-   :logseq.property/table-hidden-columns {:schema
+   :logseq.property.table/hidden-columns {:schema
                                           {:type :keyword
                                            :cardinality :many
                                            :hide? true
                                            :public? false}}
 
-   :logseq.property/table-ordered-columns {:schema
+   :logseq.property.table/ordered-columns {:schema
                                            {:type :coll
                                             :hide? true
                                             :public? false}}
@@ -201,7 +205,7 @@
 
 (def logseq-property-namespaces
   #{"logseq.property" "logseq.property.tldraw" "logseq.property.pdf" "logseq.task"
-    "logseq.property.linked-references" "logseq.property.asset"})
+    "logseq.property.linked-references" "logseq.property.asset" "logseq.property.table"})
 
 (defn logseq-property?
   "Determines if keyword is a logseq property"

+ 3 - 6
deps/db/src/logseq/db/frontend/property/build.cljs

@@ -21,7 +21,7 @@
 
 (defn build-closed-value-block
   "Builds a closed value block to be transacted"
-  [block-uuid block-value property {:keys [db-ident icon description]}]
+  [block-uuid block-value property {:keys [db-ident icon]}]
   (assert block-uuid (uuid? block-uuid))
   (cond->
    (closed-value-new-block block-uuid block-value property)
@@ -31,9 +31,6 @@
     icon
     (assoc :logseq.property/icon icon)
 
-    description
-    (update :block/schema assoc :description description)
-
     true
     sqlite-util/block-with-timestamps))
 
@@ -46,13 +43,13 @@
                                                                                      :ref-type? true})
                            property-attributes)]
     (into [property-tx]
-          (map (fn [{:keys [db-ident value icon description uuid]}]
+          (map (fn [{:keys [db-ident value icon uuid]}]
                  (cond->
                   (build-closed-value-block
                    uuid
                    value
                    property
-                   {:db-ident db-ident :icon icon :description description})
+                   {:db-ident db-ident :icon icon})
                    true
                    (assoc :block/order (db-order/gen-key))))
                (:closed-values property)))))

+ 2 - 4
deps/db/src/logseq/db/frontend/rules.cljc

@@ -172,8 +172,7 @@
       [?p :block/name]
       [?p ?prop ?pv]
       (or [?pv :block/title ?val]
-          [?pv :property.value/content ?val]
-          [?pv :block/title ?val])
+          [?pv :property.value/content ?val])
       [?prop-e :db/ident ?prop]
       [?prop-e :block/type "property"]]
 
@@ -188,8 +187,7 @@
     '[(property ?b ?prop ?val)
       [?b ?prop ?pv]
       (or [?pv :block/title ?val]
-          [?pv :property.value/content ?val]
-          [?pv :block/title ?val])
+          [?pv :property.value/content ?val])
       [(missing? $ ?b :block/name)]
       [?prop-e :db/ident ?prop]
       [?prop-e :block/type "property"]]

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

@@ -2,7 +2,7 @@
   "Main datascript schemas for the Logseq app"
   (:require [clojure.set :as set]))
 
-(def version 9)
+(def version 10)
 ;; A page is a special block, a page can corresponds to multiple files with the same ":block/name".
 (def ^:large-vars/data-var schema
   {:db/ident        {:db/unique :db.unique/identity}

+ 3 - 3
deps/db/src/logseq/db/sqlite/build.cljs

@@ -484,7 +484,7 @@
         prop-value' (if (coll? prop-value) (first prop-value) prop-value)
         prop-type (if prop-value'
                     (if (page-prop-value? prop-value')
-                      :page
+                      :node
                       (db-property-type/infer-property-type-from-value prop-value'))
                     :default)]
     (cond-> {:block/schema {:type prop-type}}
@@ -585,8 +585,8 @@
    The :build/properties in :pages-and-blocks, :properties and :classes is a map of
    property name keywords to property values.  Multiple property values for a many
    cardinality property are defined as a set. The following property types are
-   supported: :default, :url, :checkbox, :number, :page and :date. :checkbox and
-   :number values are written as booleans and integers/floats. :page references
+   supported: :default, :url, :checkbox, :number, :node and :date. :checkbox and
+   :number values are written as booleans and integers/floats. :node references
    are written as vectors e.g. `[:page \"PAGE NAME\"]`"
   [options]
   (validate-options options)

+ 26 - 28
deps/db/src/logseq/db/sqlite/common_db.cljs

@@ -82,34 +82,32 @@
 (defn- property-with-values
   [db block]
   (when (entity-plus/db-based-graph? db)
-    (let [block (d/entity db (:db/id block))
-          block-properties (when (seq (:block/properties block))
-                             (mapcat
-                              (fn [[_property-id property-values]]
-                                (let [values (if (and (coll? property-values)
-                                                      (map? (first property-values)))
-                                               property-values
-                                               #{property-values})
-                                      value-ids (when (every? map? values)
-                                                  (->> (map :db/id values)
-                                                       (filter (fn [id] (or (int? id) (keyword? id))))))
-                                      value-blocks (->>
-                                                    (when (seq value-ids)
-                                                      (map
-                                                       (fn [id] (d/pull db '[*] id))
-                                                       value-ids))
-                                                    ;; FIXME: why d/pull returns {:db/id db-ident} instead of {:db/id number-eid}?
-                                                    (map (fn [block]
-                                                           (let [from-property-id (get-in block [:logseq.property/created-from-property :db/id])]
-                                                             (if (keyword? from-property-id)
-                                                               (assoc-in block [:logseq.property/created-from-property :db/id] (:db/id (d/entity db from-property-id)))
-                                                               block)))))
-                                      page (when (seq values)
-                                             (when-let [page-id (:db/id (:block/page (d/entity db (:db/id (first values)))))]
-                                               (d/pull db '[*] page-id)))]
-                                  (remove nil? (concat [page] value-blocks))))
-                              (:block/properties block)))]
-      block-properties)))
+    (let [block (d/entity db (:db/id block))]
+      (->> (:block/properties block)
+           vals
+           (mapcat
+            (fn [property-values]
+              (let [values (->>
+                            (if (and (coll? property-values)
+                                     (map? (first property-values)))
+                              property-values
+                              #{property-values})
+                            (remove sqlite-util/page?))
+                    value-ids (when (every? map? values)
+                                (->> (map :db/id values)
+                                     (filter (fn [id] (or (int? id) (keyword? id))))))
+                    value-blocks (->>
+                                  (when (seq value-ids)
+                                    (map
+                                     (fn [id] (d/pull db '[*] id))
+                                     value-ids))
+                             ;; FIXME: why d/pull returns {:db/id db-ident} instead of {:db/id number-eid}?
+                                  (keep (fn [block]
+                                          (let [from-property-id (get-in block [:logseq.property/created-from-property :db/id])]
+                                            (if (keyword? from-property-id)
+                                              (assoc-in block [:logseq.property/created-from-property :db/id] (:db/id (d/entity db from-property-id)))
+                                              block)))))]
+                value-blocks)))))))
 
 (defn get-block-children-ids
   "Returns children UUIDs"

+ 1 - 1
deps/db/src/logseq/db/sqlite/util.cljs

@@ -107,7 +107,7 @@
         (assoc :db/valueType :db.type/ref))))))
 
 (defn build-new-class
-  "Build a standard new class so that it is is consistent across contexts"
+  "Build a standard new class so that it is consistent across contexts"
   [block]
   {:pre [(qualified-keyword? (:db/ident block))]}
   (block-with-timestamps

+ 1 - 1
deps/db/test/logseq/db/frontend/rules_test.cljs

@@ -50,7 +50,7 @@
            {:properties {:foo {:block/schema {:type :default}}
                          :foo2 {:block/schema {:type :default}}
                          :number-many {:block/schema {:type :number :cardinality :many}}
-                         :page-many {:block/schema {:type :page :cardinality :many}}}
+                         :page-many {:block/schema {:type :node :cardinality :many}}}
             :pages-and-blocks
             [{:page {:block/title "Page"
                      :build/properties {:foo "bar" :number-many #{5 10} :page-many #{[:page "Page A"]}}}}

+ 1 - 1
deps/graph-parser/package.json

@@ -7,7 +7,7 @@
     "better-sqlite3": "9.3.0"
   },
   "dependencies": {
-    "mldoc": "^1.5.8"
+    "mldoc": "^1.5.9"
   },
   "scripts": {
     "test": "nbb-logseq -cp test:../outliner/src -m nextjournal.test-runner"

+ 3 - 1
deps/graph-parser/src/logseq/graph_parser/mldoc.cljc

@@ -145,7 +145,9 @@
     (->>
      (cond-> (default-config-map format)
        db-based?
-       (assoc :enable_drawers false))
+       (assoc :enable_drawers false
+              :parse_marker false
+              :parse_priority false))
      bean/->js
      js/JSON.stringify)))
 

+ 6 - 6
deps/graph-parser/test/logseq/graph_parser/block_test.cljs

@@ -24,21 +24,21 @@
              (not= (:uuid x) (:block/uuid result))
              (= (select-keys result
                              [:block/properties :block/title :block/properties-text-values :block/properties-order]) (gp-block/block-keywordize y))))
-    {:properties {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :tags [], :format :markdown, :meta {:start_pos 51, :end_pos 101}, :macros [], :content "bar\nid:: 63f199bc-c737-459f-983d-84acfcda14fe", :properties-text-values {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :level 1, :uuid #uuid "63f199bc-c737-459f-983d-84acfcda14fe", :properties-order [:id]}
+    {:properties {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :tags [], :format :markdown, :meta {:start_pos 51, :end_pos 101}, :macros [], :title "bar\nid:: 63f199bc-c737-459f-983d-84acfcda14fe", :properties-text-values {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :level 1, :uuid #uuid "63f199bc-c737-459f-983d-84acfcda14fe", :properties-order [:id]}
     {:properties {},
-     :content "bar",
+     :title "bar",
      :properties-text-values {},
      :properties-order []}
 
-    {:properties {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :tags [], :format :org, :meta {:start_pos 51, :end_pos 101}, :macros [], :content "bar\n:id: 63f199bc-c737-459f-983d-84acfcda14fe", :properties-text-values {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :level 1, :uuid #uuid "63f199bc-c737-459f-983d-84acfcda14fe", :properties-order [:id]}
+    {:properties {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :tags [], :format :org, :meta {:start_pos 51, :end_pos 101}, :macros [], :title "bar\n:id: 63f199bc-c737-459f-983d-84acfcda14fe", :properties-text-values {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :level 1, :uuid #uuid "63f199bc-c737-459f-983d-84acfcda14fe", :properties-order [:id]}
     {:properties {},
-     :content "bar",
+     :title "bar",
      :properties-text-values {},
      :properties-order []}
 
-    {:properties {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :tags [], :format :markdown, :meta {:start_pos 51, :end_pos 101}, :macros [], :content "bar\n  \n  id:: 63f199bc-c737-459f-983d-84acfcda14fe\nblock body", :properties-text-values {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :level 1, :uuid #uuid "63f199bc-c737-459f-983d-84acfcda14fe", :properties-order [:id]}
+    {:properties {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :tags [], :format :markdown, :meta {:start_pos 51, :end_pos 101}, :macros [], :title "bar\n  \n  id:: 63f199bc-c737-459f-983d-84acfcda14fe\nblock body", :properties-text-values {:id "63f199bc-c737-459f-983d-84acfcda14fe"}, :level 1, :uuid #uuid "63f199bc-c737-459f-983d-84acfcda14fe", :properties-order [:id]}
     {:properties {},
-     :content "bar\nblock body",
+     :title "bar\nblock body",
      :properties-text-values {},
      :properties-order []}))
 

+ 1 - 1
deps/graph-parser/test/logseq/graph_parser/cli_test.cljs

@@ -14,7 +14,7 @@
     (docs-graph-helper/docs-graph-assertions @conn graph-dir files)
 
     (testing "Additional counts"
-      (is (= 58051 (count (d/datoms @conn :eavt))) "Correct datoms count"))
+      (is (= 58054 (count (d/datoms @conn :eavt))) "Correct datoms count"))
 
     (testing "Asts"
       (is (seq asts) "Asts returned are non-zero")

+ 4 - 4
deps/graph-parser/yarn.lock

@@ -276,10 +276,10 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
   resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
   integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
 
-mldoc@^1.5.8:
-  version "1.5.8"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.8.tgz#5433950c9124f8917075bafa4f1ed886959504c5"
-  integrity sha512-wNUGgwdgB/5c3GTp2qRPzWD8CpeTmA/HDlGJjTJUo2WXJttiBaz8cHQh2OdAshiLdVxteqIaSTXSPFRf7z1YRw==
+mldoc@^1.5.9:
+  version "1.5.9"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.9.tgz#43d740351c64285f0f4988ac9497922d54ae66fc"
+  integrity sha512-87FQ7hseS87tsk+VdpIigpu8LH+GwmbbFgpxgFwvnbH5oOjmIrc47laH4Dyggzqiy8/vMjDHkl7vsId0eXhCDQ==
   dependencies:
     yargs "^12.0.2"
 

+ 1 - 1
deps/outliner/package.json

@@ -7,7 +7,7 @@
   },
   "dependencies": {
     "better-sqlite3": "9.3.0",
-    "mldoc": "^1.5.8"
+    "mldoc": "^1.5.9"
   },
   "scripts": {
     "test": "yarn nbb-logseq -cp test -m nextjournal.test-runner"

+ 31 - 26
deps/outliner/src/logseq/outliner/property.cljs

@@ -221,8 +221,10 @@
   [conn property-id v property-type]
   (if (and (integer? v)
            (or (not= property-type :number)
-               ;; Allows :number property to use number as a ref (for closed value) or value. Number value maybe only used in tests
-               (and (= property-type :number) (= property-id (:db/ident (:logseq.property/created-from-property (d/entity @conn v)))))))
+               ;; Allows :number property to use number as a ref (for closed value) or value
+               (and (= property-type :number)
+                    (or (= property-id (:db/ident (:logseq.property/created-from-property (d/entity @conn v))))
+                        (= :logseq.property/empty-placeholder (:db/ident (d/entity @conn v)))))))
     v
     ;; only value-ref-property types should call this
     (find-or-create-property-value conn property-id v)))
@@ -395,32 +397,24 @@
      (seq properties))))
 
 (defn- build-closed-value-tx
-  [db property resolved-value {:keys [id icon description]
-                               :or {description ""}}]
+  [db property resolved-value {:keys [id icon]}]
   (let [block (when id (d/entity db [:block/uuid id]))
         block-id (or id (ldb/new-block-id))
         icon (when-not (and (string? icon) (string/blank? icon)) icon)
-        description (string/trim description)
-        description (when-not (string/blank? description) description)
         tx-data (if block
-                  [(let [schema (:block/schema block)]
-                     (cond->
-                      (outliner-core/block-with-updated-at
-                       (merge
-                        {:block/uuid id
-                         :block/closed-value-property (:db/id property)
-                         :block/schema (if description
-                                         (assoc schema :description description)
-                                         (dissoc schema :description))}
-                        (if (db-property-type/original-value-ref-property-types (get-in property [:block/schema :type]))
-                          {:property.value/content resolved-value}
-                          {:block/title resolved-value})))
-                       icon
-                       (assoc :logseq.property/icon icon)))]
+                  [(cond->
+                    (outliner-core/block-with-updated-at
+                     (merge
+                      {:block/uuid id
+                       :block/closed-value-property (:db/id property)}
+                      (if (db-property-type/original-value-ref-property-types (get-in property [:block/schema :type]))
+                        {:property.value/content resolved-value}
+                        {:block/title resolved-value})))
+                     icon
+                     (assoc :logseq.property/icon icon))]
                   (let [max-order (:block/order (last (:property/closed-values property)))
                         new-block (-> (db-property-build/build-closed-value-block block-id resolved-value
-                                                                                  property {:icon icon
-                                                                                            :description description})
+                                                                                  property {:icon icon})
                                       (assoc :block/order (db-order/gen-key max-order nil)))]
                     [new-block
                      (outliner-core/block-with-updated-at
@@ -432,7 +426,7 @@
 
 (defn upsert-closed-value!
   "id should be a block UUID or nil"
-  [conn property-id {:keys [id value] :as opts}]
+  [conn property-id {:keys [id value description] :as opts}]
   (assert (or (nil? id) (uuid? id)))
   (let [db @conn
         property (d/entity db property-id)
@@ -470,9 +464,20 @@
           nil
 
           :else
-          (ldb/transact! conn
-                         (build-closed-value-tx @conn property resolved-value opts)
-                         {:outliner-op :save-block}))))))
+          (let [tx-data (build-closed-value-tx @conn property resolved-value opts)]
+            (ldb/transact! conn tx-data {:outliner-op :save-block})
+
+            (when (seq description)
+              (if-let [desc-ent (and id (:logseq.property/description (d/entity db [:block/uuid id])))]
+                (ldb/transact! conn
+                               [(outliner-core/block-with-updated-at {:db/id (:db/id desc-ent)
+                                                                      :block/title description})]
+                               {:outliner-op :save-block})
+                (set-block-property! conn
+                                     ;; new closed value is first in tx-data
+                                     [:block/uuid (or id (:block/uuid (first tx-data)))]
+                                     :logseq.property/description
+                                     description)))))))))
 
 (defn add-existing-values-to-closed-values!
   "Adds existing values as closed values and returns their new block uuids"

+ 1 - 1
deps/outliner/test/logseq/outliner/property_test.cljs

@@ -256,7 +256,7 @@
                                                                                :description "choice 4"})
             updated-b (d/entity @conn [:block/uuid (:block/uuid b)])]
         (is (= 4 (db-property/closed-value-content updated-b)))
-        (is (= "choice 4" (:description (:block/schema updated-b))))))))
+        (is (= "choice 4" (db-property/property-value-content (:logseq.property/description updated-b))))))))
 
 (deftest delete-closed-value!
   (let [closed-value-uuid (random-uuid)

+ 4 - 4
deps/outliner/yarn.lock

@@ -276,10 +276,10 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
   resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
   integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
 
-mldoc@^1.5.8:
-  version "1.5.8"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.8.tgz#5433950c9124f8917075bafa4f1ed886959504c5"
-  integrity sha512-wNUGgwdgB/5c3GTp2qRPzWD8CpeTmA/HDlGJjTJUo2WXJttiBaz8cHQh2OdAshiLdVxteqIaSTXSPFRf7z1YRw==
+mldoc@^1.5.9:
+  version "1.5.9"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.9.tgz#43d740351c64285f0f4988ac9497922d54ae66fc"
+  integrity sha512-87FQ7hseS87tsk+VdpIigpu8LH+GwmbbFgpxgFwvnbH5oOjmIrc47laH4Dyggzqiy8/vMjDHkl7vsId0eXhCDQ==
   dependencies:
     yargs "^12.0.2"
 

+ 1 - 1
deps/publishing/package.json

@@ -4,7 +4,7 @@
   "private": true,
   "devDependencies": {
     "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v15",
-    "mldoc": "^1.5.8"
+    "mldoc": "^1.5.9"
   },
   "dependencies": {
     "fs-extra": "9.1.0"

+ 4 - 4
deps/publishing/yarn.lock

@@ -189,10 +189,10 @@ mimic-fn@^2.0.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
-mldoc@^1.5.8:
-  version "1.5.8"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.8.tgz#5433950c9124f8917075bafa4f1ed886959504c5"
-  integrity sha512-wNUGgwdgB/5c3GTp2qRPzWD8CpeTmA/HDlGJjTJUo2WXJttiBaz8cHQh2OdAshiLdVxteqIaSTXSPFRf7z1YRw==
+mldoc@^1.5.9:
+  version "1.5.9"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.9.tgz#43d740351c64285f0f4988ac9497922d54ae66fc"
+  integrity sha512-87FQ7hseS87tsk+VdpIigpu8LH+GwmbbFgpxgFwvnbH5oOjmIrc47laH4Dyggzqiy8/vMjDHkl7vsId0eXhCDQ==
   dependencies:
     yargs "^12.0.2"
 

+ 1 - 1
package.json

@@ -135,7 +135,7 @@
         "jszip": "3.8.0",
         "katex": "^0.16.10",
         "marked": "^5.1.2",
-        "mldoc": "^1.5.8",
+        "mldoc": "^1.5.9",
         "path": "0.12.7",
         "path-complete-extname": "1.0.0",
         "pdfjs-dist": "^3.9.179",

+ 1 - 1
scripts/package.json

@@ -8,6 +8,6 @@
   "dependencies": {
     "better-sqlite3": "9.3.0",
     "fs-extra": "9.1.0",
-    "mldoc": "^1.5.8"
+    "mldoc": "^1.5.9"
   }
 }

+ 25 - 17
scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs

@@ -11,7 +11,6 @@
        type logseq doesnt' support yet
      * schema.org assumes no cardinality. For now, only :node properties are given a :cardinality :many"
   (:require [logseq.outliner.cli :as outliner-cli]
-            [logseq.common.util :as common-util]
             [logseq.db.frontend.property :as db-property]
             [clojure.string :as string]
             [clojure.edn :as edn]
@@ -66,7 +65,7 @@
     (cond-> {:block/title class-name
              :build/properties (cond-> {:url url}
                                  (class-m "rdfs:comment")
-                                 (assoc :description (get-comment-string (class-m "rdfs:comment") renamed-pages)))}
+                                 (assoc :logseq.property/description (get-comment-string (class-m "rdfs:comment") renamed-pages)))}
       parent-class'
       (assoc :build/class-parent (keyword (strip-schema-prefix parent-class')))
       (seq properties)
@@ -77,8 +76,8 @@
   {"schema:Integer" :number
    "schema:Float" :number
    "schema:Number" :number
-   "schema:Text_Class" :default
-   "schema:URL_Class" :url
+   "schema:Text" :default
+   "schema:URL" :url
    "schema:Boolean" :checkbox
    "schema:Date" :date})
 
@@ -118,12 +117,12 @@
         schema (cond-> {:type schema-type}
                  ;; This cardinality rule should be adjusted as we use schema.org more
                  (= schema-type :node)
-                 (assoc :cardinality :many)
-                 (property-m "rdfs:comment")
-                 (assoc :description (get-comment-string (property-m "rdfs:comment") renamed-pages)))]
+                 (assoc :cardinality :many))]
     {(keyword (strip-schema-prefix (property-m "@id")))
      (cond-> {:block/schema schema
-              :build/properties {:url url}}
+              :build/properties (cond-> {:url url}
+                                  (property-m "rdfs:comment")
+                                  (assoc :logseq.property/description (get-comment-string (property-m "rdfs:comment") renamed-pages)))}
        (= schema-type :node)
        (assoc :build/schema-classes (mapv (comp keyword strip-schema-prefix) range-includes)))}))
 
@@ -158,11 +157,11 @@
 
 (defn- get-vector-conflicts
   "Given a seq of tuples returns a seq of tuples that conflict i.e. their first element
-   has a case insensitive conflict/duplicate with another. An example conflict:
-   [[\"schema:businessFunction\" :property] [\"schema:BusinessFunction\" :class]]"
+   has a case sensitive conflict/duplicate with another. An example conflict:
+   [[\"schema:status\" :property] [\"schema:status\" :node]]"
   [tuples-seq]
   (->> tuples-seq
-       (group-by (comp common-util/page-name-sanity-lc first))
+       (group-by first)
        (filter #(> (count (val %)) 1))
        vals))
 
@@ -190,7 +189,8 @@
     (if verbose
       (println "Renaming the following properties because they have names that conflict with Logseq's built in pages"
                (keys renamed-properties) "\n")
-      (println "Renaming" (count renamed-properties) "properties due to page name conflicts"))
+      (when (pos? (count renamed-properties))
+        (println "Renaming" (count renamed-properties) "properties due to page name conflicts")))
     renamed-properties))
 
 (defn- detect-id-conflicts-and-get-renamed-classes
@@ -219,7 +219,8 @@
     (if verbose
       (println "Renaming the following classes because they have property names that conflict with Logseq's case insensitive :block/name:"
                (keys renamed-classes) "\n")
-      (println "Renaming" (count renamed-classes) "classes due to page name conflicts"))
+      (when (pos? (count renamed-classes))
+        (println "Renaming" (count renamed-classes) "classes due to page name conflicts")))
     renamed-classes))
 
 (defn- get-all-properties [schema-data {:keys [verbose]}]
@@ -255,8 +256,13 @@
                           (get-schema-type (get-range-includes property-m) class-map)))
                   frequencies)
              "\n"))
-  (apply merge
-         (mapv #(->property-page % class-map options) select-properties)))
+  (assoc
+   (apply merge
+          (mapv #(->property-page % class-map options) select-properties))
+   ;; Have to update schema for now as validation doesn't take into account existing properties
+   :logseq.property/description {:block/schema {:public? true :type :default}
+                                 :build/properties {:url "https://schema.org/description"
+                                                    :logseq.property/description "A description of the item."}}))
 
 (defn- get-all-classes-and-properties
   "Get all classes and properties from raw json file"
@@ -266,7 +272,8 @@
                                                 (if (string? type') [type'] type')))
                                          "rdfs:Class")
                              schema-data)
-        all-properties* (get-all-properties schema-data options)
+        ;; Use built-in description
+        all-properties* (remove #(= "schema:description" (% "@id")) (get-all-properties schema-data options))
         property-tuples (map #(vector (% "@id") :property) all-properties*)
         class-tuples (map #(vector (% "@id") :class) all-classes*)
         page-tuples (map #(vector (str "schema:" %) :node) existing-pages)
@@ -275,6 +282,7 @@
         renamed-properties (detect-property-conflicts-and-get-renamed-properties
                             property-tuples page-tuples options)
         renamed-pages (merge renamed-classes renamed-properties)
+        ;; Note: schema:description refs don't get renamed but they aren't used
         ;; Updates keys like @id, @subClassOf
         rename-page-ids (fn [m]
                           (w/postwalk (fn [x]
@@ -308,7 +316,7 @@
         select-class-ids
         (if (:subset options)
           ["schema:Person" "schema:CreativeWorkSeries" "schema:Organization"
-           "schema:Movie" "schema:CreativeWork" "schema:Thing"]
+           "schema:Movie" "schema:CreativeWork" "schema:Thing" "schema:Comment"]
           (keys class-map))
         class-to-properties (get-class-to-properties select-class-ids all-properties)
         select-properties (set (mapcat val class-to-properties))

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

@@ -16,7 +16,8 @@
          "electron.db"
          "frontend.handler.db-based."
          "frontend.worker.handler.page.db-based"
-         "frontend.components.property" "frontend.components.class" "frontend.components.db-based"]))
+         "frontend.components.property" "frontend.components.class" "frontend.components.db-based"
+         "frontend.components.objects"]))
 
 (def file-graph-ns
   "Namespaces or parent namespaces _only_ for file graphs"
@@ -38,6 +39,7 @@
    "src/main/frontend/components/class.cljs"
    "src/main/frontend/components/property.cljs"
    "src/main/frontend/components/property"
+   "src/main/frontend/components/objects.cljs"
    "src/main/frontend/components/db_based"
    "src/electron/electron/db.cljs"])
 

+ 4 - 4
scripts/yarn.lock

@@ -305,10 +305,10 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
   resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
   integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
 
-mldoc@^1.5.8:
-  version "1.5.8"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.8.tgz#5433950c9124f8917075bafa4f1ed886959504c5"
-  integrity sha512-wNUGgwdgB/5c3GTp2qRPzWD8CpeTmA/HDlGJjTJUo2WXJttiBaz8cHQh2OdAshiLdVxteqIaSTXSPFRf7z1YRw==
+mldoc@^1.5.9:
+  version "1.5.9"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.9.tgz#43d740351c64285f0f4988ac9497922d54ae66fc"
+  integrity sha512-87FQ7hseS87tsk+VdpIigpu8LH+GwmbbFgpxgFwvnbH5oOjmIrc47laH4Dyggzqiy8/vMjDHkl7vsId0eXhCDQ==
   dependencies:
     yargs "^12.0.2"
 

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

@@ -384,7 +384,7 @@ button.menu:focus {
 
 .menu-link {
   @apply text-popover-foreground/75 select-none hover:text-popover-foreground/100;
-  @apply text-sm px-2 py-1.5 mx-1 hover:rounded transition-opacity duration-150;
+  @apply text-sm px-2 py-1.5 hover:rounded transition-opacity duration-150;
 }
 
 .menu-separator {

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

@@ -2479,7 +2479,7 @@
        (when-not plugin-slotted?
          [:div.block-head-wrap
           (cond
-            (ldb/page? block)
+            (or (ldb/page? block) (and (:table? config) (seq (:block/tags block))))
             [:div.flex.flex-row.items-center.gap-1
              (page-cp config block)]
 

+ 18 - 0
src/main/frontend/components/block.css

@@ -204,6 +204,24 @@
 
   &.as-heading {
     @apply flex flex-1;
+
+    h1& {
+      .ui__icon.ti svg {
+        @apply w-8 h-7;
+      }
+    }
+
+    h2& {
+      .ui__icon.ti svg {
+        @apply w-7 h-6;
+      }
+    }
+
+    h3& {
+      .ui__icon.ti svg {
+        @apply w-5 h-5;
+      }
+    }
   }
 
   &:has(.dsl-query), &:has(.embed-page) {

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

@@ -545,7 +545,7 @@
                    {:drop (fn [_e files]
                             (when-let [id (state/get-edit-input-id)]
                               (let [format (:block/format (state/get-edit-block))]
-                                (editor-handler/upload-asset id files format editor-handler/*asset-uploading? true))))})
+                                (editor-handler/upload-asset! id files format editor-handler/*asset-uploading? true))))})
                   (common-handler/listen-to-scroll! element)
                   (when (:margin-less-pages? (first (:rum/args state))) ;; makes sure full screen pages displaying without scrollbar
                     (set! (.. element -scrollTop) 0)))
@@ -555,10 +555,10 @@
                      (dnd/unsubscribe! el :upload-files))
                    state)}
   [{:keys [route-match margin-less-pages? route-name indexeddb-support? db-restoring? main-content show-action-bar? show-recording-bar?]}]
-  (let [left-sidebar-open?   (state/sub :ui/left-sidebar-open?)
+  (let [left-sidebar-open? (state/sub :ui/left-sidebar-open?)
         onboarding-and-home? (and (or (nil? (state/get-current-repo)) (config/demo-graph?))
-                                  (not config/publishing?)
-                                  (= :home route-name))
+                               (not config/publishing?)
+                               (= :home route-name))
         margin-less-pages?   (or (and (mobile-util/native-platform?) onboarding-and-home?) margin-less-pages?)]
     [:div#main-container.cp__sidebar-main-layout.flex-1.flex
      {:class (util/classnames [{:is-left-sidebar-open left-sidebar-open?}])}

+ 80 - 250
src/main/frontend/components/editor.cljs

@@ -482,124 +482,6 @@
                (util/stop e)
                (on-submit command @input-value pos)))])))))
 
-(rum/defc absolute-modal < rum/static
-  [cp modal-name set-default-width? {:keys [top left rect]}]
-  (let [MAX-HEIGHT 700
-        MAX-HEIGHT' 600
-        MAX-WIDTH 600
-        SM-MAX-WIDTH 300
-        Y-BOUNDARY-HEIGHT 150
-        vw-width js/window.innerWidth
-        vw-height js/window.innerHeight
-        vw-max-width (- vw-width (:left rect))
-        vw-max-height (- vw-height (:top rect))
-        vw-max-height' (:top rect)
-        sm? (< vw-width 415)
-        max-height (min (- vw-max-height 20) MAX-HEIGHT)
-        max-height' (min (- vw-max-height' 70) MAX-HEIGHT')
-        max-width (if sm? SM-MAX-WIDTH (min (max 400 (/ vw-max-width 2)) MAX-WIDTH))
-        offset-top 24
-        to-max-height (cond-> (if (and (seq rect) (> vw-height max-height))
-                                (let [delta-height (- vw-height (+ (:top rect) top offset-top))]
-                                  (if (< delta-height max-height)
-                                    (- (max (* 2 offset-top) delta-height) 16)
-                                    max-height))
-                                max-height)
-
-                        (= modal-name "commands")
-                        (min 500))
-        right-sidebar? (:ui/sidebar-open? @state/state)
-        editing-key    (state/get-edit-input-id)
-        *el (rum/use-ref nil)
-        y-overflow-vh? (or (< to-max-height Y-BOUNDARY-HEIGHT)
-                           (> (- max-height' to-max-height) Y-BOUNDARY-HEIGHT))
-        to-max-height (if y-overflow-vh? max-height' to-max-height)
-        pos-rect (when (and (seq rect) editing-key)
-                   (:rect (cursor/get-caret-pos (state/get-input))))
-        y-diff (when pos-rect (- (:height pos-rect) (:height rect)))
-        style (merge
-               {:top        (+ top offset-top (if (int? y-diff) y-diff 0))
-                :max-height to-max-height
-                :max-width  700
-                ;; TODO: auto responsive fixed size
-                :width      "fit-content"
-                :z-index    11}
-               (when set-default-width?
-                 {:width max-width})
-               (if (<= vw-max-width (+ left (if set-default-width? max-width 500)))
-                 {:right 0}
-                 {:left 0}))]
-
-    (rum/use-effect!
-     (fn []
-       (when-let [^js/HTMLElement cnt
-                  (and right-sidebar? editing-key
-                       (js/document.querySelector "#main-content-container"))]
-         (when (.contains cnt (js/document.querySelector (str "#" editing-key)))
-           (let [el  (rum/deref *el)
-                 ofx (- (.-scrollWidth cnt) (.-clientWidth cnt))]
-             (when (> ofx 0)
-               (set! (.-transform (.-style el))
-                     (util/format "translate(-%spx, %s)" (+ ofx 20) (if y-overflow-vh? "calc(-100% - 2rem)" 0))))))))
-     [right-sidebar? editing-key y-overflow-vh?])
-
-    ;; HACK: close when click outside for classic editing models (popup)
-    (rum/use-effect!
-      (fn []
-        (let [^js cnt js/document.body
-              handle (fn [^js e]
-                       (when-not (some->> (.-target e) (.contains (rum/deref *el)))
-                         (state/clear-editor-action!)))]
-          (.addEventListener cnt "click" handle false)
-          #(.removeEventListener cnt "click" handle)))
-      [])
-
-    [:div.absolute.rounded-md.shadow-lg.absolute-modal
-     {:ref             *el
-      :data-modal-name modal-name
-      :class           (if y-overflow-vh? "is-overflow-vh-y" "")
-      :on-pointer-down   (fn [e]
-                         (.stopPropagation e))
-      :on-key-down     (fn [^js e]
-                         (case (.-key e)
-                           "Escape"
-                           (do (state/clear-editor-action!)
-                               (some-> (state/get-input)
-                                 (.focus)))
-                           :dune)
-                         (util/stop-propagation e))
-      :style style}
-     cp]))
-
-(rum/defc transition-cp < rum/reactive
-  [cp modal-name set-default-width?]
-  (when-let [pos (:pos (state/sub :editor/action-data))]
-    (ui/css-transition
-      {:class-names "fade"
-       :timeout {:enter 500
-                 :exit 300}}
-      (absolute-modal cp modal-name set-default-width? pos))))
-
-(rum/defc image-uploader < rum/reactive
-  [id format]
-  [:div.image-uploader
-   [:input
-    {:id        "upload-file"
-     :type      "file"
-     :on-change (fn [e]
-                  (let [files (.-files (.-target e))]
-                    (editor-handler/upload-asset id files format editor-handler/*asset-uploading? false)))
-     :hidden    true}]
-   #_:clj-kondo/ignore
-   (when-let [uploading? (util/react editor-handler/*asset-uploading?)]
-     (let [processing (util/react editor-handler/*asset-uploading-process)]
-       (transition-cp
-        [:div.flex.flex-row.align-center.rounded-md.shadow-sm.bg-base-2.px-1.py-1
-         (ui/loading
-          (util/format "Uploading %s%" (util/format "%2d" processing)))]
-        "upload-file"
-        false)))])
-
 (defn- set-up-key-down!
   [state format]
   (mixins/on-key-down
@@ -694,146 +576,98 @@
          [:span {:id (str "mock-text_" idx)
                  :key idx} c])))])
 
-(rum/defc animated-modal < rum/reactive
-  [modal-name component set-default-width?]
-  (when-let [pos (:pos (state/get-editor-action-data))]
-    (ui/css-transition
-     {:key modal-name
-      :class-names {:enter "origin-top-left opacity-0 transform scale-95"
-                    :enter-done "origin-top-left transition opacity-100 transform scale-100"
-                    :exit "origin-top-left transition opacity-0 transform scale-95"}
-      :timeout {:enter 0
-                :exit 150}}
-     (fn [_]
-       (absolute-modal
-        component
-        modal-name
-        set-default-width?
-        pos)))))
-
 (defn- exist-editor-commands-popup?
   []
   (some->> (shui-popup/get-popups)
     (some #(some-> % (:id) (str) (string/starts-with? ":editor.commands")))))
 
-;; TODO: [WIP]
-(rum/defc shui-modals
+(defn- open-editor-popup!
+  [id content opts]
+  (let [{:keys [left top rect]} (cursor/get-caret-pos (state/get-input))
+        pos [(+ left (:left rect) -20) (+ top (:top rect) 20)]
+        {:keys [root-props content-props]} opts]
+    (shui/popup-show!
+      pos content
+      (merge
+        {:id (keyword :editor.commands id)
+         :align :start
+         :root-props (merge {:onOpenChange #(when-not % (state/clear-editor-action!))} root-props)
+         :content-props (merge {:onOpenAutoFocus #(.preventDefault %)
+                                :onCloseAutoFocus #(.preventDefault %)
+                                :data-editor-popup-ref (name id)} content-props)
+         :force-popover? true}
+        (dissoc opts :root-props :content-props)))))
+
+(rum/defc shui-editor-popups
   [id format action _data]
   (rum/use-effect!
     (fn []
-      (let [{:keys [left top rect]} (cursor/get-caret-pos (state/get-input))
-            pos [(+ left (:left rect) -20) (+ top (:top rect) 20)]]
-        (let [pid (case action
-                    :commands
-                    (shui/popup-show! pos
-                      (commands id format)
-                      {:id :editor.commands/commands
-                       :align :start
-                       :root-props {:onOpenChange
-                                    #(when-not %
-                                       (when (= :commands (state/get-editor-action))
-                                         (state/clear-editor-action!)))}
-                       :content-props {:onOpenAutoFocus #(.preventDefault %)
-                                       :onCloseAutoFocus #(.preventDefault %)
-                                       :withoutAnimation true
-                                       :data-editor-popup-ref "commands"}
-                       :force-popover? true})
-
-                    :block-commands
-                    (shui/popup-show! pos
-                      (block-commands id format)
-                      {:id :editor.commands/block-commands
-                       :align :start
-                       :root-props {:onOpenChange
-                                    #(when-not %
-                                       (when (= :block-commands (state/get-editor-action))
-                                         (state/clear-editor-action!)))}
-                       :content-props {:onOpenAutoFocus #(.preventDefault %)
-                                       :onCloseAutoFocus #(.preventDefault %)
-                                       :withoutAnimation true
-                                       :data-editor-popup-ref "commands"}
-                       :force-popover? true})
-
-                    (:block-search :page-search :page-search-hashtag)
-                    (shui/popup-show!
-                      pos (if (= :block-search action)
-                            (block-search id format)
-                            (page-search id format))
-                      {:id :editor.commands/block-search
-                       :align :start
-                       :root-props {:onOpenChange
-                                    #(when-not %
-                                       (when (contains?
-                                               #{:block-search :page-search :page-search-hashtag}
-                                               (state/get-editor-action))
-                                         (state/clear-editor-action!)))}
-                       :content-props {:onOpenAutoFocus #(.preventDefault %)
-                                       :onCloseAutoFocus #(.preventDefault %)
-                                       :data-editor-popup-ref (name action)}
-                       :force-popover? true})
-
-                    :datepicker
-                    (shui/popup-show!
-                      pos (datetime-comp/date-picker id format nil)
-                      {:id :editor.commands/datepicker
-                       :align :start
-                       :root-props {:onOpenChange #(when-not % (state/clear-editor-action!))}
-                       :content-props {:onOpenAutoFocus #(.preventDefault %)
-                                       :data-editor-popup-ref "datepicker"}
-                       :force-popover? true})
-
-                    :input
-                    (shui/popup-show!
-                      pos (input id
-                            (fn [command m]
-                              (editor-handler/handle-command-input command id format m))
-                            (fn []
-                              (editor-handler/handle-command-input-close id)))
-                      {:id :editor.commands/input
-                       :align :start
-                       :root-props {:onOpenChange #(when-not % (state/clear-editor-action!))}
-                       :content-props {:onOpenAutoFocus #(.preventDefault %)
-                                       :onCloseAutoFocus #(.preventDefault %)
-                                       :data-editor-popup-ref "input"}})
-
-                    :select-code-block-mode
-                    (shui/popup-show!
-                      pos (code-block-mode-picker id format)
-                      {:id :editor.commands/code-block-mode-picker
-                       :align :start
-                       :root-props {:onOpenChange #(when-not % (state/clear-editor-action!))}
-                       :content-props {:onOpenAutoFocus #(.preventDefault %)
-                                       :data-editor-popup-ref "code-block-mode-picker"}
-                       :force-popover? true})
-
-                    ;; TODO: try remove local model state
-                    false)]
-          #(when pid
-             (shui/popup-hide! pid)))))
+      (let [pid (case action
+                  :commands
+                  (open-editor-popup! :commands
+                    (commands id format)
+                    {:content-props {:withoutAnimation false}})
+
+                  :block-commands
+                  (open-editor-popup! :block-commands
+                    (block-commands id format)
+                    {:content-props {:withoutAnimation true}})
+
+                  (:block-search :page-search :page-search-hashtag)
+                  (open-editor-popup! action
+                    (if (= :block-search action)
+                      (block-search id format)
+                      (page-search id format))
+                    {:root-props {:onOpenChange
+                                  #(when-not %
+                                     (when (contains?
+                                             #{:block-search :page-search :page-search-hashtag}
+                                             (state/get-editor-action))
+                                       (state/clear-editor-action!)))}})
+
+                  :datepicker
+                  (open-editor-popup! :datepicker
+                    (datetime-comp/date-picker id format nil) {})
+
+                  :input
+                  (open-editor-popup! :input
+                    (input id
+                      (fn [command m]
+                        (editor-handler/handle-command-input command id format m))
+                      (fn []
+                        (editor-handler/handle-command-input-close id))) {})
+
+                  :select-code-block-mode
+                  (open-editor-popup! :code-block-mode-picker
+                    (code-block-mode-picker id format) {})
+
+                  :template-search
+                  (open-editor-popup! :template-search
+                    (template-search id format) {})
+
+                  (:property-search :property-value-search)
+                  (open-editor-popup! action
+                    (if (= :property-search action)
+                      (property-search id) (property-value-search id))
+                    {})
+
+                  :zotero
+                  (open-editor-popup! :zotero
+                    (zotero/zotero-search id) {})
+
+                  ;; TODO: try remove local model state
+                  false)]
+        #(when pid
+           (shui/popup-hide! pid))))
     [action])
   [:<>])
 
-(rum/defc modals < rum/reactive
-  "React to atom changes, find and render the correct modal"
+(rum/defc command-popups <
+  rum/reactive
+  "React to atom changes, find and render the correct popup"
   [id format]
   (let [action (state/sub :editor/action)]
-    [:<>
-     (shui-modals id format action nil)
-     (cond
-       (= :template-search action)
-       (animated-modal "template-search" (template-search id format) true)
-
-       (= :property-search action)
-       (animated-modal "property-search" (property-search id) true)
-
-       (= :property-value-search action)
-       (animated-modal "property-value-search" (property-value-search id) true)
-
-       (= :zotero action)
-       (animated-modal "zotero-search" (zotero/zotero-search id) false)
-
-       :else
-       nil)]))
+    (shui-editor-popups id format action nil)))
 
 (defn- editor-on-hide
   [state value* type e]
@@ -916,9 +750,5 @@
     [:div.editor-inner.flex.flex-1 {:class (if block "block-editor" "non-block-editor")}
 
      (ui/ls-textarea opts)
-
      (mock-textarea content)
-     (modals id format)
-
-     (when format
-       (image-uploader id format))]))
+     (command-popups id format)]))

+ 3 - 3
src/main/frontend/components/editor.css

@@ -73,7 +73,7 @@ pre {
 
 .ui__popover-content, .ui__dropdown-menu-content {
   &[data-editor-popup-ref] {
-    @apply p-1 w-72;
+    @apply p-1.5 w-72;
 
     &[data-side=top] {
       position: relative;
@@ -82,7 +82,7 @@ pre {
   }
 
   &[data-editor-popup-ref=commands] {
-    @apply px-1 py-1 w-72;
+    @apply w-72;
 
     &[data-side=top] {
       max-height: min(calc(var(--radix-popover-content-available-height) - 60px), 460px);
@@ -96,7 +96,7 @@ pre {
   &[data-editor-popup-ref=page-search],
   &[data-editor-popup-ref=block-search],
   &[data-editor-popup-ref=page-search-hashtag] {
-    @apply px-1 py-1 w-full sm:w-128;
+    @apply w-full sm:w-128;
   }
 
   &[data-editor-popup-ref=datepicker] {

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

@@ -40,7 +40,7 @@
     (when class
       (ui/checkbox {:class class
                     :style {:margin-right 5}
-                    :value checked?
+                    :checked checked?
                     :on-pointer-down (fn [e]
                                        (util/stop-propagation e))
                     :on-change (fn [_e]

+ 91 - 12
src/main/frontend/components/objects.cljs

@@ -1,5 +1,5 @@
 (ns frontend.components.objects
-  "Tagged objects"
+  "Provides table views for class objects and property related objects"
   (:require [frontend.components.views :as views]
             [frontend.db :as db]
             [logseq.db :as ldb]
@@ -15,29 +15,29 @@
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.op :as outliner-op]))
 
-(defn- get-all-objects
+(defn- get-class-objects
   [class]
   (->> (db-model/get-class-objects (state/get-current-repo) (:db/id class))
        (map (fn [row] (assoc row :id (:db/id row))))))
 
-(defn- add-new-object!
+(defn- add-new-class-object!
   [class set-data!]
   (p/let [block (editor-handler/api-insert-new-block! ""
                                                       {:page (:block/uuid class)
                                                        :properties {:block/tags (:db/id class)}
                                                        :edit-block? false})
-          _ (set-data! (get-all-objects class))]
+          _ (set-data! (get-class-objects class))]
     (editor-handler/edit-block! (db/entity [:block/uuid (:block/uuid block)]) 0 :unknown-container)))
 
 (defn- get-views
-  [class]
-  (let [class (db/entity (:db/id class))]
+  [ent]
+  (let [class (db/entity (:db/id ent))]
     (-> (filter (fn [b]
                  (= (:db/ident class) (:logseq.property/view-for b)))
                (:block/_parent class))
        (ldb/sort-by-order))))
 
-(rum/defc objects-inner < rum/static
+(rum/defc class-objects-inner < rum/static
   [config class objects properties]
   (let [[loading? set-loading?] (rum/use-state nil)
         [view-entity set-view-entity!] (rum/use-state nil)
@@ -57,7 +57,7 @@
          (when-let [view (first views)]
            (set-view-entity! view))
          (p/let [_result (db-async/<get-tag-objects (state/get-current-repo) (:db/id class))]
-           (set-data! (get-all-objects class))
+           (set-data! (get-class-objects class))
            (set-loading? false))))
      [])
 
@@ -65,7 +65,7 @@
       (views/view view-entity {:data data
                                :set-data! set-data!
                                :columns columns
-                               :add-new-object! #(add-new-object! class set-data!)
+                               :add-new-object! #(add-new-class-object! class set-data!)
                                :create-view! (fn []
                                                (p/let [result (editor-handler/api-insert-new-block! "" {:page (:block/uuid class)
                                                                                                         :properties {:logseq.property/view-for (:db/ident class)}})]
@@ -89,11 +89,11 @@
                                                            tx-data (map (fn [pid] [:db/retract pid :block/tags (:db/id class)]) page-ids)]
                                                        (when (seq tx-data)
                                                          (outliner-op/transact! tx-data {:outliner-op :save-block}))))
-                                                    (set-data! (get-all-objects class))
+                                                    (set-data! (get-class-objects class))
                                                     (when-let [f (get-in table [:data-fns :set-row-selection!])]
                                                       (f {})))))}))))
 
-(rum/defcs objects < rum/reactive db-mixins/query mixins/container-id
+(rum/defcs class-objects < rum/reactive db-mixins/query mixins/container-id
   [state class]
   (when class
     (let [class (db/sub-block (:db/id class))
@@ -105,4 +105,83 @@
           objects (->> (db-model/sub-class-objects repo (:db/id class))
                        (map (fn [row] (assoc row :id (:db/id row)))))]
       [:div.ml-2
-       (objects-inner config class objects properties)])))
+       (class-objects-inner config class objects properties)])))
+
+(defn- get-property-related-objects [repo property]
+  (->> (db-model/get-property-related-objects repo (:db/id property))
+       (map (fn [row] (assoc row :id (:db/id row))))))
+
+(defn- add-new-property-object!
+  [property set-data!]
+  (p/let [block (editor-handler/api-insert-new-block! ""
+                                                      {:page (:block/uuid property)
+                                                       :properties {(:db/ident property) (:db/id (db/entity :logseq.property/empty-placeholder))}
+                                                       :edit-block? false})
+          _ (set-data! (get-property-related-objects (state/get-current-repo) property))]
+    (editor-handler/edit-block! (db/entity [:block/uuid (:block/uuid block)]) 0 :unknown-container)))
+
+(rum/defc property-related-objects-inner < rum/static
+  [config property objects properties]
+  (let [[loading? set-loading?] (rum/use-state nil)
+        [view-entity set-view-entity!] (rum/use-state nil)
+        [data set-data!] (rum/use-state objects)
+        columns (views/build-columns config properties)]
+
+    (rum/use-effect!
+     (fn []
+       (set-data! objects))
+     [objects])
+
+    (rum/use-effect!
+     (fn []
+       (set-loading? true)
+       (p/let [_result (db-async/<get-views (state/get-current-repo) (:db/id property))
+               views (get-views property)]
+         (when-let [view (first views)]
+           (set-view-entity! view))
+         (p/let [result (db-async/<get-property-objects (state/get-current-repo) (:db/ident property))]
+           (set-data! (mapv #(assoc % :id (:db/id %)) result))
+           (set-loading? false))))
+     [])
+
+    (when (false? loading?)
+      (views/view view-entity {:data data
+                               :set-data! set-data!
+                               :columns columns
+                               :add-new-object! #(add-new-property-object! property set-data!)
+                               :create-view! (fn []
+                                               (p/let [result (editor-handler/api-insert-new-block! "" {:page (:block/uuid property)
+                                                                                                        :properties {:logseq.property/view-for (:db/ident property)}})]
+                                                 (let [view (db/entity [:block/uuid (:block/uuid result)])]
+                                                   (set-view-entity! view)
+                                                   view)))
+                               ;; 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 {})))))}))))
+
+;; Show all nodes containing the given property
+(rum/defcs property-related-objects < rum/reactive db-mixins/query mixins/container-id
+  [state property]
+  (when property
+    (let [property' (db/sub-block (:db/id property))
+          config {:container-id (:container-id state)}
+          ;; Show tags to help differentiate property rows
+          properties [property' (db/entity :block/tags)]
+          repo (state/get-current-repo)
+          objects (get-property-related-objects repo property)]
+      [:div.ml-2
+       (property-related-objects-inner config property' objects properties)])))

+ 7 - 3
src/main/frontend/components/page.cljs

@@ -561,9 +561,13 @@
 
             (when (and db-based? (ldb/class? page))
               [:div.mt-8
-               (objects/objects page)])
+               (objects/class-objects page)])
 
-            (when-not (and db-based? (ldb/class? page))
+            (when (and db-based? (ldb/property? page))
+              [:div.mt-8
+               (objects/property-related-objects page)])
+
+            (when-not (and db-based? (or (ldb/class? page) (ldb/property? page)))
               [:div
                (when (and block? (not sidebar?) (not whiteboard?))
                  (let [config (merge config {:id "block-parent"
@@ -571,7 +575,7 @@
                    [:div.mb-4
                     (component-block/breadcrumb config repo block-id {:level-limit 3})]))
 
-             ;; blocks
+               ;; blocks
                (page-blocks-cp repo page (merge option {:sidebar? sidebar?
                                                         :whiteboard? whiteboard?}))])])
 

+ 16 - 4
src/main/frontend/components/property.cljs

@@ -238,6 +238,7 @@
   db-mixins/query
   (rum/local nil ::property-name)
   (rum/local nil ::property-schema)
+  (rum/local nil ::property-description)
   {:init (fn [state]
            (let [*values (atom :loading)]
              (p/let [result (db-async/<get-block-property-values (state/get-current-repo)
@@ -248,6 +249,7 @@
                  (let [[property _opts] (:rum/args state)]
                    (reset! (::property-name state) (:block/title property))
                    (reset! (::property-schema state) (:block/schema property))
+                   (reset! (::property-description state) (db-property/property-value-content (:logseq.property/description property)))
                    (state/set-state! :editor/property-configure? true)
                    state))
    :will-unmount (fn [state]
@@ -260,6 +262,7 @@
     (when-not (= :loading values)
       (let [*property-name (::property-name state)
             *property-schema (::property-schema state)
+            *property-description (::property-description state)
             property (db/sub-block (:db/id property))
             built-in? (ldb/built-in? property)
             disabled? (or built-in? config/publishing?)
@@ -391,15 +394,24 @@
                                     (swap! *property-schema assoc :hide? (not hide?))
                                     (save-property-fn))})])
 
-          (let [description (or (:description @*property-schema) "")]
+          (let [description (or @*property-description "")]
             [:div.grid.grid-cols-5.gap-1.items-start.leading-8
              [:label.col-span-2 "Description:"]
              [:div.col-span-3
               [:div.mt-1
                (shui/textarea
                 {:on-change (fn [e]
-                              (swap! *property-schema assoc :description (util/evalue e)))
-                 :on-blur save-property-fn
+                              (reset! *property-description (util/evalue e)))
+                 :on-blur (fn []
+                            (if-let [ent (:logseq.property/description property)]
+                              (db/transact! (state/get-current-repo)
+                                            [(outliner-core/block-with-updated-at
+                                              {:db/id (:db/id ent) :block/title @*property-description})]
+                                            {:outliner-op :save-block})
+                              (db-property-handler/set-block-property!
+                               (:db/id property)
+                               :logseq.property/description
+                               @*property-description)))
                  :disabled disabled?
                  :default-value description})]]])]]))))
 
@@ -716,7 +728,7 @@
           [:div.flex.flex-1
            (if (and (:class-schema? opts) (:page-configure? opts))
              [:div.property-description.text-sm.opacity-70
-              (inline-text {} :markdown (get-in property [:block/schema :description]))]
+              (inline-text {} :markdown (db-property/property-value-content (:logseq.property/description property)))]
              [:div.property-value.flex.flex-1
               (pv/property-value block property v opts)])]]]))))
 

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

@@ -63,7 +63,7 @@
            (let [block (second (:rum/args state))
                  value (or (str (db-property/closed-value-content block)) "")
                  icon (:logseq.property/icon block)
-                 description (or (get-in block [:block/schema :description]) "")]
+                 description (or (db-property/property-value-content (:logseq.property/description block)) "")]
              (assoc state
                     ::value (atom value)
                     ::icon (atom icon)

+ 16 - 8
src/main/frontend/components/property/value.cljs

@@ -70,14 +70,16 @@
           (if (and (= :default (get-in property [:block/schema :type]))
                    (not (db-property/many? property)))
             (p/let [existing-value (get block (:db/ident property))
-                    new-block-id (when-not existing-value (db/new-block-id))
-                    _ (when-not existing-value
+                    existing-value? (and (some? existing-value)
+                                         (not= (:db/ident existing-value) :logseq.property/empty-placeholder))
+                    new-block-id (when-not existing-value? (db/new-block-id))
+                    _ (when-not existing-value?
                         (db-property-handler/create-property-text-block!
                          (:db/id block)
                          (:db/id property)
                          value
                          {:new-block-id new-block-id}))]
-              (or existing-value (db/entity [:block/uuid new-block-id])))
+              (if existing-value? existing-value (db/entity [:block/uuid new-block-id])))
             (p/let [new-block-id (db/new-block-id)
                     _ (db-property-handler/create-property-text-block!
                        (:db/id block)
@@ -349,7 +351,8 @@
            (let [result (rum/react *result)]
              (if (empty? result)
                (let [v (get block (:db/ident property))]
-                 (if (every? de/entity? v) v [v]))
+                 (remove #(= :logseq.property/empty-placeholder (:db/ident %))
+                         (if (every? de/entity? v) v [v])))
                (remove (fn [node]
                          (or (= (:db/id block) (:db/id node))
                               ;; A page's alias can't be itself
@@ -535,8 +538,11 @@
   [block property value-block opts]
   (let [multiple-values? (db-property/many? property)
         block-container (state/get-component :block/container)
-        blocks-container (state/get-component :block/blocks-container)]
-    (if value-block
+        blocks-container (state/get-component :block/blocks-container)
+        value-block (if (and (coll? value-block) (every? de/entity? value-block))
+                      (set (remove #(= (:db/ident %) :logseq.property/empty-placeholder) value-block))
+                      value-block)]
+    (if (seq value-block)
       [:div.property-block-container.content.w-full
        (let [config {:id (str (if multiple-values?
                                 (:block/uuid block)
@@ -818,7 +824,9 @@
               (when date?
                 [(property-value-date-picker block property nil {:toggle-fn toggle-fn})]))
              (when-not editing?
-               (property-empty-text-value))))]))))
+               (if date?
+                 [(property-empty-text-value) (property-value-date-picker block property nil {:toggle-fn toggle-fn})]
+                 (property-empty-text-value)))))]))))
 
 (rum/defc multiple-values < rum/reactive db-mixins/query
   [block property opts schema]
@@ -845,7 +853,6 @@
          schema (:block/schema property)
          type (some-> schema (get :type :default))
          multiple-values? (db-property/many? property)
-         empty-value? (= :logseq.property/empty-placeholder v)
          v (cond
              (and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
              v
@@ -855,6 +862,7 @@
              (first v)
              :else
              v)
+         empty-value? (= :logseq.property/empty-placeholder (:db/ident (first v)))
          closed-values? (seq (:property/closed-values property))
          value-cp [:div.property-value-inner
                    {:data-type type

+ 4 - 17
src/main/frontend/components/repo.css

@@ -16,27 +16,15 @@
 
 .ui__dropdown-menu-content {
   &.repos-list {
-    @apply px-2 pb-[210px] relative overflow-hidden;
-    @apply min-w-[280px] sm:max-w-[320px] max-h-[66vh];
-
-    &.no-repos {
-      @apply pb-[180px];
-    }
-
-    &[data-mode=db] {
-      @apply pb-[114px];
-
-      &.no-repos {
-        @apply pb-[109px];
-      }
-    }
+    @apply px-2 relative overflow-hidden;
+    @apply min-w-[280px] sm:max-w-[320px];
 
     .ui__dropdown-menu-item {
       @apply overflow-hidden overflow-ellipsis;
     }
 
     .cp__repos-list-wrap {
-      @apply max-h-96 overflow-scroll m-[-8px] px-2 pb-6;
+      @apply max-h-80 overflow-scroll mx-[-8px] px-2 pb-2;
     }
   }
 }
@@ -64,8 +52,7 @@
 }
 
 .cp__repos-quick-actions {
-  @apply absolute left-[1px] right-[1px] bottom-[1px] bg-gray-01 px-2 py-3 border-t
-  flex flex-col rounded-b overflow-hidden;
+  @apply -mx-2 bg-gray-01 px-2 pb-1.5 pt-3 border-t flex flex-col rounded-b overflow-hidden;
 
   .ui__button {
     @apply w-full !py-4 !justify-start opacity-70 font-medium hover:opacity-90

+ 9 - 9
src/main/frontend/components/views.cljs

@@ -95,7 +95,7 @@
   [block property]
   (let [type (get-in property [:block/schema :type])
         many? (= :db.cardinality/many (get property :db/cardinality))
-        ref-types (into db-property-type/value-ref-property-types #{:entity})
+        ref-types (into db-property-type/ref-property-types #{:entity})
         number-type? (= :number type)
         v (get block (:db/ident property))
         v' (if many? v [v])
@@ -871,13 +871,13 @@
      (fn [sorting]
        (set-sorting! sorting)
        (p/let [entity (or entity (create-view!))]
-         (property-handler/set-block-property! repo (:db/id entity) :logseq.property/table-sorting sorting)))
+         (property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/sorting sorting)))
      :set-filters!
      (fn [filters]
        (let [filters (table-filters->persist-state filters)]
          (set-filters! filters)
          (p/let [entity (or entity (create-view!))]
-           (property-handler/set-block-property! repo (:db/id entity) :logseq.property/table-filters filters))))
+           (property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/filters filters))))
      :set-visible-columns!
      (fn [columns]
        (let [hidden-columns (vec (keep (fn [[column visible?]]
@@ -885,24 +885,24 @@
                                            column)) columns))]
          (set-visible-columns! columns)
          (p/let [entity (or entity (create-view!))]
-           (property-handler/set-block-property! repo (:db/id entity) :logseq.property/table-hidden-columns hidden-columns))))
+           (property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/hidden-columns hidden-columns))))
      :set-ordered-columns!
      (fn [ordered-columns]
        (let [ids (vec (remove #{:select} ordered-columns))]
          (set-ordered-columns! ordered-columns)
          (p/let [entity (or entity (create-view!))]
-           (property-handler/set-block-property! repo (:db/id entity) :logseq.property/table-ordered-columns ids))))}))
+           (property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/ordered-columns ids))))}))
 
 (rum/defc view-inner < rum/static
   [view-entity {:keys [data set-data! columns add-new-object! create-view! title-key] :as option}]
   (let [[input set-input!] (rum/use-state "")
-        sorting (:logseq.property/table-sorting view-entity)
+        sorting (:logseq.property.table/sorting view-entity)
         [sorting set-sorting!] (rum/use-state (or sorting [{:id :block/updated-at, :asc? false}]))
-        filters (:logseq.property/table-filters view-entity)
+        filters (:logseq.property.table/filters view-entity)
         [filters set-filters!] (rum/use-state (or filters []))
-        hidden-columns (:logseq.property/table-hidden-columns view-entity)
+        hidden-columns (:logseq.property.table/hidden-columns view-entity)
         [visible-columns set-visible-columns!] (rum/use-state (zipmap hidden-columns (repeat false)))
-        ordered-columns (vec (concat [:select] (:logseq.property/table-ordered-columns view-entity)))
+        ordered-columns (vec (concat [:select] (:logseq.property.table/ordered-columns view-entity)))
         [ordered-columns set-ordered-columns!] (rum/use-state ordered-columns)
         {:keys [set-sorting! set-filters! set-visible-columns! set-ordered-columns!]}
         (db-set-table-state! view-entity {:set-sorting! set-sorting!

+ 9 - 0
src/main/frontend/db/async.cljs

@@ -253,6 +253,15 @@
         [?page :block/name]]
       tag-id))
 
+(defn <get-property-objects
+  [graph property-ident]
+  (<q graph {:transact-db? true}
+      '[:find [(pull ?b [*]) ...]
+        :in $ ?property-ident
+        :where
+        [?b ?property-ident]]
+      property-ident))
+
 (defn <get-tag-objects
   [graph class-id]
   (let [class-children (db-model/get-class-children graph class-id)

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

@@ -797,6 +797,15 @@ independent of format as format specific heading characters are stripped"
                  nil)
         react)))
 
+(defn get-property-related-objects
+  [repo property-id]
+  (when-let [property (db-utils/entity repo property-id)]
+    (->> (d/q '[:find [?objects ...]
+                :in $ ?prop
+                :where [?objects ?prop]]
+              (conn/get-db repo)
+              (:db/ident property))
+         (map #(db-utils/entity repo %)))))
 
 (defn get-all-namespace-relation
   [repo]

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

@@ -268,7 +268,7 @@
             (subs v' 1)
             (or (page-ref/get-page-name v') v'))
           ;; Convert number pages to string
-          (and (double? v) (= :page (get-in (db-utils/entity k) [:block/schema :type])))
+          (and (double? v) (= :node (get-in (db-utils/entity k) [:block/schema :type])))
           (str v)
           :else
           v')))

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

@@ -1535,7 +1535,7 @@
       (path/get-relative-path current-file-fpath file-path))
     file-path))
 
-(defn upload-asset
+(defn upload-asset!
   "Paste asset and insert link to current editing block"
   [id ^js files format uploading? drop-or-paste?]
   (let [repo (state/get-current-repo)]

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

@@ -56,7 +56,6 @@
             [frontend.handler.user :as user-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.db-based.property.util :as db-pu]
-            [frontend.handler.file-based.property.util :as property-util]
             [frontend.handler.property :as property-handler]
             [frontend.handler.file-based.nfs :as nfs-handler]
             [frontend.handler.code :as code-handler]
@@ -846,19 +845,6 @@
 (defmethod handle :graph/save-db-to-disk [[_ _opts]]
   (persist-db/export-current-graph! {:succ-notification? true}))
 
-(defmethod handle :search/transact-data [[_ repo data]]
-  (let [file-based? (config/local-file-based-graph? repo)
-        data' (cond-> data
-                file-based?
-                ;; remove built-in properties from content
-                (update :blocks-to-add
-                  (fn [blocks]
-                    (map #(update % :content
-                            (fn [content]
-                              (property-util/remove-built-in-properties (get % :format :markdown) content)))
-                      blocks))))]
-    (search/transact-blocks! repo data')))
-
 (defmethod handle :class/configure [[_ page]]
   (shui/dialog-open!
     #(vector :<>

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

@@ -44,7 +44,8 @@
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.handler.property.util :as pu]
-            [datascript.impl.entity :as de]))
+            [datascript.impl.entity :as de]
+            [logseq.db.frontend.property :as db-property]))
 
 (def <create! page-common-handler/<create!)
 (def <delete! page-common-handler/<delete!)
@@ -495,4 +496,5 @@
   (property-handler/set-block-property! (state/get-current-repo)
                                         (:block/uuid page-entity)
                                         :logseq.property/hide-properties?
-                                        (not (:logseq.property/hide-properties? page-entity))))
+                                        (not
+                                         (db-property/property-value-content (:logseq.property/hide-properties? page-entity)))))

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

@@ -220,7 +220,7 @@
           files (.-files clipboard-data)]
       (when-let [file (first files)]
         (when-let [block (state/get-edit-block)]
-          (editor-handler/upload-asset id #js[file] (:block/format block)
+          (editor-handler/upload-asset! id #js[file] (:block/format block)
                                        editor-handler/*asset-uploading? true)))
       (util/stop e))))
 

+ 9 - 1
src/main/frontend/handler/plugin.cljs

@@ -252,10 +252,18 @@
 
     (js/window.apis.addListener channel listener)))
 
+(defn- normalize-plugin-metadata
+  [metadata]
+  (cond-> metadata
+    (not (string? (:author metadata)))
+    (assoc :author (or (get-in metadata [:author :name]) ""))))
+
 (defn register-plugin
   [plugin-metadata]
   (when-let [pid (keyword (:id plugin-metadata))]
-    (swap! state/state update-in [:plugin/installed-plugins] assoc pid plugin-metadata)))
+    (some->> plugin-metadata
+      (normalize-plugin-metadata)
+      (swap! state/state update-in [:plugin/installed-plugins] assoc pid))))
 
 (defn host-mounted!
   []

+ 0 - 5
src/main/frontend/search.cljs

@@ -106,11 +106,6 @@
   (when-let [engine (get-engine repo)]
     (protocol/remove-db! engine)))
 
-(defn transact-blocks!
-  [repo data]
-  (when-let [engine (get-engine repo)]
-    (protocol/transact-blocks! engine data)))
-
 (defn get-unlinked-refs
   "Get matched result from search first, and then filter by worker db"
   [eid]

+ 22 - 1
src/main/frontend/worker/db/migrate.cljs

@@ -43,6 +43,26 @@
   [{:db/id (:db/id (d/entity @conn :logseq.class/task))
     :db/ident :logseq.class/Task}])
 
+(defn- update-table-properties
+  [conn _search-db]
+  (let [old-new-props {:logseq.property/table-sorting :logseq.property.table/sorting
+                       :logseq.property/table-filters :logseq.property.table/filters
+                       :logseq.property/table-ordered-columns :logseq.property.table/ordered-columns
+                       :logseq.property/table-hidden-columns :logseq.property.table/hidden-columns}
+        props-tx (mapv (fn [[old new]]
+                         {:db/id (:db/id (d/entity @conn old))
+                          :db/ident new})
+                       old-new-props)]
+    ;; Property changes need to be in their own tx for subsequent uses of properties to take effect
+    (ldb/transact! conn props-tx {:db-migrate? true})
+
+    (mapcat (fn [[old new]]
+              (->> (d/q '[:find ?b ?prop-v :in $ ?prop :where [?b ?prop ?prop-v]] @conn old)
+                   (mapcat (fn [[id prop-value]]
+                             [[:db/retract id old]
+                              [:db/add id new prop-value]]))))
+            old-new-props)))
+
 (def schema-version->updates
   [[3 {:properties [:logseq.property/table-sorting :logseq.property/table-filters
                     :logseq.property/table-hidden-columns :logseq.property/table-ordered-columns]
@@ -60,7 +80,8 @@
    [6 {:properties [:logseq.property.asset/remote-metadata]}]
    [7 {:fix replace-original-name-content-with-title}]
    [8 {:fix replace-object-and-page-type-with-node}]
-   [9 {:fix update-task-ident}]])
+   [9 {:fix update-task-ident}]
+   [10 {:fix update-table-properties}]])
 
 (let [max-schema-version (apply max (map first schema-version->updates))]
   (assert (<= db-schema/version max-schema-version))

+ 7 - 6
src/main/frontend/worker/rtc/client.cljs

@@ -306,12 +306,13 @@
 
 (defn- rollback
   [repo block-ops-map-coll]
-  (let [ops (map (fn [m]
-                   (keep (fn [[k op]]
-                           (when (not= :block/uuid k)
-                             op))
-                         m))
-                 block-ops-map-coll)]
+  (let [ops (mapcat
+             (fn [m]
+               (keep (fn [[k op]]
+                       (when (not= :block/uuid k)
+                         op))
+                     m))
+             block-ops-map-coll)]
     (client-op/add-ops repo ops)
     nil))
 

+ 1 - 1
src/main/frontend/worker/rtc/client_op.cljs

@@ -85,7 +85,7 @@
     (if (> t1 t2)
       (merge-update-ops update-op2 update-op1)
       (let [{av-coll1 :av-coll block-uuid :block-uuid} (last update-op1)
-            {av-coll2 :av-coll} (last update-op1)]
+            {av-coll2 :av-coll} (last update-op2)]
         [:update t2
          {:block-uuid block-uuid
           :av-coll (concat av-coll1 av-coll2)}]))))

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

@@ -26,7 +26,7 @@
 ;;         _ (test-helper/create-page! "page2" opts)
 ;;         p1id (:block/uuid (db/get-page "page1"))
 ;;         p2id (:block/uuid (db/get-page "page2"))]
-;;     (outliner-property/upsert-property! repo "property-1" {:type :page} {})
+;;     (outliner-property/upsert-property! repo "property-1" {:type :node} {})
 ;;     (outliner-property/set-block-property! repo fbid "property-1" p1id {})
 ;;     (outliner-property/set-block-property! repo sbid "property-1" p2id {})
 ;;     (is (= '("[[page1]]" "[[page2]]") (model/get-db-property-values repo "property-1")))))
@@ -63,7 +63,7 @@
         class1 (db/get-case-page "class1")
         class2 (db/get-case-page "class2")
         conn (db/get-db false)]
-    (outliner-property/upsert-property! conn :user.property/property-1 {:type :page} {})
+    (outliner-property/upsert-property! conn :user.property/property-1 {:type :node} {})
     (outliner-property/class-add-property! conn (:db/id class1) :user.property/property-1)
     (outliner-property/class-add-property! conn (:db/id class2) :user.property/property-1)
     (let [property (db/entity :user.property/property-1)

+ 12 - 12
src/test/frontend/db/query_dsl_test.cljs

@@ -223,14 +223,14 @@ prop-d:: [[nada]]"}])
       "Page property queries ORed")
 
   (is (= ["page1" "page3"]
-           (map :block/name
-                (dsl-query "(and (page-property parent [[child page 1]]) (or (page-property foo baz) (page-property parent [[child page 2]])))"))))
+         (map :block/name
+              (dsl-query "(and (page-property parent [[child page 1]]) (or (page-property foo baz) (page-property parent [[child page 2]])))"))))
 
   (is (= ["page4"]
-           (map
-            :block/name
-            (dsl-query "(and (page-property parent [[child page 2]]) (not (page-property foo bar)))")))
-        "Page property queries nested NOT in second clause")
+         (map
+          :block/name
+          (dsl-query "(and (page-property parent [[child page 2]]) (not (page-property foo bar)))")))
+      "Page property queries nested NOT in second clause")
 
   (is (= ["page4"]
          (map
@@ -239,13 +239,13 @@ prop-d:: [[nada]]"}])
       "Page property queries nested NOT in first clause")
 
   (testing "boolean values"
-      (is (= ["page1"]
-             (map :block/name (dsl-query "(page-property interesting true)")))
-          "Boolean true")
+    (is (= ["page1"]
+           (map :block/name (dsl-query "(page-property interesting true)")))
+        "Boolean true")
 
-      (is (= ["page2" "page3"]
-             (map :block/name (dsl-query "(page-property interesting false)")))
-          "Boolean false")))
+    (is (= #{"page2" "page3"}
+           (set (map :block/name (dsl-query "(page-property interesting false)"))))
+        "Boolean false")))
 
 (deftest page-property-queries
   (testing "page property tests with default config"

+ 1 - 1
src/test/frontend/handler/paste_test.cljs

@@ -241,7 +241,7 @@
       reset
       [state/preferred-pasting-file? (constantly true)
        ;; paste-file-if-exists mocks below
-       editor-handler/upload-asset (fn [_id file & _]
+       editor-handler/upload-asset! (fn [_id file & _]
                                      (reset! pasted-file file))
        util/stop (constantly nil)
        state/get-edit-block (constantly {})]

+ 22 - 18
src/test/logseq/api_test.cljs

@@ -12,28 +12,32 @@
 (deftest get-block
   (with-redefs [state/get-current-repo (constantly test-helper/test-db)]
     (db/transact! test-helper/test-db
-                  [{:db/id 10000
-                    :block/uuid #uuid "4406f839-6410-43b5-87db-25e9b8f54cc0"
-                    :block/title "1"}
-                   {:db/id 10001
-                    :block/uuid #uuid "d9b7b45f-267f-4794-9569-f43d1ce77172"
-                    :block/title "2"}
-                   {:db/id 10002
-                    :block/uuid #uuid "adae3006-f03e-4814-a1f5-f17f15b86556"
-                    :block/parent 10001
-                    :block/title "3"}
-                   {:db/id 10003
-                    :block/uuid #uuid "0c3053c3-2dab-4769-badd-14ce16d8ba8d"
-                    :block/parent 10002
-                    :block/title "4"}])
+      [{:db/id 10000
+        :block/uuid #uuid "4406f839-6410-43b5-87db-25e9b8f54cc0"
+        :block/title "1"}
+       {:db/id 10001
+        :block/uuid #uuid "d9b7b45f-267f-4794-9569-f43d1ce77172"
+        :block/title "2"}
+       {:db/id 10002
+        :block/uuid #uuid "adae3006-f03e-4814-a1f5-f17f15b86556"
+        :block/parent 10001
+        :block/title "3"}
+       {:db/id 10003
+        :block/uuid #uuid "0c3053c3-2dab-4769-badd-14ce16d8ba8d"
+        :block/parent 10002
+        :block/title "4"}])
 
     (is (= (:title (bean/->clj (api-block/get_block 10000 #js {}))) "1"))
     (is (= (:title (bean/->clj (api-block/get_block "d9b7b45f-267f-4794-9569-f43d1ce77172" #js {}))) "2"))
     (is (= (:title (bean/->clj (api-block/get_block #uuid "d9b7b45f-267f-4794-9569-f43d1ce77172" #js {}))) "2"))
     (is (= {:id 10001, :title "2", :uuid "d9b7b45f-267f-4794-9569-f43d1ce77172", :children [["uuid" "adae3006-f03e-4814-a1f5-f17f15b86556"]]}
-           (select-keys (js->clj (api-block/get_block 10001 #js {:includeChildren false}) :keywordize-keys true)
-                        [:id :title :uuid :children])))
-    (is (= {:title "2", :uuid "d9b7b45f-267f-4794-9569-f43d1ce77172", :id 10001, :children [{:title "3", :parent {:id 10001}, :uuid "adae3006-f03e-4814-a1f5-f17f15b86556", :id 10002, :level 1, :children [{:title "4", :parent {:id 10002}, :uuid "0c3053c3-2dab-4769-badd-14ce16d8ba8d", :id 10003, :level 2, :children []}]}]}
-           (js->clj (api-block/get_block 10001 #js {:includeChildren true}) :keywordize-keys true)))))
+          (select-keys (js->clj (api-block/get_block 10001 #js {:includeChildren false}) :keywordize-keys true)
+            [:id :title :uuid :children])))
+    ;; NOTE: `content` key is to be compatible with old APIs
+    (is (= {:id 10001, :title "2", :content "2" :uuid "d9b7b45f-267f-4794-9569-f43d1ce77172"
+            :children [{:id 10002 :title "3", :content "3"
+                        :parent {:id 10001}, :uuid "adae3006-f03e-4814-a1f5-f17f15b86556", :level 1,
+                        :children [{:id 10003, :title "4", :content "4" :parent {:id 10002}, :uuid "0c3053c3-2dab-4769-badd-14ce16d8ba8d", :level 2, :children []}]}]}
+          (js->clj (api-block/get_block 10001 #js {:includeChildren true}) :keywordize-keys true)))))
 
 #_(cljs.test/run-tests)

+ 4 - 4
yarn.lock

@@ -5070,10 +5070,10 @@ mkdirp@^1.0.3:
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 
-mldoc@^1.5.8:
-  version "1.5.8"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.8.tgz#5433950c9124f8917075bafa4f1ed886959504c5"
-  integrity sha512-wNUGgwdgB/5c3GTp2qRPzWD8CpeTmA/HDlGJjTJUo2WXJttiBaz8cHQh2OdAshiLdVxteqIaSTXSPFRf7z1YRw==
+mldoc@^1.5.9:
+  version "1.5.9"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.5.9.tgz#43d740351c64285f0f4988ac9497922d54ae66fc"
+  integrity sha512-87FQ7hseS87tsk+VdpIigpu8LH+GwmbbFgpxgFwvnbH5oOjmIrc47laH4Dyggzqiy8/vMjDHkl7vsId0eXhCDQ==
   dependencies:
     yargs "^12.0.2"