ソースを参照

Merge branch 'feat/db' into feat/blocks-action-bar

Tienson Qin 11 ヶ月 前
コミット
02a3c407fb

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

@@ -20,7 +20,7 @@
 
 
 (defn path-normalize
 (defn path-normalize
   "Normalize file path (for reading paths from FS, not required by writing)
   "Normalize file path (for reading paths from FS, not required by writing)
-   Keep capitalization senstivity"
+   Keep capitalization sensitivity"
   [s]
   [s]
   (.normalize s "NFC"))
   (.normalize s "NFC"))
 
 

+ 65 - 15
deps/db/src/logseq/db/sqlite/export.cljs

@@ -231,6 +231,21 @@
       (seq classes)
       (seq classes)
       (assoc :classes classes))))
       (assoc :classes classes))))
 
 
+(defn- build-mixed-properties-and-classes-export
+  "Builds an export of properties and classes from a mixed group of nodes that may both"
+  [db ents export-opts]
+  (let [properties
+        (when-let [prop-ids (seq (map :db/ident (filter entity-util/property? ents)))]
+          (build-export-properties db prop-ids export-opts))
+        classes
+        (when-let [class-ents (seq (filter ldb/class? ents))]
+          (->> class-ents
+               (map #(vector (:db/ident %) (build-export-class % export-opts)))
+               (into {})))]
+    (cond-> {}
+      properties (assoc :properties properties)
+      classes (assoc :classes classes))))
+
 (defn- build-content-ref-export
 (defn- build-content-ref-export
   "Builds an export config (and additional info) for refs in the given blocks. All the exported
   "Builds an export config (and additional info) for refs in the given blocks. All the exported
    entities found in block refs include their uuid in order to preserve the relationship to the blocks"
    entities found in block refs include their uuid in order to preserve the relationship to the blocks"
@@ -239,20 +254,13 @@
         blocks (remove :logseq.property/value blocks*)
         blocks (remove :logseq.property/value blocks*)
         content-ref-uuids (set (mapcat (comp db-content/get-matched-ids block-title) blocks))
         content-ref-uuids (set (mapcat (comp db-content/get-matched-ids block-title) blocks))
         content-ref-ents (map #(d/entity db [:block/uuid %]) content-ref-uuids)
         content-ref-ents (map #(d/entity db [:block/uuid %]) content-ref-uuids)
-        content-ref-pages (filter #(or (ldb/internal-page? %) (entity-util/journal? %)) content-ref-ents)
-        content-ref-properties (when-let [prop-ids (seq (map :db/ident (filter ldb/property? content-ref-ents)))]
-                                 (update-vals (build-export-properties db prop-ids {:include-uuid? true :shallow-copy? true})
-                                              #(merge % {:build/keep-uuid? true})))
-        content-ref-classes (when-let [class-ents (seq (filter ldb/class? content-ref-ents))]
-                              (->> class-ents
-                                   (map #(vector (:db/ident %)
-                                                 (assoc (build-export-class % {:include-uuid? true :shallow-copy? true})
-                                                        :build/keep-uuid? true)))
-                                   (into {})))]
+        content-ref-pages (filter #(or (entity-util/internal-page? %) (entity-util/journal? %)) content-ref-ents)
+        {:keys [properties classes]}
+        (build-mixed-properties-and-classes-export db content-ref-ents {:include-uuid? true :shallow-copy? true})]
     {:content-ref-uuids content-ref-uuids
     {:content-ref-uuids content-ref-uuids
      :content-ref-ents content-ref-ents
      :content-ref-ents content-ref-ents
-     :properties content-ref-properties
-     :classes content-ref-classes
+     :properties (update-vals properties #(merge % {:build/keep-uuid? true}))
+     :classes (update-vals classes #(merge % {:build/keep-uuid? true}))
      :pages-and-blocks (mapv #(hash-map :page (merge (shallow-copy-page %)
      :pages-and-blocks (mapv #(hash-map :page (merge (shallow-copy-page %)
                                                      {:block/uuid (:block/uuid %) :build/keep-uuid? true}))
                                                      {:block/uuid (:block/uuid %) :build/keep-uuid? true}))
                              content-ref-pages)}))
                              content-ref-pages)}))
@@ -278,14 +286,14 @@
      :properties properties}))
      :properties properties}))
 
 
 (defn- build-blocks-export
 (defn- build-blocks-export
-  "Given a page's block entities, returns the blocks in a sqlite.build EDN format
+  "Given a vec of block entities, returns the blocks in a sqlite.build EDN format
    and all properties and classes used in these blocks"
    and all properties and classes used in these blocks"
-  [db blocks opts]
+  [db blocks {:keys [include-children?] :or {include-children? true} :as opts}]
   (let [*properties (atom {})
   (let [*properties (atom {})
         *classes (atom {})
         *classes (atom {})
         *pvalue-uuids (atom #{})
         *pvalue-uuids (atom #{})
         id-map (into {} (map (juxt :db/id identity)) blocks)
         id-map (into {} (map (juxt :db/id identity)) blocks)
-        children (group-by #(get-in % [:block/parent :db/id]) blocks)
+        children (if include-children? (group-by #(get-in % [:block/parent :db/id]) blocks) {})
         build-block (fn build-block [block*]
         build-block (fn build-block [block*]
                       (let [child-nodes (mapv build-block (get children (:db/id block*) []))
                       (let [child-nodes (mapv build-block (get children (:db/id block*) []))
                             {:keys [node properties classes]}
                             {:keys [node properties classes]}
@@ -377,6 +385,46 @@
         page-export (finalize-export-maps db page-blocks-export uuid-block-export content-ref-export)]
         page-export (finalize-export-maps db page-blocks-export uuid-block-export content-ref-export)]
     page-export))
     page-export))
 
 
+(defn build-view-nodes-export* [db nodes opts]
+  (let [node-pages (filter entity-util/page? nodes)
+        pages-export
+        (merge
+         (build-mixed-properties-and-classes-export db node-pages {:shallow-copy? true})
+         {:pages-and-blocks (mapv #(hash-map :page (shallow-copy-page %))
+                                  (filter #(or (entity-util/internal-page? %) (entity-util/journal? %)) node-pages))})
+        node-blocks (remove entity-util/page? nodes)
+        ;; Similar to build-uuid-block-export
+        pages-to-blocks
+        (->> node-blocks
+             (group-by :block/parent)
+             (map (fn [[parent-page-ent blocks]]
+                    (merge (build-blocks-export db
+                                                (sort-by :block/order blocks)
+                                                (merge opts {:include-children? false}))
+                           {:page (shallow-copy-page parent-page-ent)}))))
+        pages-to-blocks-export
+        {:properties (apply merge (map :properties pages-to-blocks))
+         :classes (apply merge (map :classes pages-to-blocks))
+         :pages-and-blocks (mapv #(select-keys % [:page :blocks]) pages-to-blocks)}]
+    (merge (merge-export-maps pages-export pages-to-blocks-export)
+           {:pvalue-uuids (apply set/union (map :pvalue-uuids pages-to-blocks))})))
+
+(defn- build-view-nodes-export
+  "Exports given nodes from a view. Nodes are a random mix of blocks and pages"
+  [db eids]
+  (let [nodes (map #(d/entity db %) eids)
+        property-value-ents (mapcat #(->> (dissoc (db-property/properties %) :block/tags)
+                                          vals
+                                          (filter de/entity?))
+                                    nodes)
+        {:keys [content-ref-uuids content-ref-ents] :as content-ref-export}
+        (build-content-ref-export db (into nodes property-value-ents))
+        {:keys [pvalue-uuids] :as nodes-export}
+        (build-view-nodes-export* db nodes {:include-uuid-fn content-ref-uuids})
+        uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {})
+        view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)]
+    view-nodes-export))
+
 (defn- build-graph-ontology-export
 (defn- build-graph-ontology-export
   "Exports a graph's tags and properties"
   "Exports a graph's tags and properties"
   [db]
   [db]
@@ -467,6 +515,8 @@
           (build-block-export db (:block-id options))
           (build-block-export db (:block-id options))
           :page
           :page
           (build-page-export db (:page-id options))
           (build-page-export db (:page-id options))
+          :view-nodes
+          (build-view-nodes-export db (:node-ids options))
           :graph-ontology
           :graph-ontology
           (build-graph-ontology-export db))]
           (build-graph-ontology-export db))]
     (ensure-export-is-valid (dissoc export-map ::block))
     (ensure-export-is-valid (dissoc export-map ::block))

+ 47 - 8
deps/db/test/logseq/db/sqlite/export_test.cljs

@@ -11,6 +11,13 @@
 
 
 ;; Test helpers
 ;; Test helpers
 ;; ============
 ;; ============
+(defn- validate-db
+  "Validate db, usually after transacting an import"
+  [db]
+  (let [validation (db-validate/validate-db! db)]
+    (when (seq (:errors validation)) (cljs.pprint/pprint {:validate (:errors validation)}))
+    (is (empty? (map :entity (:errors validation))) "Imported graph has no validation errors")))
+
 (defn- export-block-and-import-to-another-block
 (defn- export-block-and-import-to-another-block
   "Exports given block from one graph/conn, imports it to a 2nd block and then
   "Exports given block from one graph/conn, imports it to a 2nd block and then
    exports the 2nd block. The two blocks do not have to be in the same graph"
    exports the 2nd block. The two blocks do not have to be in the same graph"
@@ -23,10 +30,8 @@
             (sqlite-export/build-import @import-conn {:current-block import-block}))
             (sqlite-export/build-import @import-conn {:current-block import-block}))
         ;; _ (cljs.pprint/pprint _txs)
         ;; _ (cljs.pprint/pprint _txs)
         _ (d/transact! import-conn init-tx)
         _ (d/transact! import-conn init-tx)
-        _ (d/transact! import-conn block-props-tx)
-        validation (db-validate/validate-db! @import-conn)
-        _ (when (seq (:errors validation)) (cljs.pprint/pprint {:validate (:errors validation)}))
-        _  (is (empty? (map :entity (:errors validation))) "Imported graph has no validation errors")]
+        _ (d/transact! import-conn block-props-tx)]
+    (validate-db @import-conn)
     (sqlite-export/build-export @import-conn {:export-type :block
     (sqlite-export/build-export @import-conn {:export-type :block
                                               :block-id (:db/id import-block)})))
                                               :block-id (:db/id import-block)})))
 
 
@@ -79,7 +84,7 @@
           [{:page {:build/journal 20250220}
           [{:page {:build/journal 20250220}
             :blocks [{:block/title "b1"}]}
             :blocks [{:block/title "b1"}]}
            {:page {:build/journal 20250221}}]}
            {:page {:build/journal 20250221}}]}
-       (#'sqlite-export/merge-export-maps
+         (#'sqlite-export/merge-export-maps
           {:pages-and-blocks
           {:pages-and-blocks
            [{:page {:build/journal 20250220}
            [{:page {:build/journal 20250220}
              :blocks [{:block/title "b1"}]}]}
              :blocks [{:block/title "b1"}]}]}
@@ -180,9 +185,7 @@
         ;; _ (cljs.pprint/pprint _txs)
         ;; _ (cljs.pprint/pprint _txs)
         _ (d/transact! import-conn init-tx)
         _ (d/transact! import-conn init-tx)
         _ (d/transact! import-conn block-props-tx)
         _ (d/transact! import-conn block-props-tx)
-        validation (db-validate/validate-db! @import-conn)
-        _ (when (seq (:errors validation)) (cljs.pprint/pprint {:validate (:errors validation)}))
-        _  (is (empty? (map :entity (:errors validation))) "Imported graph has no validation errors")
+        _ (validate-db @import-conn)
         page2 (db-test/find-page-by-title @import-conn page-title)]
         page2 (db-test/find-page-by-title @import-conn page-title)]
     (sqlite-export/build-export @import-conn {:export-type :page :page-id (:db/id page2)})))
     (sqlite-export/build-export @import-conn {:export-type :page :page-id (:db/id page2)})))
 
 
@@ -442,7 +445,43 @@
         ;; _ (cljs.pprint/pprint _txs)
         ;; _ (cljs.pprint/pprint _txs)
         _ (d/transact! conn2 init-tx)
         _ (d/transact! conn2 init-tx)
         _ (d/transact! conn2 block-props-tx)
         _ (d/transact! conn2 block-props-tx)
+        _ (validate-db @conn2)
         imported-ontology (sqlite-export/build-export @conn2 {:export-type :graph-ontology})]
         imported-ontology (sqlite-export/build-export @conn2 {:export-type :graph-ontology})]
 
 
     (is (= (expand-properties (:properties original-data)) (:properties imported-ontology)))
     (is (= (expand-properties (:properties original-data)) (:properties imported-ontology)))
     (is (= (expand-classes (:classes original-data)) (:classes imported-ontology)))))
     (is (= (expand-classes (:classes original-data)) (:classes imported-ontology)))))
+
+(deftest import-view-blocks
+  (let [original-data
+        ;; Test a mix of page and block types
+        {:properties {:user.property/p1 {:logseq.property/type :default}
+                      :user.property/p2 {:logseq.property/type :default}}
+         :classes {:user.class/class1 {}}
+         :pages-and-blocks [{:page {:block/title "page1"}}
+                            {:page {:build/journal 20250226}}
+                            {:page {:block/title "page2"}
+                             :blocks [{:block/title "b1"
+                                       :build/properties {:user.property/p2 "ok"}}]}]}
+        conn (db-test/create-conn-with-blocks original-data)
+        get-node-ids (fn [db]
+                       (->> [(d/entity db :user.property/p1)
+                             (d/entity db :user.class/class1)
+                             (db-test/find-page-by-title db "page1")
+                             (db-test/find-journal-by-journal-day db 20250226)
+                             (db-test/find-block-by-content db "b1")]
+                            (remove nil?)
+                            (mapv #(vector :block/uuid (:block/uuid %)))))
+        conn2 (db-test/create-conn)
+        {:keys [init-tx block-props-tx] :as _txs}
+        (-> (sqlite-export/build-export @conn {:export-type :view-nodes :node-ids (get-node-ids @conn)})
+            (sqlite-export/build-import @conn2 {}))
+        ;; _ (cljs.pprint/pprint _txs)
+        _ (d/transact! conn2 init-tx)
+        _ (d/transact! conn2 block-props-tx)
+        _ (validate-db @conn2)
+        imported-nodes (sqlite-export/build-export @conn2 {:export-type :view-nodes
+                                                           :node-ids (get-node-ids @conn2)})]
+
+    (is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-nodes)))
+    (is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
+    (is (= (expand-classes (:classes original-data)) (:classes imported-nodes)))))

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

@@ -891,7 +891,7 @@
                    (update-block-marker options)
                    (update-block-marker options)
                    (update-block-priority options)
                    (update-block-priority options)
                    add-missing-timestamps
                    add-missing-timestamps
-                   ;; old whiteboards may have this
+                   ;; old whiteboards may have :block/left
                    (dissoc :block/left :block/format)
                    (dissoc :block/left :block/format)
                    ;; ((fn [x] (prn :block-out x) x))
                    ;; ((fn [x] (prn :block-out x) x))
                    )]
                    )]
@@ -1242,6 +1242,7 @@
         extract-options' (merge {:block-pattern (common-config/get-block-pattern format)
         extract-options' (merge {:block-pattern (common-config/get-block-pattern format)
                                  :date-formatter "MMM do, yyyy"
                                  :date-formatter "MMM do, yyyy"
                                  :uri-encoded? false
                                  :uri-encoded? false
+                                 ;; Alters behavior in gp-block
                                  :export-to-db-graph? true
                                  :export-to-db-graph? true
                                  :filename-format :legacy}
                                  :filename-format :legacy}
                                 extract-options
                                 extract-options

+ 14 - 21
deps/graph-parser/src/logseq/graph_parser/extract.cljc

@@ -192,15 +192,13 @@
       blocks))
       blocks))
 
 
 (defn- build-pages-aux
 (defn- build-pages-aux
-  [db db-based? options page-map ref-pages date-formatter format]
-  (let [namespace-pages (when (or (not db-based?) (:export-to-db-graph? options))
-                          (let [page (:block/title page-map)]
-                            (when (text/namespace-page? page)
-                              (->> (common-util/split-namespace-pages page)
-                                   (map (fn [page]
-                                          (cond-> (gp-block/page-name->map page db true date-formatter)
-                                            (not db-based?)
-                                            (assoc :block/format format))))))))
+  [db page-map ref-pages date-formatter format]
+  (let [namespace-pages (let [page (:block/title page-map)]
+                          (when (text/namespace-page? page)
+                            (->> (common-util/split-namespace-pages page)
+                                 (map (fn [page]
+                                        (-> (gp-block/page-name->map page db true date-formatter)
+                                            (assoc :block/format format)))))))
         pages (->> (concat
         pages (->> (concat
                     [page-map]
                     [page-map]
                     @ref-pages
                     @ref-pages
@@ -234,8 +232,7 @@
                                        :as options}]
                                        :as options}]
   (assert db "Datascript DB is required")
   (assert db "Datascript DB is required")
   (try
   (try
-    (let [db-based? (ldb/db-based-graph? db)
-          page (get-page-name file ast false filename-format)
+    (let [page (get-page-name file ast false filename-format)
           [page page-name _journal-day] (gp-block/convert-page-if-journal page date-formatter)
           [page page-name _journal-day] (gp-block/convert-page-if-journal page date-formatter)
           options' (assoc options :page-name page-name)
           options' (assoc options :page-name page-name)
           ;; In case of diff-merge (2way) triggered, use the uuids to override the ones extracted from the AST
           ;; In case of diff-merge (2way) triggered, use the uuids to override the ones extracted from the AST
@@ -253,15 +250,11 @@
                           (let [block-ref-pages (seq (:block/refs block))]
                           (let [block-ref-pages (seq (:block/refs block))]
                             (when block-ref-pages
                             (when block-ref-pages
                               (swap! ref-pages set/union (set block-ref-pages)))
                               (swap! ref-pages set/union (set block-ref-pages)))
-                            (cond->
-                             (-> block
-                                 (dissoc :ref-pages)
-                                 (assoc :block/page [:block/name page-name]
-                                        :block/refs block-ref-pages))
-                              (not db-based?)
-                              (assoc :block/format format)
-                              db-based?
-                              (dissoc :block/format)))))
+                            (-> block
+                                (dissoc :ref-pages)
+                                (assoc :block/page [:block/name page-name]
+                                       :block/refs block-ref-pages
+                                       :block/format format)))))
                       blocks)
                       blocks)
           [properties invalid-properties properties-text-values]
           [properties invalid-properties properties-text-values]
           (if (:block/pre-block? (first blocks))
           (if (:block/pre-block? (first blocks))
@@ -270,7 +263,7 @@
              (:block/properties-text-values (first blocks))]
              (:block/properties-text-values (first blocks))]
             [properties [] {}])
             [properties [] {}])
           page-map (build-page-map properties invalid-properties properties-text-values file page page-name (assoc options' :from-page page))
           page-map (build-page-map properties invalid-properties properties-text-values file page page-name (assoc options' :from-page page))
-          pages (build-pages-aux db db-based? options page-map ref-pages date-formatter format)
+          pages (build-pages-aux db page-map ref-pages date-formatter format)
           blocks (->> (remove nil? blocks)
           blocks (->> (remove nil? blocks)
                       (map (fn [b] (dissoc b :block.temp/ast-title :block.temp/ast-body :block/level :block/children :block/meta))))]
                       (map (fn [b] (dissoc b :block.temp/ast-title :block.temp/ast-body :block/level :block/children :block/meta))))]
       [pages blocks])
       [pages blocks])

+ 2 - 1
deps/outliner/src/logseq/outliner/property.cljs

@@ -9,6 +9,7 @@
             [logseq.db.common.order :as db-order]
             [logseq.db.common.order :as db-order]
             [logseq.db.frontend.db-ident :as db-ident]
             [logseq.db.frontend.db-ident :as db-ident]
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-plus :as entity-plus]
+            [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.build :as db-property-build]
             [logseq.db.frontend.property.build :as db-property-build]
@@ -358,7 +359,7 @@
                                               (fn [value]
                                               (fn [value]
                                                 (and
                                                 (and
                                                  (:logseq.property/created-from-property value)
                                                  (:logseq.property/created-from-property value)
-                                                 (not (or (ldb/page? value) (ldb/closed-value? value)))
+                                                 (not (or (entity-util/page? value) (ldb/closed-value? value)))
                                                  (empty? (set/difference (set (map :e (d/datoms @conn :avet (:db/ident property) (:db/id value)))) block-id-set))))
                                                  (empty? (set/difference (set (map :e (d/datoms @conn :avet (:db/ident property) (:db/id value)))) block-id-set))))
                                               entities)
                                               entities)
                            ;; Delete property value block if it's no longer used by other blocks
                            ;; Delete property value block if it's no longer used by other blocks

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

@@ -4,6 +4,7 @@
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
             [datascript.core :as d]
             [datascript.core :as d]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.db.frontend.entity-util :as entity-util]
             [logseq.common.date :as common-date]
             [logseq.common.date :as common-date]
             [logseq.common.util.namespace :as ns-util]
             [logseq.common.util.namespace :as ns-util]
             [clojure.set :as set]
             [clojure.set :as set]
@@ -110,13 +111,13 @@
    - Page names are unique for a tag e.g. their can be Apple #Company and Apple #Fruit
    - Page names are unique for a tag e.g. their can be Apple #Company and Apple #Fruit
    - Page names are unique for a :logseq.property/parent"
    - Page names are unique for a :logseq.property/parent"
   [db new-title entity]
   [db new-title entity]
-  (when (ldb/page? entity)
+  (when (entity-util/page? entity)
     (validate-unique-for-page db new-title entity)))
     (validate-unique-for-page db new-title entity)))
 
 
 (defn ^:api validate-disallow-page-with-journal-name
 (defn ^:api validate-disallow-page-with-journal-name
   "Validates a non-journal page renamed to journal format"
   "Validates a non-journal page renamed to journal format"
   [new-title entity]
   [new-title entity]
-  (when (and (ldb/page? entity) (not (ldb/journal? entity))
+  (when (and (entity-util/page? entity) (not (entity-util/journal? entity))
              (common-date/normalize-date new-title nil))
              (common-date/normalize-date new-title nil))
     (throw (ex-info "Page can't be renamed to a journal"
     (throw (ex-info "Page can't be renamed to a journal"
                     {:type :notification
                     {:type :notification

+ 2 - 4
deps/shui/src/logseq/shui/select/core.cljs

@@ -1,13 +1,11 @@
 (ns logseq.shui.select.core
 (ns logseq.shui.select.core
-  (:require [rum.core :as rum]
-            [daiquiri.interpreter :refer [interpret]]
-            [logseq.shui.util :as util]
-            [cljs-bean.core :as bean]))
+  (:require [logseq.shui.util :as util]))
 
 
 (def select (util/lsui-wrap "Select"))
 (def select (util/lsui-wrap "Select"))
 (def select-group (util/lsui-wrap "SelectGroup"))
 (def select-group (util/lsui-wrap "SelectGroup"))
 (def select-value (util/lsui-wrap "SelectValue"))
 (def select-value (util/lsui-wrap "SelectValue"))
 (def select-trigger (util/lsui-wrap "SelectTrigger"))
 (def select-trigger (util/lsui-wrap "SelectTrigger"))
+(def select-icon (util/lsui-wrap "SelectIcon"))
 (def select-content (util/lsui-wrap "SelectContent"))
 (def select-content (util/lsui-wrap "SelectContent"))
 (def select-label (util/lsui-wrap "SelectLabel"))
 (def select-label (util/lsui-wrap "SelectLabel"))
 (def select-item (util/lsui-wrap "SelectItem"))
 (def select-item (util/lsui-wrap "SelectItem"))

+ 1 - 0
deps/shui/src/logseq/shui/ui.cljs

@@ -73,6 +73,7 @@
 (def select-group select-core/select-group)
 (def select-group select-core/select-group)
 (def select-value select-core/select-value)
 (def select-value select-core/select-value)
 (def select-trigger select-core/select-trigger)
 (def select-trigger select-core/select-trigger)
+(def select-icon select-core/select-icon)
 (def select-content select-core/select-content)
 (def select-content select-core/select-content)
 (def select-label select-core/select-label)
 (def select-label select-core/select-label)
 (def select-item select-core/select-item)
 (def select-item select-core/select-item)

+ 1 - 1
packages/ui/package.json

@@ -8,7 +8,7 @@
     "build:ui:only": "parcel build --target ui",
     "build:ui:only": "parcel build --target ui",
     "build:ui": "rm -rf .parcel-cache && yarn build:ui:only",
     "build:ui": "rm -rf .parcel-cache && yarn build:ui:only",
     "watch:storybook": "storybook dev -p 6006",
     "watch:storybook": "storybook dev -p 6006",
-    "install:primitives": "rm -rf primitives/ && git clone https://github.com/xyhp915/primitives.git --depth=1",
+    "install:primitives": "rm -rf primitives/ && git clone -b x https://github.com/xyhp915/primitives.git --depth=1",
     "build:primitives": "yarn install:primitives && cd primitives/ && yarn install && yarn build",
     "build:primitives": "yarn install:primitives && cd primitives/ && yarn install && yarn build",
     "preinstall": "test -f primitives/package.json || yarn build:primitives",
     "preinstall": "test -f primitives/package.json || yarn build:primitives",
     "postinstall": "yarn build:ui"
     "postinstall": "yarn build:ui"

+ 14 - 4
scripts/src/logseq/tasks/dev/db_and_file_graphs.clj

@@ -14,6 +14,7 @@
   "Namespaces or parent namespaces _only_ for DB graphs. Use a '.' at end of a namespace for parent namespaces"
   "Namespaces or parent namespaces _only_ for DB graphs. Use a '.' at end of a namespace for parent namespaces"
   (mapv escape-shell-regex
   (mapv escape-shell-regex
         ["logseq.db.sqlite." "logseq.db.frontend."
         ["logseq.db.sqlite." "logseq.db.frontend."
+         "logseq.outliner.property" "logseq.outliner.validate" "logseq.outliner.cli" "logseq.outliner.db-pipeline"
          "electron.db"
          "electron.db"
          "frontend.handler.db-based."
          "frontend.handler.db-based."
          "frontend.worker.handler.page.db-based"
          "frontend.worker.handler.page.db-based"
@@ -23,7 +24,8 @@
 (def file-graph-ns
 (def file-graph-ns
   "Namespaces or parent namespaces _only_ for file graphs"
   "Namespaces or parent namespaces _only_ for file graphs"
   (mapv escape-shell-regex
   (mapv escape-shell-regex
-        ["frontend.handler.file-based" "frontend.handler.file-sync"
+        ["logseq.graph-parser.db" "logseq.graph-parser.property" "logseq.graph-parser.extract"
+         "frontend.handler.file-based" "frontend.handler.file-sync"
          "frontend.db.file-based"
          "frontend.db.file-based"
          "frontend.util.file-based"
          "frontend.util.file-based"
          "frontend.common.file-based"
          "frontend.common.file-based"
@@ -39,12 +41,16 @@
   "DB graph paths with :block/name"
   "DB graph paths with :block/name"
   ["deps/db/src/logseq/db/frontend"
   ["deps/db/src/logseq/db/frontend"
    "deps/db/src/logseq/db/sqlite"
    "deps/db/src/logseq/db/sqlite"
+   "deps/outliner/src/logseq/outliner/property.cljs"
    "src/main/frontend/worker/handler/page/db_based"])
    "src/main/frontend/worker/handler/page/db_based"])
 
 
 (def db-graph-paths
 (def db-graph-paths
   "Paths _only_ for DB graphs"
   "Paths _only_ for DB graphs"
   (into block-name-db-graph-paths
   (into block-name-db-graph-paths
-        ["src/main/frontend/handler/db_based"
+        ["deps/outliner/src/logseq/outliner/cli.cljs"
+         "deps/outliner/src/logseq/outliner/db_pipeline.cljs"
+         "deps/outliner/src/logseq/outliner/validate.cljs"
+         "src/main/frontend/handler/db_based"
          "src/main/frontend/components/class.cljs"
          "src/main/frontend/components/class.cljs"
          "src/main/frontend/components/property.cljs"
          "src/main/frontend/components/property.cljs"
          "src/main/frontend/components/property"
          "src/main/frontend/components/property"
@@ -55,7 +61,11 @@
 
 
 (def file-graph-paths
 (def file-graph-paths
   "Paths _only_ for file graphs"
   "Paths _only_ for file graphs"
-  ["src/main/frontend/handler/file_based" "src/main/frontend/handler/file_sync.cljs" "src/main/frontend/db/file_based"
+  ["deps/graph-parser/src/logseq/graph_parser/db.cljs"
+   "deps/graph-parser/src/logseq/graph_parser/extract.cljc"
+   "deps/graph-parser/src/logseq/graph_parser/property.cljs"
+   "deps/graph-parser/src/logseq/graph_parser.cljs"
+   "src/main/frontend/handler/file_based" "src/main/frontend/handler/file_sync.cljs" "src/main/frontend/db/file_based"
    "src/main/frontend/util/file_based" "src/main/frontend/worker/handler/page/file_based" "src/main/frontend/worker/file.cljs"
    "src/main/frontend/util/file_based" "src/main/frontend/worker/handler/page/file_based" "src/main/frontend/worker/file.cljs"
    "src/main/frontend/common/file_based"
    "src/main/frontend/common/file_based"
    "src/main/frontend/fs"
    "src/main/frontend/fs"
@@ -131,7 +141,7 @@
             (System/exit 1))
             (System/exit 1))
 
 
         ;; :block/name isn't used in db graphs except for fns with journal or internal-page
         ;; :block/name isn't used in db graphs except for fns with journal or internal-page
-        block-name-file-concepts #{"block/name" "/page-name-sanity-lc" "db/get-page"}
+        block-name-file-concepts #{"block/name" "/page-name-sanity-lc" "db/get-page "}
         no-block-name-db-graph-paths (set/difference (set db-graph-paths) (set block-name-db-graph-paths))
         no-block-name-db-graph-paths (set/difference (set db-graph-paths) (set block-name-db-graph-paths))
         block-name-res (grep-many block-name-file-concepts no-block-name-db-graph-paths)
         block-name-res (grep-many block-name-file-concepts no-block-name-db-graph-paths)
         block-name-invalid-lines (when (= 0 (:exit block-name-res))
         block-name-invalid-lines (when (= 0 (:exit block-name-res))

+ 1 - 1
scripts/src/logseq/tasks/lang.clj

@@ -133,7 +133,7 @@
    "(t prompt-key" [:select/default-prompt :select/default-select-multiple :select.graph/prompt]
    "(t prompt-key" [:select/default-prompt :select/default-select-multiple :select.graph/prompt]
    ;; All args to ui/make-confirm-modal are not keywords
    ;; All args to ui/make-confirm-modal are not keywords
    "(t title" []
    "(t title" []
-   "(t (or title-key" [:views.table/live-query-title :views.table/default-title]
+   "(t (or title-key" [:views.table/live-query-title :views.table/default-title :all-pages/table-title]
    "(t subtitle" [:asset/physical-delete]})
    "(t subtitle" [:asset/physical-delete]})
 
 
 (defn- whiteboard-dicts
 (defn- whiteboard-dicts

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

@@ -64,6 +64,7 @@
                   :view-feature-type :all-pages
                   :view-feature-type :all-pages
                   :show-items-count? true
                   :show-items-count? true
                   :columns columns'
                   :columns columns'
+                  :title-key :all-pages/table-title
                   :on-delete-rows (fn [table selected-rows]
                   :on-delete-rows (fn [table selected-rows]
                                     (shui/dialog-open!
                                     (shui/dialog-open!
                                      (component-page/batch-delete-dialog
                                      (component-page/batch-delete-dialog

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

@@ -370,10 +370,7 @@
       (for [page pages]
       (for [page pages]
         [:li.recent-item.select-none.font-medium
         [:li.recent-item.select-none.font-medium
          {:key (str "recent-" (:db/id page))
          {:key (str "recent-" (:db/id page))
-          :title (block-handler/block-unique-title page)
-          :draggable true
-          :on-drag-start (fn [event] (editor-handler/block->data-transfer! (:block/name page) event true))
-          :data-ref (str name)}
+          :title (block-handler/block-unique-title page)}
          (page-name page (icon/get-node-icon-cp page {:size 16}) true)])])))
          (page-name page (icon/get-node-icon-cp page {:size 16}) true)])])))
 
 
 (defn get-default-home-if-valid
 (defn get-default-home-if-valid

+ 2 - 3
src/main/frontend/components/container.css

@@ -241,9 +241,8 @@
               @apply flex items-center;
               @apply flex items-center;
             }
             }
 
 
-            &:hover {
-              background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
-              opacity: 1;
+            &:hover, &:has([data-popup-active]) {
+              @apply bg-gray-04 opacity-100;
             }
             }
           }
           }
         }
         }

+ 6 - 6
src/main/frontend/components/db_based/page.cljs

@@ -17,9 +17,9 @@
                          (util/stop e))
                          (util/stop e))
       :on-click (fn [^js e]
       :on-click (fn [^js e]
                   (shui/popup-show! (.-target e)
                   (shui/popup-show! (.-target e)
-                                    (fn []
-                                      (property-config/dropdown-editor page nil {:debug? (.-altKey e)}))
-                                    {:content-props {:class "ls-property-dropdown-editor as-root"}
-                                     :align "start"
-                                     :as-dropdown? true}))}
-     "Configure property")))
+                    (fn []
+                      (property-config/dropdown-editor page nil {:debug? (.-altKey e)}))
+                    {:content-props {:class "ls-property-dropdown-editor as-root"}
+                     :align "start"
+                     :as-dropdown? true}))}
+      "Configure property")))

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

@@ -731,7 +731,6 @@
                   (hierarchy/structures (:block/title page))))
                   (hierarchy/structures (:block/title page))))
 
 
               (when-not (or whiteboard? unlinked-refs?
               (when-not (or whiteboard? unlinked-refs?
-                            db-based?
                             sidebar?
                             sidebar?
                             home?
                             home?
                             (or class-page? property-page?)
                             (or class-page? property-page?)

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

@@ -202,8 +202,8 @@
 
 
 (rum/defc remote-readme-display
 (rum/defc remote-readme-display
   [{:keys [repo]} _content]
   [{:keys [repo]} _content]
-
-  (let [src (str "./marketplace.html?repo=" repo)]
+  (let [src (str (if (string/includes? js/location.href "logseq")
+                   "./static/" "./") "marketplace.html?repo=" repo)]
     [:iframe.lsp-frame-readme {:src src}]))
     [:iframe.lsp-frame-readme {:src src}]))
 
 
 (defn security-warning
 (defn security-warning

+ 7 - 3
src/main/frontend/components/property.css

@@ -354,10 +354,14 @@ a.control-link {
   }
   }
 
 
   .inner-wrap {
   .inner-wrap {
-    @apply flex items-center w-full justify-between gap-1 flex-wrap;
+    @apply flex items-center w-full justify-between gap-1 flex-nowrap;
 
 
     > .property-setting-title {
     > .property-setting-title {
-      @apply flex items-center gap-1 font-normal opacity-90;
+      @apply flex items-center gap-1 font-normal opacity-90 overflow-hidden;
+
+      > span {
+        @apply whitespace-nowrap text-ellipsis overflow-hidden;
+      }
     }
     }
 
 
     > label {
     > label {
@@ -413,7 +417,7 @@ a.control-link {
   @apply w-[220px] p-0;
   @apply w-[220px] p-0;
 
 
   > ul.choices-list {
   > ul.choices-list {
-    @apply m-0 px-1.5 pt-1 pb-1;
+    @apply m-0 px-1.5 pt-1 pb-[1px];
 
 
     li {
     li {
       @apply flex items-center gap-1 relative;
       @apply flex items-center gap-1 relative;

+ 34 - 30
src/main/frontend/components/property/config.cljs

@@ -290,7 +290,7 @@
       [:div.property-setting-title
       [:div.property-setting-title
        (some-> icon (name) (shui/tabler-icon {:size 14
        (some-> icon (name) (shui/tabler-icon {:size 14
                                               :style {:margin-top "-1"}}))
                                               :style {:margin-top "-1"}}))
-       [:span title]]
+       [:span {:title title} title]]
       (cond
       (cond
         (fn? desc)
         (fn? desc)
         (desc)
         (desc)
@@ -403,30 +403,31 @@
                            :value id
                            :value id
                            :content (choice-item-content property block opts)}))
                            :content (choice-item-content property block opts)}))
                       choices)]
                       choices)]
+
     [:div.ls-property-dropdown-editor.ls-property-choices-sub-pane
     [:div.ls-property-dropdown-editor.ls-property-choices-sub-pane
      (when (seq choices)
      (when (seq choices)
-       [:ul.choices-list
-        (dnd/items choice-items
-                   {:sort-by-inner-element? false
-                    :on-drag-end (fn [_ {:keys [active-id over-id direction]}]
-                                   (let [move-down? (= direction :down)
-                                         over (db/entity [:block/uuid (uuid over-id)])
-                                         active (db/entity [:block/uuid (uuid active-id)])
-                                         over-order (:block/order over)
-                                         new-order (if move-down?
-                                                     (let [next-order (db-order/get-next-order (db/get-db) property (:db/id over))]
-                                                       (db-order/gen-key over-order next-order))
-                                                     (let [prev-order (db-order/get-prev-order (db/get-db) property (:db/id over))]
-                                                       (db-order/gen-key prev-order over-order)))]
-
-                                     (db/transact! (state/get-current-repo)
-                                                   [{:db/id (:db/id active)
-                                                     :block/order new-order}
-                                                    (outliner-core/block-with-updated-at
-                                                     {:db/id (:db/id property)})]
-                                                   {:outliner-op :save-block})))})])
-
-     (shui/dropdown-menu-separator)
+       [:<>
+        [:ul.choices-list
+         (dnd/items choice-items
+           {:sort-by-inner-element? false
+            :on-drag-end (fn [_ {:keys [active-id over-id direction]}]
+                           (let [move-down? (= direction :down)
+                                 over (db/entity [:block/uuid (uuid over-id)])
+                                 active (db/entity [:block/uuid (uuid active-id)])
+                                 over-order (:block/order over)
+                                 new-order (if move-down?
+                                             (let [next-order (db-order/get-next-order (db/get-db) property (:db/id over))]
+                                               (db-order/gen-key over-order next-order))
+                                             (let [prev-order (db-order/get-prev-order (db/get-db) property (:db/id over))]
+                                               (db-order/gen-key prev-order over-order)))]
+
+                             (db/transact! (state/get-current-repo)
+                               [{:db/id (:db/id active)
+                                 :block/order new-order}
+                                (outliner-core/block-with-updated-at
+                                  {:db/id (:db/id property)})]
+                               {:outliner-op :save-block})))})]
+        (shui/dropdown-menu-separator)])
 
 
      ;; add choice
      ;; add choice
      (when-not disabled?
      (when-not disabled?
@@ -462,13 +463,14 @@
   [choices]
   [choices]
   (let [select-cp (fn [opts]
   (let [select-cp (fn [opts]
                     (shui/select
                     (shui/select
-                     opts
-                     (shui/select-trigger
-                      (shui/select-value {:placeholder "Select a choice"}))
-                     (shui/select-content
-                      (map (fn [choice]
-                             (shui/select-item {:key (str (:db/id choice))
-                                                :value (:db/id choice)} (:block/title choice))) choices))))
+                      opts
+                      (shui/select-trigger
+                        {:class "h-8"}
+                        (shui/select-value {:placeholder "Select a choice"}))
+                      (shui/select-content
+                        (map (fn [choice]
+                               (shui/select-item {:key (str (:db/id choice))
+                                                  :value (:db/id choice)} (:block/title choice))) choices))))
         checked-choice (some (fn [choice] (when (true? (:logseq.property/choice-checkbox-state choice)) choice)) choices)
         checked-choice (some (fn [choice] (when (true? (:logseq.property/choice-checkbox-state choice)) choice)) choices)
         unchecked-choice (some (fn [choice] (when (false? (:logseq.property/choice-checkbox-state choice)) choice)) choices)]
         unchecked-choice (some (fn [choice] (when (false? (:logseq.property/choice-checkbox-state choice)) choice)) choices)]
     [:div.flex.flex-col.gap-4.text-sm.p-2
     [:div.flex.flex-col.gap-4.text-sm.p-2
@@ -603,6 +605,8 @@
         built-in? (ldb/built-in? property)
         built-in? (ldb/built-in? property)
         disabled? (or built-in? config/publishing?)]
         disabled? (or built-in? config/publishing?)]
     [:<>
     [:<>
+     [:h3.font-medium.px-2.pt-2.pb-2.opacity-90.flex.items-center.gap-1
+      (shui/tabler-icon "adjustments-alt") [:span "Configure property"]]
      (dropdown-editor-menuitem {:icon :pencil :title "Property name" :desc [:span.flex.items-center.gap-1 icon title]
      (dropdown-editor-menuitem {:icon :pencil :title "Property name" :desc [:span.flex.items-center.gap-1 icon title]
                                 :submenu-content (fn [ops] (name-edit-pane property (assoc ops :disabled? disabled?)))})
                                 :submenu-content (fn [ops] (name-edit-pane property (assoc ops :disabled? disabled?)))})
      (let [disabled?' (or disabled? (and property-type (seq values)))]
      (let [disabled?' (or disabled? (and property-type (seq values)))]

+ 27 - 15
src/main/frontend/components/reference.cljs

@@ -249,15 +249,25 @@
   [state page _n-ref]
   [state page _n-ref]
   (let [ref-blocks (rum/react (::result state))]
   (let [ref-blocks (rum/react (::result state))]
     (when (seq ref-blocks)
     (when (seq ref-blocks)
-      [:div.references-blocks
-       (let [ref-hiccup (block/->hiccup ref-blocks
-                                        {:id (str (:block/title page) "-unlinked-")
-                                         :ref? true
-                                         :group-by-page? true
-                                         :editor-box editor/box}
-                                        {})]
-         (content/content (:block/name page)
-                          {:hiccup ref-hiccup}))])))
+      (if (config/db-based-graph?)
+        (let [blocks (->> (mapcat val ref-blocks)
+                          (map (fn [b] (assoc (db/entity (:db/id b)) :id (:db/id b)))))
+              columns' (columns {} blocks)]
+          (views/view
+           {:view-parent page
+            :view-feature-type :unlinked-references
+            :data blocks
+            :columns columns'
+            :foldable-options {:default-collapsed? true}}))
+        [:div.references-blocks
+         (let [ref-hiccup (block/->hiccup ref-blocks
+                                          {:id (str (:block/title page) "-unlinked-")
+                                           :ref? true
+                                           :group-by-page? true
+                                           :editor-box editor/box}
+                                          {})]
+           (content/content (:block/name page)
+                            {:hiccup ref-hiccup}))]))))
 
 
 (rum/defcs unlinked-references < rum/reactive
 (rum/defcs unlinked-references < rum/reactive
   (rum/local nil ::n-ref)
   (rum/local nil ::n-ref)
@@ -266,9 +276,11 @@
     (when page
     (when page
       [:div.references.page-unlinked.mt-6.flex-1.flex-row.faster.fade-in
       [:div.references.page-unlinked.mt-6.flex-1.flex-row.faster.fade-in
        [:div.content.flex-1
        [:div.content.flex-1
-        (ui/foldable
-         [:div.font-medium.opacity-50
-          (t :unlinked-references/reference-count @n-ref)]
-         (fn [] (unlinked-references-aux page n-ref))
-         {:default-collapsed? true
-          :title-trigger? true})]])))
+        (if (config/db-based-graph?)
+          (unlinked-references-aux page n-ref)
+          (ui/foldable
+           [:div.font-medium.opacity-50
+            (t :unlinked-references/reference-count @n-ref)]
+           (fn [] (unlinked-references-aux page n-ref))
+           {:default-collapsed? true
+            :title-trigger? true}))]])))

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

@@ -20,6 +20,7 @@
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.async :as db-async]
             [frontend.db.async :as db-async]
+            [frontend.handler.db-based.export :as db-export-handler]
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
@@ -316,7 +317,7 @@
     columns))
     columns))
 
 
 (rum/defc more-actions
 (rum/defc more-actions
-  [view-entity columns {:keys [column-visible? column-toggle-visibility]}]
+  [view-entity columns {:keys [column-visible? rows column-toggle-visibility]}]
   (let [display-type (:db/ident (:logseq.property.view/type view-entity))
   (let [display-type (:db/ident (:logseq.property.view/type view-entity))
         table? (= display-type :logseq.property.view/type.table)
         table? (= display-type :logseq.property.view/type.table)
         columns' (filter (fn [column]
         columns' (filter (fn [column]
@@ -367,7 +368,11 @@
                                                                                (:db/id (db/entity (:id column))))
                                                                                (:db/id (db/entity (:id column))))
                                       (db-property-handler/remove-block-property! (:db/id view-entity) :logseq.property.view/group-by-property)))
                                       (db-property-handler/remove-block-property! (:db/id view-entity) :logseq.property.view/group-by-property)))
                  :onSelect (fn [e] (.preventDefault e))}
                  :onSelect (fn [e] (.preventDefault e))}
-                (:name column))))))))))))
+                (:name column))))))
+         (shui/dropdown-menu-item
+          {:key "export-edn"
+           :on-click #(db-export-handler/export-view-nodes-data rows)}
+          "Export EDN")))))))
 
 
 (defn- get-column-size
 (defn- get-column-size
   [column sized-columns]
   [column sized-columns]
@@ -1542,16 +1547,21 @@
    set-input! add-new-object!
    set-input! add-new-object!
    {:keys [view-feature-type title-key additional-actions]
    {:keys [view-feature-type title-key additional-actions]
     :as option}]
     :as option}]
-  (let [[hover? set-hover?] (hooks/use-state nil)]
+  (let [[hover? set-hover?] (hooks/use-state nil)
+        db-based? (config/db-based-graph? (state/get-current-repo))]
     [:div.flex.flex-1.flex-wrap.items-center.justify-between.gap-1
     [:div.flex.flex-1.flex-wrap.items-center.justify-between.gap-1
      {:on-mouse-over #(set-hover? true)
      {:on-mouse-over #(set-hover? true)
       :on-mouse-out #(set-hover? false)}
       :on-mouse-out #(set-hover? false)}
      [:div.flex.flex-row.items-center.gap-2
      [:div.flex.flex-row.items-center.gap-2
-      (if (= view-feature-type :query-result)
+      (if db-based?
+        (if (= view-feature-type :query-result)
+          [:div.font-medium.opacity-50.text-sm
+           (t (or title-key :views.table/default-title)
+              (count (:rows table)))]
+          (views-tab view-parent view-entity (:rows table) option hover?))
         [:div.font-medium.opacity-50.text-sm
         [:div.font-medium.opacity-50.text-sm
          (t (or title-key :views.table/default-title)
          (t (or title-key :views.table/default-title)
-            (count (:rows table)))]
-        (views-tab view-parent view-entity (:rows table) option hover?))]
+            (count (:rows table)))])]
      [:div.view-actions.flex.items-center.gap-1.transition-opacity.ease-in.duration-300
      [:div.view-actions.flex.items-center.gap-1.transition-opacity.ease-in.duration-300
       {:class (if hover? "opacity-100" "opacity-75")}
       {:class (if hover? "opacity-100" "opacity-75")}
 
 
@@ -1569,12 +1579,12 @@
       [:div.text-muted-foreground.text-sm
       [:div.text-muted-foreground.text-sm
        (pv/property-value view-entity (db/entity :logseq.property.view/type) {})]
        (pv/property-value view-entity (db/entity :logseq.property.view/type) {})]
 
 
-      (more-actions view-entity columns table)
+      (when db-based? (more-actions view-entity columns table))
 
 
       (when add-new-object! (new-record-button table view-entity))]]))
       (when add-new-object! (new-record-button table view-entity))]]))
 
 
 (rum/defc ^:large-vars/cleanup-todo view-inner < rum/static
 (rum/defc ^:large-vars/cleanup-todo view-inner < rum/static
-  [view-entity {:keys [view-parent data set-data! columns add-new-object!] :as option}
+  [view-entity {:keys [view-parent data set-data! columns add-new-object! foldable-options] :as option}
    *scroller-ref]
    *scroller-ref]
   (let [[input set-input!] (rum/use-state "")
   (let [[input set-input!] (rum/use-state "")
         sorting* (:logseq.property.table/sorting view-entity)
         sorting* (:logseq.property.table/sorting view-entity)
@@ -1695,7 +1705,7 @@
                     (view-cp view-entity (assoc table' :rows group) option view-opts)]
                     (view-cp view-entity (assoc table' :rows group) option view-opts)]
                    {:title-trigger? false})))])
                    {:title-trigger? false})))])
            (view-cp view-entity table option view-opts)))]
            (view-cp view-entity table option view-opts)))]
-      {:title-trigger? false})]))
+      (merge {:title-trigger? false} foldable-options))]))
 
 
 (rum/defcs view-container
 (rum/defcs view-container
   "Provides a view for data like query results and tagged objects, multiple
   "Provides a view for data like query results and tagged objects, multiple

+ 1 - 0
src/main/frontend/error.cljs

@@ -4,6 +4,7 @@
 
 
 (def ignored
 (def ignored
   #{"ResizeObserver loop limit exceeded"
   #{"ResizeObserver loop limit exceeded"
+    "ResizeObserver loop completed with undelivered notifications"
     "Uncaught TypeError:"})
     "Uncaught TypeError:"})
 
 
 (defn ignored?
 (defn ignored?

+ 12 - 0
src/main/frontend/handler/db_based/export.cljs

@@ -27,6 +27,18 @@
         (notification/show! "Copied block's data!" :success)))
         (notification/show! "Copied block's data!" :success)))
     (notification/show! "No block found" :warning)))
     (notification/show! "No block found" :warning)))
 
 
+(defn export-view-nodes-data [nodes]
+  (let [block-uuids (mapv #(vector :block/uuid (:block/uuid %)) nodes)]
+    (when-let [^Object worker @state/*db-worker]
+      (p/let [result* (.export-edn worker
+                                   (state/get-current-repo)
+                                   (ldb/write-transit-str {:export-type :view-nodes :node-ids block-uuids}))
+              result (ldb/read-transit-str result*)
+              pull-data (with-out-str (pprint/pprint result))]
+        (.writeText js/navigator.clipboard pull-data)
+        (println pull-data)
+        (notification/show! "Copied block's data!" :success)))))
+
 (defn ^:export export-page-data []
 (defn ^:export export-page-data []
   (if-let [page-id (page-util/get-current-page-id)]
   (if-let [page-id (page-util/get-current-page-id)]
     (when-let [^Object worker @state/*db-worker]
     (when-let [^Object worker @state/*db-worker]

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

@@ -472,6 +472,8 @@
  :export-save-to-file "Save to file"
  :export-save-to-file "Save to file"
  :all-graphs "All graphs"
  :all-graphs "All graphs"
  :all-pages "All pages"
  :all-pages "All pages"
+  ;; E.g. 1 Page or 2 Pages
+ :all-pages/table-title (fn [total] (str total (if (= total 1) " Page" " Pages")))
  :all-pages/failed-to-delete-pages "These pages had their content deleted but were unable to be deleted: {1}. See javascript console for more details."
  :all-pages/failed-to-delete-pages "These pages had their content deleted but were unable to be deleted: {1}. See javascript console for more details."
  :all-whiteboards "All whiteboards"
  :all-whiteboards "All whiteboards"
  :all-files "All files"
  :all-files "All files"