浏览代码

Merge branch 'master' into feat/capacitor-new

Tienson Qin 3 月之前
父节点
当前提交
13305e3169
共有 28 个文件被更改,包括 217 次插入128 次删除
  1. 23 1
      clj-e2e/test/logseq/e2e/outliner_basic_test.clj
  2. 1 1
      deps/db/src/logseq/db.cljs
  3. 11 10
      deps/db/src/logseq/db/common/initial_data.cljs
  4. 20 2
      deps/db/src/logseq/db/frontend/class.cljs
  5. 2 14
      deps/db/src/logseq/db/frontend/db.cljs
  6. 1 1
      deps/db/src/logseq/db/frontend/malli_schema.cljs
  7. 1 1
      deps/db/src/logseq/db/frontend/property.cljs
  8. 1 1
      deps/db/src/logseq/db/frontend/schema.cljs
  9. 18 10
      deps/db/src/logseq/db/sqlite/build.cljs
  10. 4 4
      deps/db/src/logseq/db/sqlite/export.cljs
  11. 4 4
      deps/db/test/logseq/db/sqlite/export_test.cljs
  12. 2 2
      deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs
  13. 3 1
      deps/outliner/src/logseq/outliner/core.cljs
  14. 15 4
      deps/outliner/src/logseq/outliner/property.cljs
  15. 19 3
      deps/outliner/src/logseq/outliner/validate.cljs
  16. 18 0
      deps/outliner/test/logseq/outliner/property_test.cljs
  17. 6 4
      deps/outliner/test/logseq/outliner/validate_test.cljs
  18. 2 4
      scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs
  19. 3 4
      src/main/frontend/components/page.cljs
  20. 6 0
      src/main/frontend/components/page.css
  21. 15 36
      src/main/frontend/components/property/value.cljs
  22. 1 1
      src/main/frontend/handler/common/page.cljs
  23. 11 8
      src/main/frontend/handler/editor.cljs
  24. 14 6
      src/main/frontend/state.cljs
  25. 7 1
      src/main/frontend/worker/db/migrate.cljs
  26. 7 3
      src/main/frontend/worker/db_worker.cljs
  27. 1 1
      src/test/frontend/db/query_dsl_test.cljs
  28. 1 1
      src/test/frontend/worker/handler/page/db_based/page_test.cljs

+ 23 - 1
clj-e2e/test/logseq/e2e/outliner_basic_test.clj

@@ -4,8 +4,10 @@
    [logseq.e2e.block :as b]
    [logseq.e2e.fixtures :as fixtures]
    [logseq.e2e.keyboard :as k]
+   [logseq.e2e.page :as p]
    [logseq.e2e.util :as util]
-   [wally.main :as w]))
+   [wally.main :as w]
+   [wally.repl :as repl]))
 
 (use-fixtures :once fixtures/open-page)
 (use-fixtures :each
@@ -44,6 +46,23 @@
     (let [[x2 x3 x4 x5] (map (comp first util/bounding-xy #(w/find-one-by-text "span" %)) ["b2" "b3" "b4" "b5"])]
       (is (and (= x2 x4) (= x3 x5) (< x2 x3))))))
 
+(defn indent-outdent-embed-page []
+  (p/new-page "Page embed")
+  (b/new-blocks ["b1" "b2"])
+  (p/new-page "Page testing")
+  (b/new-blocks ["b3" ""])
+  (util/input-command "Node embed")
+  (util/press-seq "Page embed" {:delay 60})
+  (k/press "Enter" {:delay 60})
+  (util/exit-edit)
+  (b/new-blocks ["b4"])
+  (b/outdent)
+  (b/indent)
+  (util/exit-edit)
+  (let [[x2 x3 x4] (map (comp first util/bounding-xy #(w/find-one-by-text "span" %)) ["b2" "b3" "b4"])]
+    (is (= x2 x4))
+    (is (< x3 x2))))
+
 (defn move-up-down []
   (b/new-blocks ["b1" "b2" "b3" "b4"])
   (util/repeat-keyboard 2 "Shift+ArrowUp")
@@ -90,6 +109,9 @@
 (deftest indent-and-outdent-test
   (indent-and-outdent))
 
+(deftest indent-outdent-embed-page-test
+  (indent-outdent-embed-page))
+
 (deftest move-up-down-test
   (move-up-down))
 

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

@@ -522,7 +522,7 @@
   (when db (get-key-value db :logseq.kv/remote-schema-version)))
 
 (def get-all-properties db-db/get-all-properties)
-(def get-class-extends db-db/get-class-extends)
+(def get-class-extends db-class/get-class-extends)
 (def get-classes-parents db-db/get-classes-parents)
 (def get-title-with-parents db-db/get-title-with-parents)
 (def class-instance? db-db/class-instance?)

+ 11 - 10
deps/db/src/logseq/db/common/initial_data.cljs

@@ -186,18 +186,19 @@
        (= (:db/id ref-block) id)
        (= id (:db/id (:block/page ref-block)))))))
 
+(defn get-block-refs
+  [db id]
+  (let [with-alias (->> (get-block-alias db id)
+                        (cons id)
+                        distinct)]
+    (some->> with-alias
+             (map #(d/entity db %))
+             (mapcat :block/_refs)
+             (remove (fn [ref-block] (hidden-ref? db ref-block id))))))
+
 (defn get-block-refs-count
   [db id]
-  (or
-   (let [with-alias (->> (get-block-alias db id)
-                         (cons id)
-                         distinct)]
-     (some->> with-alias
-              (map #(d/entity db %))
-              (mapcat :block/_refs)
-              (remove (fn [ref-block] (hidden-ref? db ref-block id)))
-              count))
-   0))
+  (count (get-block-refs db id)))
 
 (defn ^:large-vars/cleanup-todo get-block-and-children
   [db id-or-page-name {:keys [children? properties include-collapsed-children?]

+ 20 - 2
deps/db/src/logseq/db/frontend/class.cljs

@@ -3,6 +3,7 @@
   (:require [clojure.set :as set]
             [clojure.string :as string]
             [datascript.core :as d]
+            [datascript.impl.entity :as de]
             [flatland.ordered.map :refer [ordered-map]]
             [logseq.common.defkeywords :refer [defkeywords]]
             [logseq.db.frontend.db-ident :as db-ident]
@@ -118,7 +119,10 @@
   "Built-in classes that are hidden in a few contexts like property values"
   #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset})
 
+;; Helper fns
+;; ==========
 (defn get-structured-children
+  "Returns all children of a class"
   [db eid]
   (->>
    (d/q '[:find [?c ...]
@@ -130,8 +134,22 @@
         (:class-extends rules/rules))
    (remove #{eid})))
 
-;; Helper fns
-;; ==========
+(defn get-class-extends
+  "Returns all parents of a class"
+  [node]
+  (assert (de/entity? node) "get-class-extends `node` should be an entity")
+  (let [db (.-db node)
+        eid (:db/id node)]
+    (->>
+     (d/q '[:find [?p ...]
+            :in $ ?c %
+            :where
+            (class-extends ?p ?c)]
+          db
+          eid
+          (:class-extends rules/rules))
+     (remove #{eid})
+     (map (fn [id] (d/entity db id))))))
 
 (defn create-user-class-ident-from-name
   "Creates a class :db/ident for a default user namespace.

+ 2 - 14
deps/db/src/logseq/db/frontend/db.cljs

@@ -43,18 +43,6 @@
   (->> (d/datoms db :avet :block/tags :logseq.class/Property)
        (map (fn [d] (d/entity db (:e d))))))
 
-(defn get-class-extends
-  "Returns all parents of a class"
-  [node]
-  (when-let [parent (:logseq.property.class/extends node)]
-    (loop [current-parent parent
-           parents' []]
-      (if (and current-parent
-               (not (contains? parents' current-parent)))
-        (recur (:logseq.property.class/extends current-parent)
-               (conj parents' current-parent))
-        (vec (reverse parents'))))))
-
 (defn get-page-parents
   [node]
   (when-let [parent (:block/parent node)]
@@ -68,7 +56,7 @@
 
 (defn- get-class-title-with-extends
   [entity]
-  (let [parents' (->> (get-class-extends entity)
+  (let [parents' (->> (db-class/get-class-extends entity)
                       (remove (fn [e] (= :logseq.class/Root (:db/ident e))))
                       vec)]
     (string/join
@@ -96,7 +84,7 @@
   "Returns all parents of all classes. Like get-class-extends but for multiple classes"
   [tags]
   (let [tags' (filter entity-util/class? tags)
-        result (mapcat get-class-extends tags')]
+        result (mapcat db-class/get-class-extends tags')]
     (set result)))
 
 (defn class-instance?

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

@@ -301,7 +301,7 @@
     (concat
      [:map
       [:db/ident class-ident]
-      [:logseq.property.class/extends :int]]
+      [:logseq.property.class/extends [:set :int]]]
      page-attrs
      page-or-block-attrs))
    (vec

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

@@ -158,9 +158,9 @@
                                                      :public? false
                                                      :hide? true
                                                      :view-context :property}}
-     ;; TODO: support cardinality many for extends
      :logseq.property.class/extends {:title "Extends"
                                      :schema {:type :class
+                                              :cardinality :many
                                               :public? true
                                               :view-context :class}
                                      :queryable? true

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

@@ -37,7 +37,7 @@
          (map (juxt :major :minor)
               [(parse-schema-version x) (parse-schema-version y)])))
 
-(def version (parse-schema-version "65.5"))
+(def version (parse-schema-version "65.6"))
 
 (defn major-version
   "Return a number.

+ 18 - 10
deps/db/src/logseq/db/sqlite/build.cljs

@@ -247,6 +247,18 @@
                                    properties'))]
     new-properties-tx))
 
+(defn- build-class-extends [{:build/keys [class-parent class-extends]} class-db-ids]
+  (when-let [class-extends' (if class-parent
+                              (do (println "Warning: :build/class-parent is deprecated and will be removed soon.")
+                                  [class-parent])
+                              class-extends)]
+    (mapv (fn [c]
+            (or (class-db-ids c)
+                (if (db-malli-schema/class? c)
+                  c
+                  (throw (ex-info (str "No :db/id for " c) {})))))
+          class-extends')))
+
 (defn- build-classes-tx [classes properties-config uuid-maps all-idents {:keys [build-existing-tx?] :as options}]
   (let [classes' (if build-existing-tx?
                    (->> classes
@@ -258,7 +270,7 @@
                           (into {}))
         classes-tx (vec
                     (mapcat
-                     (fn [[class-name {:build/keys [class-parent class-properties] :as class-m}]]
+                     (fn [[class-name {:build/keys [class-properties] :as class-m}]]
                        (let [db-ident (get-ident all-idents class-name)
                              new-block
                              (sqlite-util/build-new-class
@@ -277,16 +289,12 @@
                            (conj
                             (merge
                              new-block
-                             (dissoc class-m :build/properties :build/class-parent :build/class-properties :build/keep-uuid?)
+                             (dissoc class-m :build/properties :build/class-extends :build/class-parent :build/class-properties :build/keep-uuid?)
                              (when-let [props (not-empty (:build/properties class-m))]
                                (->block-properties (merge props (db-property-build/build-properties-with-ref-values pvalue-tx-m))
                                                    uuid-maps all-idents options))
-                             (when class-parent
-                               {:logseq.property.class/extends
-                                (or (class-db-ids class-parent)
-                                    (if (db-malli-schema/class? class-parent)
-                                      class-parent
-                                      (throw (ex-info (str "No :db/id for " class-parent) {}))))})
+                             (when-let [class-extends (build-class-extends class-m class-db-ids)]
+                               {:logseq.property.class/extends class-extends})
                              (when class-properties
                                {:logseq.property.class/properties
                                 (mapv #(hash-map :db/ident (get-ident all-idents %))
@@ -343,7 +351,7 @@
    Class
    [:map
     [:build/properties {:optional true} User-properties]
-    [:build/class-parent {:optional true} Class]
+    [:build/class-extends {:optional true} [:vector Class]]
     [:build/class-properties {:optional true} [:vector Property]]
     [:build/keep-uuid? {:optional true} :boolean]]])
 
@@ -788,7 +796,7 @@
      and the values are maps of datascript attributes e.g. `{:block/title \"Foo\"}`.
      Additional keys available:
      * :build/properties - Define properties on a class page
-     * :build/class-parent - Add a class parent by its keyword name
+     * :build/class-extends - Vec of class name keywords which extend a class.
      * :build/class-properties - Vec of property name keywords. Defines properties that a class gives to its objects
      * :build/keep-uuid? - Keeps :block/uuid because another block depends on it
   * :graph-namespace - namespace to use for db-ident creation. Useful when importing an ontology

+ 4 - 4
deps/db/src/logseq/db/sqlite/export.cljs

@@ -190,9 +190,9 @@
     ;; It's caller's responsibility to ensure parent is included in final export
     (and (not shallow-copy?)
          (:logseq.property.class/extends class-ent)
-         (not= :logseq.class/Root (:db/ident (:logseq.property.class/extends class-ent))))
-    (assoc :build/class-parent
-           (:db/ident (:logseq.property.class/extends class-ent)))))
+         (not= [:logseq.class/Root] (mapv :db/ident (:logseq.property.class/extends class-ent))))
+    (assoc :build/class-extends
+           (mapv :db/ident (:logseq.property.class/extends class-ent)))))
 
 (defn- build-node-classes
   [db build-block block-tags properties]
@@ -341,7 +341,7 @@
 
 (defn- build-class-parents-export [db classes-config]
   (let [class-parent-ents (->> classes-config
-                               (filter #(:build/class-parent (val %)))
+                               (filter #(:build/class-extends (val %)))
                                (map #(d/entity db (key %)))
                                db-db/get-classes-parents)
         classes

+ 4 - 4
deps/db/test/logseq/db/sqlite/export_test.cljs

@@ -371,9 +371,9 @@
                       :user.property/node-p1 {:logseq.property/type :default}}
          :classes {:user.class/MyClass {:build/class-properties [:user.property/p1 :user.property/p2]}
                    :user.class/MyClass2 {:build/class-properties [:user.property/p2]}
-                   :user.class/ChildClass {:build/class-parent :user.class/MyClass
+                   :user.class/ChildClass {:build/class-extends [:user.class/MyClass]
                                            :build/class-properties [:user.property/p3]}
-                   :user.class/ChildClass2 {:build/class-parent :user.class/MyClass2}
+                   :user.class/ChildClass2 {:build/class-extends [:user.class/MyClass2]}
                    ;; shallow class b/c it's a property's class property
                    :user.class/NodeClass {:build/class-properties [:user.property/node-p1]}
                    :user.class/NodeClass2 {}}
@@ -486,7 +486,7 @@
                                :build/property-classes [:user.class/MyClass]}}
          :classes
          {:user.class/MyClass {:build/properties {:user.property/url "https://example.com/MyClass"}}
-          :user.class/MyClass2 {:build/class-parent :user.class/MyClass
+          :user.class/MyClass2 {:build/class-extends [:user.class/MyClass]
                                 :build/properties {:logseq.property/description "tests child class"}}}}
         conn (db-test/create-conn-with-blocks original-data)
         conn2 (db-test/create-conn)
@@ -619,7 +619,7 @@
                                 (assoc :block/alias #{[:block/uuid class-alias-uuid]}))
           :user.class/MyClassAlias {:block/uuid class-alias-uuid
                                     :build/keep-uuid? true}
-          :user.class/MyClass2 {:build/class-parent :user.class/MyClass
+          :user.class/MyClass2 {:build/class-extends [:user.class/MyClass]
                                 :block/collapsed? true
                                 :block/uuid class2-uuid
                                 :build/keep-uuid? true

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

@@ -763,9 +763,9 @@
                 set))
         "All classes are correctly defined by :type")
 
-    (is (= "CreativeWork" (get-in (d/entity @conn :user.class/Movie) [:logseq.property.class/extends :block/title]))
+    (is (= ["CreativeWork"] (map :block/title (:logseq.property.class/extends (d/entity @conn :user.class/Movie))))
         "Existing page correctly set as class parent")
-    (is (= "Thing" (get-in (d/entity @conn :user.class/CreativeWork) [:logseq.property.class/extends :block/title]))
+    (is (= ["Thing"] (map :block/title (:logseq.property.class/extends (d/entity @conn :user.class/CreativeWork))))
         "New page correctly set as class parent")))
 
 (deftest-async export-files-with-property-pages-disabled

+ 3 - 1
deps/outliner/src/logseq/outliner/core.cljs

@@ -628,7 +628,9 @@
                                    (get-last-child-or-self db target)
                                    [target false])))
 
-                             (and (= outliner-op :indent-outdent-blocks) (not indent?))
+                             (and (= outliner-op :indent-outdent-blocks)
+                                  (or (not indent?)
+                                      (and indent? sibling?)))
                              [block sibling?]
 
                              (contains? #{:insert-blocks :move-blocks} outliner-op)

+ 15 - 4
deps/outliner/src/logseq/outliner/property.cljs

@@ -61,6 +61,7 @@
           retract-multiple-values? (and multiple-values? (sequential? value))
           multiple-values-empty? (and (sequential? old-value)
                                       (contains? (set (map :db/ident old-value)) :logseq.property/empty-placeholder))
+          extends? (= property-id :logseq.property.class/extends)
           update-block-tx (cond-> (outliner-core/block-with-updated-at {:db/id (:db/id block)})
                             true
                             (assoc property-id value)
@@ -75,6 +76,10 @@
         (conj [:db/retract (:db/id update-block-tx) property-id :logseq.property/empty-placeholder])
         retract-multiple-values?
         (conj [:db/retract (:db/id update-block-tx) property-id])
+        extends?
+        (concat
+         (let [extends (ldb/get-class-extends (d/entity @conn value))]
+           (map (fn [extend] [:db/retract (:db/id block) property-id (:db/id extend)]) extends)))
         true
         (conj update-block-tx)))))
 
@@ -342,6 +347,7 @@
            property (d/entity @conn property-id)
            _ (when (= (:db/ident property) :logseq.property.class/extends)
                (outliner-validate/validate-extends-property
+                @conn
                 (if (number? v) (d/entity @conn v) v)
                 (map #(d/entity @conn %) block-eids)))
            _ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
@@ -388,7 +394,8 @@
 
       (and (ldb/class? block) (= property-id :logseq.property.class/extends))
       (ldb/transact! conn
-                     [[:db/add (:db/id block) :logseq.property.class/extends :logseq.class/Root]]
+                     [[:db/retract (:db/id block) :logseq.property.class/extends]
+                      [:db/add (:db/id block) :logseq.property.class/extends :logseq.class/Root]]
                      {:outliner-op :save-block})
 
       (contains? db-property/db-attribute-properties property-id)
@@ -415,12 +422,16 @@
       (when (= property-id :block/tags)
         (outliner-validate/validate-tags-property @conn [block-eid] v))
       (when (= property-id :logseq.property.class/extends)
-        (outliner-validate/validate-extends-property v [block]))
+        (outliner-validate/validate-extends-property @conn v [block]))
       (cond
         db-attribute?
         (when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself
-          (ldb/transact! conn [{:db/id (:db/id block) property-id v}]
-                         {:outliner-op :save-block}))
+          (let [tx-data (cond->
+                         [{:db/id (:db/id block) property-id v}]
+                          (= property-id :logseq.property.class/extends)
+                          (conj [:db/retract (:db/id block) :logseq.property.class/extends :logseq.class/Root]))]
+            (ldb/transact! conn tx-data
+                           {:outliner-op :save-block})))
         :else
         (let [property (d/entity @conn property-id)
               _ (assert (some? property) (str "Property " property-id " doesn't exist yet"))

+ 19 - 3
deps/outliner/src/logseq/outliner/validate.cljs

@@ -170,10 +170,26 @@
                      :payload {:message "Can't change the parent of a built-in tag"
                                :type :error}}))))
 
+(defn- disallow-extends-cycle
+  [db parent-ent child-ents]
+  (doseq [child child-ents]
+    (let [children-ids (set (cons (:db/id child)
+                                  (db-class/get-structured-children db (:db/id child))))]
+      (when (contains? children-ids (:db/id parent-ent))
+        (throw (ex-info "Extends cycle"
+                        {:type :notification
+                         :payload {:message "Tag extends cycle"
+                                   :type :error
+                                   :blocks (map #(select-keys % [:db/id :block/title]) [child])}}))))))
+
 (defn validate-extends-property
-  [parent-ent child-ents]
-  (disallow-built-in-class-extends-change parent-ent child-ents)
-  (validate-extends-property-have-correct-type parent-ent child-ents))
+  [db parent-ent* child-ents]
+  (let [parent-ent (if (integer? parent-ent*)
+                     (d/entity db parent-ent*)
+                     parent-ent*)]
+    (disallow-extends-cycle db parent-ent child-ents)
+    (disallow-built-in-class-extends-change parent-ent child-ents)
+    (validate-extends-property-have-correct-type parent-ent child-ents)))
 
 (defn- disallow-node-cant-tag-with-built-in-non-tags
   [db _block-eids v]

+ 18 - 0
deps/outliner/test/logseq/outliner/property_test.cljs

@@ -321,3 +321,21 @@
         block (db-test/find-block-by-content @conn "o1")]
     (is (= [:user.property/p1 :user.property/p2 :user.property/p3]
            (map :db/ident (:classes-properties (outliner-property/get-block-classes-properties @conn (:db/id block))))))))
+
+(deftest extends-cycle
+  (testing "Fail when creating a cycle of extends"
+    (let [conn (db-test/create-conn-with-blocks
+                {:classes {:Class1 {}
+                           :Class2 {}
+                           :Class3 {}}})
+          db @conn
+          class1 (d/entity db :user.class/Class1)
+          class2 (d/entity db :user.class/Class2)
+          class3 (d/entity db :user.class/Class3)]
+      (outliner-property/set-block-property! conn (:db/id class1) :logseq.property.class/extends (:db/id class2))
+      (outliner-property/set-block-property! conn (:db/id class2) :logseq.property.class/extends (:db/id class3))
+      (is (thrown-with-msg?
+           js/Error
+           #"Extends cycle"
+           (outliner-property/set-block-property! conn (:db/id class3) :logseq.property.class/extends (:db/id class1)))
+          "Extends cycle"))))

+ 6 - 4
deps/outliner/test/logseq/outliner/validate_test.cljs

@@ -97,10 +97,11 @@
         page1 (db-test/find-page-by-title @conn "page1")
         class1 (db-test/find-page-by-title @conn "Class1")
         class2 (db-test/find-page-by-title @conn "Class2")
-        property (db-test/find-page-by-title @conn "prop1")]
+        property (db-test/find-page-by-title @conn "prop1")
+        db @conn]
 
     (testing "valid parent and child combinations"
-      (is (nil? (outliner-validate/validate-extends-property class1 [class2]))
+      (is (nil? (outliner-validate/validate-extends-property db class1 [class2]))
           "parent class to child class is valid"))
 
     (testing "invalid parent and child combinations"
@@ -108,7 +109,7 @@
            (thrown-with-msg?
             js/Error
             #"Can't extend"
-            (outliner-validate/validate-extends-property parent [child]))
+            (outliner-validate/validate-extends-property db parent [child]))
 
         class1 page1
         page1 class1
@@ -118,7 +119,8 @@
       (is (thrown-with-msg?
            js/Error
            #"Can't change.*built-in"
-           (outliner-validate/validate-extends-property (entity-plus/entity-memoized @conn :logseq.class/Task)
+           (outliner-validate/validate-extends-property db
+                                                        (entity-plus/entity-memoized @conn :logseq.class/Task)
                                                         [(entity-plus/entity-memoized @conn :logseq.class/Cards)]))))))
 
 (deftest validate-tags-property

+ 2 - 4
scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs

@@ -19,8 +19,6 @@
             [clojure.walk :as w]
             [datascript.core :as d]
             [logseq.db.common.sqlite-cli :as sqlite-cli]
-            [logseq.db.frontend.malli-schema :as db-malli-schema]
-            [logseq.db.frontend.property :as db-property]
             [logseq.db.sqlite.export :as sqlite-export]
             [logseq.outliner.cli :as outliner-cli]
             [nbb.classpath :as cp]
@@ -68,7 +66,7 @@
                                  (class-m "rdfs:comment")
                                  (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')))
+      (assoc :build/class-extends [(keyword (strip-schema-prefix parent-class'))])
       (seq properties)
       (assoc :build/class-properties (mapv (comp keyword strip-schema-prefix) properties)))))
 
@@ -241,7 +239,7 @@
                      (map #(vector (keyword (strip-schema-prefix (get % "@id")))
                                    (->class-page % class-to-properties options)))
                      (into {}))]
-    (assert (= ["Thing"] (keep #(when-not (:build/class-parent %)
+    (assert (= ["Thing"] (keep #(when-not (:build/class-extends %)
                                   (:block/title %))
                                (vals classes)))
             "Thing is the only class that doesn't have a schema.org parent class")

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

@@ -110,7 +110,7 @@
     (when page?
       [:div.ls-block.block-add-button.flex-1.flex-col.rounded-sm.cursor-text.transition-opacity.ease-in.duration-100.!py-0
        {:class opacity-class
-        :data-block-id (:db/id block)
+        :parentblockid (:db/id block)
         :ref *ref
         :on-click (fn [e]
                     (util/stop e)
@@ -130,11 +130,10 @@
         :on-mouse-leave #(do
                            (dom/remove-class! (rum/deref *ref) "opacity-50")
                            (dom/remove-class! (rum/deref *ref) "opacity-100"))
-        :on-key-down (fn [e]
+        :on-key-down (fn [^js e]
                        (util/stop e)
                        (when (= "Enter" (util/ekey e))
-                         (state/set-state! :editor/container-id container-id)
-                         (editor-handler/api-insert-new-block! "" (merge config block))))
+                         (-> e (.-target) (.click))))
         :tab-index 0}
        [:div.flex.flex-row
         [:div.flex.items-center {:style {:height 28

+ 6 - 0
src/main/frontend/components/page.css

@@ -239,3 +239,9 @@ html.is-native-ios {
 .ls-page-blocks {
   @apply min-h-[60px] overflow-hidden;
 }
+
+.block-add-button {
+  &.selected {
+    opacity: 1 !important;
+  }
+}

+ 15 - 36
src/main/frontend/components/property/value.cljs

@@ -679,13 +679,19 @@
         property-type (:logseq.property/type property)
         nodes (cond
                 extends-property?
-                (let [;; Disallows cyclic hierarchies
+                (let [extends (->> (mapcat (fn [e] (ldb/get-class-extends e)) (:logseq.property.class/extends block))
+                                   distinct)
+                      ;; Disallows cyclic hierarchies
                       exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
-                                      (conj (:block/uuid block))) ; break cycle
+                                      (conj (:block/uuid block)) ; break cycle
+                                      ;; hide parent extends for existing values
+                                      (set/union (set (map :block/uuid extends))))
                       options (if (ldb/class? block)
                                 (model/get-all-classes repo)
                                 result)
-                      excluded-options (remove (fn [e] (contains? exclude-ids (:block/uuid e))) options)]
+
+                      excluded-options (->> options
+                                            (remove (fn [e] (contains? exclude-ids (:block/uuid e)))))]
                   excluded-options)
 
                 (contains? #{:class :property} property-type)
@@ -779,7 +785,7 @@
                                               "Set alias"
                                               :else
                                               (str "Set " (:block/title property)))
-                 :show-new-when-not-exact-match? (if (or (and extends-property? (contains? (set children-pages) (:db/id block)))
+                 :show-new-when-not-exact-match? (if (or extends-property?
                                                          ;; Don't allow creating private tags
                                                          (and (= :block/tags (:db/ident property))
                                                               (seq (set/intersection (set (map :db/ident classes'))
@@ -875,13 +881,6 @@
     (hooks/use-effect!
      (fn []
        (cond
-         (and extends-property? (not (ldb/class? block))
-              (ldb/internal-page? block))
-         (p/let [result (db-async/<get-tag-pages repo (:db/id (db/entity :logseq.class/Page)))
-                 result' (->> result
-                              (remove ldb/built-in?))]
-           (set-result-and-initial-choices! result'))
-
          extends-property?
          nil
 
@@ -1494,7 +1493,6 @@
                      "Fix it!")]
        (let [empty-value? (when (coll? v) (= :logseq.property/empty-placeholder (:db/ident (first v))))
              closed-values? (seq (:property/closed-values property))
-             property-ident (:db/ident property)
              value-cp [:div.property-value-inner
                        {:data-type type
                         :class (str (when empty-value? "empty-value")
@@ -1507,30 +1505,11 @@
                          (multiple-values block property opts)
 
                          :else
-                         (let [extends? (= property-ident :logseq.property.class/extends)
-                               value-cp (property-scalar-value block property v
-                                                               (merge
-                                                                opts
-                                                                {:editor-id editor-id
-                                                                 :dom-id dom-id}))
-                               page-ancestors (when extends?
-                                                (let [ancestor-pages (loop [parents [block]]
-                                                                       (if-let [parent (:logseq.property.class/extends (last parents))]
-                                                                         (when-not (contains? (set parents) parent)
-                                                                           (recur (conj parents parent)))
-                                                                         parents))]
-                                                  (->> (reverse ancestor-pages)
-                                                       (remove (fn [e] (= (:db/id block) (:db/id e))))
-                                                       butlast)))]
-                           (if (seq page-ancestors)
-                             [:div.flex.flex-1.items-center.gap-1
-                              (interpose [:span.opacity-50.text-sm " > "]
-                                         (concat
-                                          (map (fn [{title :block/title :as ancestor}]
-                                                 [:a.whitespace-nowrap {:on-click #(route-handler/redirect-to-page! (:block/uuid ancestor))} title])
-                                               page-ancestors)
-                                          [value-cp]))]
-                             value-cp)))]]
+                         (property-scalar-value block property v
+                                                (merge
+                                                 opts
+                                                 {:editor-id editor-id
+                                                  :dom-id dom-id})))]]
          (if show-tooltip?
            (shui/tooltip-provider
             (shui/tooltip

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

@@ -83,7 +83,7 @@
                  (js/setTimeout
                   (fn []
                     (when-let [block-add-button (->> (dom/sel ".block-add-button")
-                                                     (filter #(= (str (:db/id page)) (dom/attr % "data-block-id")))
+                                                     (filter #(= (str (:db/id page)) (dom/attr % "parentblockid")))
                                                      first)]
                       (.click block-add-button)))
                   200)))

+ 11 - 8
src/main/frontend/handler/editor.cljs

@@ -2496,7 +2496,8 @@
             :down util/get-next-block-non-collapsed)
         sibling-block (f selected {:up-down? true
                                    :exclude-property? true})]
-    (when (and sibling-block (dom/attr sibling-block "blockid"))
+    (when (and sibling-block
+               (or (dom/attr sibling-block "blockid") (dom/attr sibling-block "parentblockid")))
       (util/scroll-to-block sibling-block)
       (state/exit-editing-and-set-selected-blocks! [sibling-block]))))
 
@@ -3317,13 +3318,15 @@
   (let [selected-blocks (state/get-selection-blocks)
         f (case direction :left first :right last)
         node (some-> selected-blocks f)]
-    (when-let [block-id (some-> node (dom/attr "blockid") uuid)]
-      (util/stop e)
-      (let [block {:block/uuid block-id}
-            left? (= direction :left)
-            opts {:container-id (some-> node (dom/attr "containerid") (parse-long))
-                  :event e}]
-        (edit-block! block (if left? 0 :max) opts)))))
+    (if (some-> node (dom/has-class? "block-add-button"))
+      (.click node)
+      (when-let [block-id (some-> node (dom/attr "blockid") uuid)]
+        (util/stop e)
+        (let [block {:block/uuid block-id}
+              left? (= direction :left)
+              opts {:container-id (some-> node (dom/attr "containerid") (parse-long))
+                    :event e}]
+          (edit-block! block (if left? 0 :max) opts))))))
 
 (defn shortcut-left-right [direction]
   (fn [e]

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

@@ -1206,19 +1206,27 @@ Similar to re-frame subscriptions"
     (async/put! chan [payload d])
     d))
 
+(defn- unselect-node
+  [node]
+  (dom/remove-class! node "selected")
+  (when (dom/has-class? node "ls-table-row")
+    (.blur node)))
+
 (defn- set-selection-blocks-aux!
   [blocks]
   (set-state! :view/selected-blocks nil)
-  (let [selected-ids (set (get-selected-block-ids @(:selection/blocks @state)))
+  (let [selected-blocks @(:selection/blocks @state)
+        selected-ids (set (get-selected-block-ids selected-blocks))
         _ (set-state! :selection/blocks blocks)
         new-ids (set (get-selection-block-ids))
         removed (set/difference selected-ids new-ids)]
     (mark-dom-blocks-as-selected blocks)
-    (doseq [id removed]
-      (doseq [node (dom/sel (util/format "[blockid='%s']" id))]
-        (dom/remove-class! node "selected")
-        (when (dom/has-class? node "ls-table-row")
-          (.blur node))))
+    (if (= (count blocks) 1)
+      (doseq [node selected-blocks]
+        (unselect-node node))
+      (doseq [id removed]
+        (doseq [node (dom/sel (util/format "[blockid='%s']" id))]
+          (unselect-node node))))
     (doseq [node (dom/sel ".block-content[contenteditable=true]")]
       (dom/set-attr! node "contenteditable" "false"))))
 

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

@@ -332,6 +332,11 @@
        [:db/retract id :block/order])
      tag-ids)))
 
+(defn- update-extends-to-cardinality-many
+  [db]
+  (let [extends (d/entity db :logseq.property.class/extends)]
+    [[:db/add (:db/id extends) :db/cardinality :db.cardinality/many]]))
+
 (def schema-version->updates
   "A vec of tuples defining datascript migrations. Each tuple consists of the
    schema version integer and a migration map. A migration map can have keys of :properties, :classes
@@ -341,7 +346,8 @@
    ["65.2" {:fix fix-tag-properties}]
    ["65.3" {:fix add-missing-db-ident-for-tags}]
    ["65.4" {:fix fix-using-properties-as-tags}]
-   ["65.5" {:fix remove-block-order-for-tags}]])
+   ["65.5" {:fix remove-block-order-for-tags}]
+   ["65.6" {:fix update-extends-to-cardinality-many}]])
 
 (let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first)
                                      schema-version->updates)))]

+ 7 - 3
src/main/frontend/worker/db_worker.cljs

@@ -468,9 +468,13 @@
           block (d/entity db id)]
       (if unlinked?
         (p/let [title (string/lower-case (:block/title block))
-                result (search-blocks repo title {:limit 3})]
-          (boolean (some (fn [b] (not= id (:db/id b))) result)))
-        (some? (first (:block/_refs block)))))))
+                result (search-blocks repo title {:limit 100})]
+          (boolean (some (fn [b]
+                           (let [block (d/entity db (:db/id b))]
+                             (and (not= id (:db/id block))
+                                  (not ((set (map :db/id (:block/refs block))) id))
+                                  (string/includes? (string/lower-case (:block/title block)) title)))) result)))
+        (some? (first (common-initial-data/get-block-refs db (:db/id block))))))))
 
 (def-thread-api :thread-api/get-block-parents
   [repo id depth]

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

@@ -280,7 +280,7 @@ prop-d:: [[nada]]"}])
                 {:logseq.property/default-value "Todo"}
                 :build/properties-ref-types {:entity :number}}}
       :classes {:Mytask {:build/class-properties [:status]}
-                :Bug {:build/class-parent :Mytask}}
+                :Bug {:build/class-extends [:Mytask]}}
       :pages-and-blocks
       [{:page {:block/title "page1"}
         :blocks [{:block/title "task1"

+ 1 - 1
src/test/frontend/worker/handler/page/db_based/page_test.cljs

@@ -45,7 +45,7 @@
         (is (= (map :block/uuid (db-db/get-page-parents child-page))
                (map :block/uuid (db-db/get-page-parents child-page2)))
             "Child page with existing parents has correct parents")
-        (is (= ["Root Tag" "c1"] (map :block/title (ldb/get-classes-parents [child-page3])))
+        (is (= #{"Root Tag" "c1"} (set (map :block/title (ldb/get-classes-parents [child-page3]))))
             "Child class with new parent has correct parents")
 
         (worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true})