Browse Source

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

Tienson Qin 7 months ago
parent
commit
02a3c407fb

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

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

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

@@ -231,6 +231,21 @@
       (seq 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
   "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"
@@ -239,20 +254,13 @@
         blocks (remove :logseq.property/value 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-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-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 %)
                                                      {:block/uuid (:block/uuid %) :build/keep-uuid? true}))
                              content-ref-pages)}))
@@ -278,14 +286,14 @@
      :properties properties}))
 
 (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"
-  [db blocks opts]
+  [db blocks {:keys [include-children?] :or {include-children? true} :as opts}]
   (let [*properties (atom {})
         *classes (atom {})
         *pvalue-uuids (atom #{})
         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*]
                       (let [child-nodes (mapv build-block (get children (:db/id block*) []))
                             {: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))
 
+(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
   "Exports a graph's tags and properties"
   [db]
@@ -467,6 +515,8 @@
           (build-block-export db (:block-id options))
           :page
           (build-page-export db (:page-id options))
+          :view-nodes
+          (build-view-nodes-export db (:node-ids options))
           :graph-ontology
           (build-graph-ontology-export db))]
     (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
 ;; ============
+(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
   "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"
@@ -23,10 +30,8 @@
             (sqlite-export/build-import @import-conn {:current-block import-block}))
         ;; _ (cljs.pprint/pprint _txs)
         _ (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
                                               :block-id (:db/id import-block)})))
 
@@ -79,7 +84,7 @@
           [{:page {:build/journal 20250220}
             :blocks [{:block/title "b1"}]}
            {:page {:build/journal 20250221}}]}
-       (#'sqlite-export/merge-export-maps
+         (#'sqlite-export/merge-export-maps
           {:pages-and-blocks
            [{:page {:build/journal 20250220}
              :blocks [{:block/title "b1"}]}]}
@@ -180,9 +185,7 @@
         ;; _ (cljs.pprint/pprint _txs)
         _ (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")
+        _ (validate-db @import-conn)
         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)})))
 
@@ -442,7 +445,43 @@
         ;; _ (cljs.pprint/pprint _txs)
         _ (d/transact! conn2 init-tx)
         _ (d/transact! conn2 block-props-tx)
+        _ (validate-db @conn2)
         imported-ontology (sqlite-export/build-export @conn2 {:export-type :graph-ontology})]
 
     (is (= (expand-properties (:properties original-data)) (:properties 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-priority options)
                    add-missing-timestamps
-                   ;; old whiteboards may have this
+                   ;; old whiteboards may have :block/left
                    (dissoc :block/left :block/format)
                    ;; ((fn [x] (prn :block-out x) x))
                    )]
@@ -1242,6 +1242,7 @@
         extract-options' (merge {:block-pattern (common-config/get-block-pattern format)
                                  :date-formatter "MMM do, yyyy"
                                  :uri-encoded? false
+                                 ;; Alters behavior in gp-block
                                  :export-to-db-graph? true
                                  :filename-format :legacy}
                                 extract-options

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

@@ -192,15 +192,13 @@
       blocks))
 
 (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
                     [page-map]
                     @ref-pages
@@ -234,8 +232,7 @@
                                        :as options}]
   (assert db "Datascript DB is required")
   (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)
           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
@@ -253,15 +250,11 @@
                           (let [block-ref-pages (seq (:block/refs block))]
                             (when 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)
           [properties invalid-properties properties-text-values]
           (if (:block/pre-block? (first blocks))
@@ -270,7 +263,7 @@
              (:block/properties-text-values (first blocks))]
             [properties [] {}])
           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)
                       (map (fn [b] (dissoc b :block.temp/ast-title :block.temp/ast-body :block/level :block/children :block/meta))))]
       [pages blocks])

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

@@ -9,6 +9,7 @@
             [logseq.db.common.order :as db-order]
             [logseq.db.frontend.db-ident :as db-ident]
             [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.property :as db-property]
             [logseq.db.frontend.property.build :as db-property-build]
@@ -358,7 +359,7 @@
                                               (fn [value]
                                                 (and
                                                  (: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))))
                                               entities)
                            ;; 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]
             [datascript.core :as d]
             [logseq.db :as ldb]
+            [logseq.db.frontend.entity-util :as entity-util]
             [logseq.common.date :as common-date]
             [logseq.common.util.namespace :as ns-util]
             [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 :logseq.property/parent"
   [db new-title entity]
-  (when (ldb/page? entity)
+  (when (entity-util/page? entity)
     (validate-unique-for-page db new-title entity)))
 
 (defn ^:api validate-disallow-page-with-journal-name
   "Validates a non-journal page renamed to journal format"
   [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))
     (throw (ex-info "Page can't be renamed to a journal"
                     {:type :notification

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

@@ -1,13 +1,11 @@
 (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-group (util/lsui-wrap "SelectGroup"))
 (def select-value (util/lsui-wrap "SelectValue"))
 (def select-trigger (util/lsui-wrap "SelectTrigger"))
+(def select-icon (util/lsui-wrap "SelectIcon"))
 (def select-content (util/lsui-wrap "SelectContent"))
 (def select-label (util/lsui-wrap "SelectLabel"))
 (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-value select-core/select-value)
 (def select-trigger select-core/select-trigger)
+(def select-icon select-core/select-icon)
 (def select-content select-core/select-content)
 (def select-label select-core/select-label)
 (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": "rm -rf .parcel-cache && yarn build:ui:only",
     "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",
     "preinstall": "test -f primitives/package.json || yarn build:primitives",
     "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"
   (mapv escape-shell-regex
         ["logseq.db.sqlite." "logseq.db.frontend."
+         "logseq.outliner.property" "logseq.outliner.validate" "logseq.outliner.cli" "logseq.outliner.db-pipeline"
          "electron.db"
          "frontend.handler.db-based."
          "frontend.worker.handler.page.db-based"
@@ -23,7 +24,8 @@
 (def file-graph-ns
   "Namespaces or parent namespaces _only_ for file graphs"
   (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.util.file-based"
          "frontend.common.file-based"
@@ -39,12 +41,16 @@
   "DB graph paths with :block/name"
   ["deps/db/src/logseq/db/frontend"
    "deps/db/src/logseq/db/sqlite"
+   "deps/outliner/src/logseq/outliner/property.cljs"
    "src/main/frontend/worker/handler/page/db_based"])
 
 (def db-graph-paths
   "Paths _only_ for DB graphs"
   (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/property.cljs"
          "src/main/frontend/components/property"
@@ -55,7 +61,11 @@
 
 (def file-graph-paths
   "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/common/file_based"
    "src/main/frontend/fs"
@@ -131,7 +141,7 @@
             (System/exit 1))
 
         ;; :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))
         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))

+ 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]
    ;; All args to ui/make-confirm-modal are not keywords
    "(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]})
 
 (defn- whiteboard-dicts

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

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

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

@@ -370,10 +370,7 @@
       (for [page pages]
         [:li.recent-item.select-none.font-medium
          {: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)])])))
 
 (defn get-default-home-if-valid

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

@@ -241,9 +241,8 @@
               @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))
       :on-click (fn [^js 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))))
 
               (when-not (or whiteboard? unlinked-refs?
-                            db-based?
                             sidebar?
                             home?
                             (or class-page? property-page?)

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

@@ -202,8 +202,8 @@
 
 (rum/defc remote-readme-display
   [{: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}]))
 
 (defn security-warning

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

@@ -354,10 +354,14 @@ a.control-link {
   }
 
   .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 {
-      @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 {
@@ -413,7 +417,7 @@ a.control-link {
   @apply w-[220px] p-0;
 
   > ul.choices-list {
-    @apply m-0 px-1.5 pt-1 pb-1;
+    @apply m-0 px-1.5 pt-1 pb-[1px];
 
     li {
       @apply flex items-center gap-1 relative;

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

@@ -290,7 +290,7 @@
       [:div.property-setting-title
        (some-> icon (name) (shui/tabler-icon {:size 14
                                               :style {:margin-top "-1"}}))
-       [:span title]]
+       [:span {:title title} title]]
       (cond
         (fn? desc)
         (desc)
@@ -403,30 +403,31 @@
                            :value id
                            :content (choice-item-content property block opts)}))
                       choices)]
+
     [:div.ls-property-dropdown-editor.ls-property-choices-sub-pane
      (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
      (when-not disabled?
@@ -462,13 +463,14 @@
   [choices]
   (let [select-cp (fn [opts]
                     (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)
         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
@@ -603,6 +605,8 @@
         built-in? (ldb/built-in? property)
         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]
                                 :submenu-content (fn [ops] (name-edit-pane property (assoc ops :disabled? disabled?)))})
      (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]
   (let [ref-blocks (rum/react (::result state))]
     (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/local nil ::n-ref)
@@ -266,9 +276,11 @@
     (when page
       [:div.references.page-unlinked.mt-6.flex-1.flex-row.faster.fade-in
        [: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-mixins :as db-mixins]
             [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.editor :as editor-handler]
             [frontend.handler.property :as property-handler]
@@ -316,7 +317,7 @@
     columns))
 
 (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))
         table? (= display-type :logseq.property.view/type.table)
         columns' (filter (fn [column]
@@ -367,7 +368,11 @@
                                                                                (:db/id (db/entity (:id column))))
                                       (db-property-handler/remove-block-property! (:db/id view-entity) :logseq.property.view/group-by-property)))
                  :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
   [column sized-columns]
@@ -1542,16 +1547,21 @@
    set-input! add-new-object!
    {:keys [view-feature-type title-key additional-actions]
     :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
      {:on-mouse-over #(set-hover? true)
       :on-mouse-out #(set-hover? false)}
      [: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
          (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
       {:class (if hover? "opacity-100" "opacity-75")}
 
@@ -1569,12 +1579,12 @@
       [:div.text-muted-foreground.text-sm
        (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))]]))
 
 (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]
   (let [[input set-input!] (rum/use-state "")
         sorting* (:logseq.property.table/sorting view-entity)
@@ -1695,7 +1705,7 @@
                     (view-cp view-entity (assoc table' :rows group) option view-opts)]
                    {:title-trigger? false})))])
            (view-cp view-entity table option view-opts)))]
-      {:title-trigger? false})]))
+      (merge {:title-trigger? false} foldable-options))]))
 
 (rum/defcs view-container
   "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
   #{"ResizeObserver loop limit exceeded"
+    "ResizeObserver loop completed with undelivered notifications"
     "Uncaught TypeError:"})
 
 (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! "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 []
   (if-let [page-id (page-util/get-current-page-id)]
     (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"
  :all-graphs "All graphs"
  :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-whiteboards "All whiteboards"
  :all-files "All files"