Browse Source

Merge branch 'feat/db' into refactor/dsl-query

Tienson Qin 1 year ago
parent
commit
47524fda15
65 changed files with 2073 additions and 2006 deletions
  1. 7 0
      deps/common/src/logseq/common/util.cljs
  2. 26 6
      deps/db/src/logseq/db.cljs
  3. 34 6
      deps/db/src/logseq/db/frontend/class.cljs
  4. 1 1
      deps/db/src/logseq/db/frontend/delete_blocks.cljs
  5. 9 4
      deps/db/src/logseq/db/frontend/entity_plus.cljc
  6. 7 1
      deps/db/src/logseq/db/frontend/entity_util.cljs
  7. 27 2
      deps/db/src/logseq/db/frontend/malli_schema.cljs
  8. 1 1
      deps/db/src/logseq/db/frontend/property.cljs
  9. 1 1
      deps/db/src/logseq/db/frontend/schema.cljs
  10. 9 9
      deps/db/src/logseq/db/sqlite/common_db.cljs
  11. 3 1
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  12. 34 29
      deps/outliner/src/logseq/outliner/core.cljs
  13. 4 0
      deps/shui/src/logseq/shui/dialog/core.cljs
  14. 96 94
      deps/shui/src/logseq/shui/table/core.cljc
  15. 3 5
      deps/shui/src/logseq/shui/table/impl.cljc
  16. 8 5
      scripts/src/logseq/tasks/db_graph/create_graph_with_properties.cljs
  17. 14 19
      src/main/frontend/commands.cljs
  18. 26 17
      src/main/frontend/components/all_pages.cljs
  19. 70 44
      src/main/frontend/components/block.cljs
  20. 1 2
      src/main/frontend/components/block/views.css
  21. 1 1
      src/main/frontend/components/class.cljs
  22. 4 0
      src/main/frontend/components/cmdk/cmdk.css
  23. 10 10
      src/main/frontend/components/cmdk/core.cljs
  24. 22 11
      src/main/frontend/components/container.cljs
  25. 5 6
      src/main/frontend/components/header.cljs
  26. 32 17
      src/main/frontend/components/objects.cljs
  27. 25 26
      src/main/frontend/components/page.cljs
  28. 0 9
      src/main/frontend/components/page.css
  29. 349 350
      src/main/frontend/components/plugins.cljs
  30. 3 9
      src/main/frontend/components/plugins.css
  31. 2 4
      src/main/frontend/components/property.cljs
  32. 1 1
      src/main/frontend/components/property/config.cljs
  33. 5 9
      src/main/frontend/components/property/value.cljs
  34. 1 1
      src/main/frontend/components/query.cljs
  35. 3 2
      src/main/frontend/components/query/view.cljs
  36. 17 11
      src/main/frontend/components/select.cljs
  37. 129 134
      src/main/frontend/components/settings.cljs
  38. 78 78
      src/main/frontend/components/shortcut.cljs
  39. 178 164
      src/main/frontend/components/views.cljs
  40. 0 7
      src/main/frontend/db/async.cljs
  41. 15 4
      src/main/frontend/extensions/fsrs.cljs
  42. 3 2
      src/main/frontend/extensions/pdf/assets.cljs
  43. 3 1
      src/main/frontend/extensions/tldraw.cljs
  44. 0 1
      src/main/frontend/handler/command_palette.cljs
  45. 1 1
      src/main/frontend/handler/common/developer.cljs
  46. 6 39
      src/main/frontend/handler/db_based/editor.cljs
  47. 27 11
      src/main/frontend/handler/editor.cljs
  48. 58 44
      src/main/frontend/handler/events.cljs
  49. 2 2
      src/main/frontend/handler/file_based/editor.cljs
  50. 0 10
      src/main/frontend/mixins.cljs
  51. 10 2
      src/main/frontend/modules/outliner/pipeline.cljs
  52. 363 363
      src/main/frontend/modules/shortcut/config.cljs
  53. 8 6
      src/main/frontend/persist_db/browser.cljs
  54. 0 1
      src/main/frontend/routes.cljs
  55. 15 123
      src/main/frontend/state.cljs
  56. 7 119
      src/main/frontend/ui.cljs
  57. 15 1
      src/main/frontend/ui.css
  58. 4 4
      src/main/frontend/util.cljc
  59. 13 14
      src/main/frontend/util/cursor.cljs
  60. 106 36
      src/main/frontend/worker/db/migrate.cljs
  61. 82 43
      src/main/frontend/worker/pipeline.cljs
  62. 76 76
      src/main/frontend/worker/rtc/asset.cljs
  63. 4 4
      src/main/frontend/worker/rtc/asset_db_listener.cljs
  64. 2 0
      src/resources/dicts/en.edn
  65. 7 2
      src/test/frontend/db/db_based_model_test.cljs

+ 7 - 0
deps/common/src/logseq/common/util.cljs

@@ -367,3 +367,10 @@ return: [{:id 3} {:id 2 :depend-on 3} {:id 1 :depend-on 2}]"
                 (vreset! seen-ids #{})
                 (recur (conj r id) rest-ids* (first rest-ids*))))))]
     (mapv id->elem sorted-ids)))
+
+(defonce markdown-heading-pattern #"^#+\s+")
+
+(defn clear-markdown-heading
+  [content]
+  [:pre (string? content)]
+  (string/replace-first content markdown-heading-pattern ""))

+ 26 - 6
deps/db/src/logseq/db.cljs

@@ -84,6 +84,7 @@
 (def journal? entity-util/journal?)
 (def hidden? entity-util/hidden?)
 (def object? entity-util/object?)
+(def asset? entity-util/asset?)
 (def public-built-in-property? db-property/public-built-in-property?)
 
 (defn sort-by-order
@@ -446,12 +447,12 @@
   [db]
   (->>
    (d/datoms db :avet :block/name)
-   (distinct)
-   (map #(d/entity db (:e %)))
-   (filter page?)
-   (remove hidden?)
-   (remove (fn [page]
-             (common-util/uuid-string? (:block/name page))))))
+   (keep (fn [d]
+           (let [e (d/entity db (:e d))]
+             (when (and (page? e)
+                        (not (hidden? e))
+                        (not (common-util/uuid-string? (:block/name e))))
+               e))))))
 
 (defn built-in?
   "Built-in page or block"
@@ -627,3 +628,22 @@
   (assert (string? block-raw-title) "block-raw-title should be a string")
   (or (string/includes? block-raw-title (str "#" (db-content/block-id->special-id-ref (:block/uuid tag))))
       (string/includes? block-raw-title (str "#" db-content/page-ref-special-chars (:block/uuid tag)))))
+
+(defonce node-display-classes
+  #{:logseq.class/Code-block :logseq.class/Math-block :logseq.class/Quote-block})
+
+(defn get-class-ident-by-display-type
+  [display-type]
+  (case display-type
+    :code :logseq.class/Code-block
+    :math :logseq.class/Math-block
+    :quote :logseq.class/Quote-block
+    nil))
+
+(defn get-display-type-by-class-ident
+  [class-ident]
+  (case class-ident
+    :logseq.class/Code-block :code
+    :logseq.class/Math-block :math
+    :logseq.class/Quote-block :quote
+    nil))

+ 34 - 6
deps/db/src/logseq/db/frontend/class.cljs

@@ -1,11 +1,13 @@
 (ns logseq.db.frontend.class
   "Class related fns for DB graphs and frontend/datascript usage"
   (:require [logseq.db.sqlite.util :as sqlite-util]
-            [logseq.db.frontend.db-ident :as db-ident]))
+            [logseq.db.frontend.db-ident :as db-ident]
+            [flatland.ordered.map :refer [ordered-map]]))
 
 (def ^:large-vars/data-var built-in-classes
   "Map of built-in classes for db graphs with their :db/ident as keys"
-  {:logseq.class/Root {:title "Root Tag"}
+  (ordered-map
+   :logseq.class/Root {:title "Root Tag"}
 
    :logseq.class/Task
    {:title "Task"
@@ -32,11 +34,37 @@
    :logseq.class/Asset
    {:title "Asset"
     :properties {;; :logseq.property/icon {:type :tabler-icon :id "file"}
-                 :logseq.property.class/hide-from-node true}
-    :schema {:properties [:logseq.property.asset/type :logseq.property.asset/size :logseq.property.asset/checksum]}}
+                 :logseq.property.class/hide-from-node true
+                 :logseq.property.view/type :logseq.property.view/type.gallery}
+    :schema {:properties [:logseq.property.asset/type :logseq.property.asset/size :logseq.property.asset/checksum]
+             :required-properties [:logseq.property.asset/type :logseq.property.asset/size :logseq.property.asset/checksum]}}
 
-   ;; TODO: Add more classes such as :book, :paper, :movie, :music, :project
-   })
+   :logseq.class/Code-block
+   {:title "Code"
+    :properties {:logseq.property.class/hide-from-node true}
+    :schema {:properties [:logseq.property.node/display-type :logseq.property.code/lang]}}
+
+   :logseq.class/Quote-block
+   {:title "Quote"
+    :properties {:logseq.property.class/hide-from-node true}
+    :schema {:properties [:logseq.property.node/display-type]}}
+
+   :logseq.class/Math-block
+   {:title "Math"
+    :properties {:logseq.property.class/hide-from-node true}
+    :schema {:properties [:logseq.property.node/display-type]}}
+
+   :logseq.class/Pdf-annotation
+   {:title "PDF Annotation"
+    :properties {:logseq.property.class/hide-from-node true}
+    :schema {:properties [:logseq.property/ls-type :logseq.property/hl-color :logseq.property/asset
+                          :logseq.property.pdf/hl-page :logseq.property.pdf/hl-value
+                          :logseq.property/hl-type :logseq.property.pdf/hl-image]
+             :required-properties [:logseq.property/ls-type :logseq.property/hl-color :logseq.property/asset
+                                   :logseq.property.pdf/hl-page :logseq.property.pdf/hl-value]}}
+
+;; TODO: Add more classes such as :book, :paper, :movie, :music, :project)
+   ))
 
 (defn create-user-class-ident-from-name
   "Creates a class :db/ident for a default user namespace.

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

@@ -10,7 +10,7 @@
 
 (defn- replace-ref-with-deleted-block-title
   [block ref-raw-title]
-  (let [block-content (if (= "asset" (:block/type block))
+  (let [block-content (if (entity-util/asset? block)
                         ""
                         (:block/title block))]
     (some-> ref-raw-title

+ 9 - 4
deps/db/src/logseq/db/frontend/entity_plus.cljc

@@ -32,9 +32,9 @@
       (or
        (get (.-kv e) k)
        (let [result (lookup-entity e k default-value)
-             result' (if (string? result)
-                       (db-content/special-id-ref->page-ref result
-                                                            (:block/refs e))
+             refs (:block/refs e)
+             result' (if (and (string? result) refs)
+                       (db-content/special-id-ref->page-ref result refs)
                        result)]
          (or result' default-value))))))
 
@@ -59,8 +59,13 @@
                                  (into {})))
              (lookup-entity e :block/properties nil)))
 
+         ;; cache :block/title
          :block/title
-         (get-block-title e k default-value)
+         (or (:block.temp/cached-title @(.-cache e))
+             (let [title (get-block-title e k default-value)]
+               (vreset! (.-cache e) (assoc @(.-cache e)
+                                           :block.temp/cached-title title))
+               title))
 
          :block/_parent
          (->> (lookup-entity e k default-value)

+ 7 - 1
deps/db/src/logseq/db/frontend/entity_util.cljs

@@ -42,6 +42,12 @@
   [page]
   (= (:block/type page) "journal"))
 
+(defn asset?
+  "Given an entity or map, check if it is an asset block"
+  [entity]
+  ;; Can't use :block/tags because this is used in some perf sensitive fns like ldb/transact!
+  (some? (:logseq.property.asset/type entity)))
+
 (defn hidden?
   [page]
   (when page
@@ -52,4 +58,4 @@
 
 (defn object?
   [node]
-  (seq (:block/tags node)))
+  (seq (:block/tags node)))

+ 27 - 2
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -1,11 +1,13 @@
 (ns logseq.db.frontend.malli-schema
   "Malli schemas and fns for logseq.db.frontend.*"
   (:require [clojure.walk :as walk]
+            [clojure.set :as set]
             [clojure.string :as string]
             [logseq.db.frontend.schema :as db-schema]
             [logseq.db.frontend.property.type :as db-property-type]
             [datascript.core :as d]
             [logseq.db.frontend.property :as db-property]
+            [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.order :as db-order]))
@@ -107,6 +109,13 @@
                      (if (:add-db meta') (partial e db) e)))
                  db-schema))
 
+(def required-properties
+  "Set of properties required by a schema and that are validated directly in a schema instead
+   of validate-property-value"
+  (set/union
+   (set (get-in db-class/built-in-classes [:logseq.class/Asset :schema :required-properties]))
+   #{:logseq.property/created-from-property}))
+
 (defn update-properties-in-ents
   "Prepares properties in entities to be validated by DB schema"
   [db ents]
@@ -114,10 +123,10 @@
    (fn [ent]
      (reduce (fn [m [k v]]
                (if-let [property (and (db-property/property? k)
-                                      ;; This allows block types like property-value-block to require properties in
+                                      ;; This allows schemas like property-value-block to require properties in
                                       ;; their schema that they depend on
-                                      (not= :logseq.property/created-from-property k)
                                       (not (db-property/db-attribute-properties k))
+                                      (not (contains? required-properties k))
                                       (d/entity db k))]
                  (update m :block/properties (fnil conj [])
                          ;; use explicit call to be nbb compatible
@@ -390,6 +399,18 @@
    whiteboard-block
    property-value-block])
 
+(def asset-block
+  "A block tagged with #Asset"
+  (vec
+   (concat
+    [:map]
+    ;; TODO: Derive required property types from existing schema in frontend.property
+    [[:logseq.property.asset/type :string]
+     [:logseq.property.asset/checksum :string]
+     [:logseq.property.asset/size :int]]
+    block-attrs
+    page-or-block-attrs)))
+
 (def file-block
   [:map
    [:block/uuid :uuid]
@@ -415,6 +436,7 @@
 (def Data
   (into
    [:multi {:dispatch (fn [d]
+                        ;; order matters as some block types are a subset of others e.g. :whiteboard
                         (cond
                           (entity-util/property? d)
                           :property
@@ -426,6 +448,8 @@
                           :normal-page
                           (entity-util/page? d)
                           :normal-page
+                          (entity-util/asset? d)
+                          :asset-block
                           (:file/path d)
                           :file-block
                           (:block/uuid d)
@@ -439,6 +463,7 @@
     :hidden hidden-page
     :normal-page normal-page
     :block block
+    :asset-block asset-block
     :file-block file-block
     :db-ident-key-value db-ident-key-val
     :property-value-placeholder property-value-placeholder}))

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

@@ -286,7 +286,7 @@
              :uuid (common-uuid/gen-uuid :db-ident-block-uuid db-ident)})
           [[:logseq.property.view/type.table "Table View"]
            [:logseq.property.view/type.list "List View"]
-           [:logseq.property.view/type.card "Card View"]])}
+           [:logseq.property.view/type.gallery "Gallery View"]])}
 
    :logseq.property.table/sorting {:schema
                                    {:type :coll

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

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

+ 9 - 9
deps/db/src/logseq/db/sqlite/common_db.cljs

@@ -164,9 +164,9 @@
                         (with-parent db)
                         (with-block-refs db)
                         (with-block-link db))
-            block' (if (and page? (not (or children? nested-children?)))
-                     block'
-                     (mark-block-fully-loaded block'))]
+            block' (if (or children? nested-children?)
+                     (mark-block-fully-loaded block')
+                     block')]
         (cond->
          {:block block'
           :properties (property-with-values db block)}
@@ -217,8 +217,7 @@
             (->> (d/datoms db :avet :block/type type')
                  (mapcat (fn [d]
                            (d/datoms db :eavt (:e d))))))
-          [
-           ;; property and class pages are pulled from `get-all-pages` already
+          [;; property and class pages are pulled from `get-all-pages` already
            ;; "property" "class"
            "closed value"]))
 
@@ -256,6 +255,7 @@
                          (when-let [e (d/entity db id)]
                            (d/datoms db :eavt (:db/id e))))
                        [:logseq.kv/db-type :logseq.kv/graph-uuid :logseq.property/empty-placeholder
+                        :logseq.kv/latest-code-lang
                         :logseq.kv/graph-backup-folder])
         favorites (when db-graph? (get-favorites db))
         views (when db-graph? (get-views-data db))
@@ -298,10 +298,10 @@
         (string/replace ":" "+3A+")
         (string/replace "/" "++"))
     (-> db-name
-       (string/replace sqlite-util/db-version-prefix "")
-       (string/replace "/" "_")
-       (string/replace "\\" "_")
-       (string/replace ":" "_"))));; windows
+        (string/replace sqlite-util/db-version-prefix "")
+        (string/replace "/" "_")
+        (string/replace "\\" "_")
+        (string/replace ":" "_"))));; windows
 
 (defn get-db-full-path
   [graphs-dir db-name]

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

@@ -387,7 +387,7 @@
   (let [m
         (->> props
              (mapcat (fn [[prop prop-value]]
-                       (if (#{:icon} prop)
+                       (if (#{:icon :file :file-path :hl-stamp} prop)
                          (do (swap! ignored-properties
                                     conj
                                     {:property prop :value prop-value :location (if name {:page name} {:block title})})
@@ -409,6 +409,8 @@
                            nil
                            :filters
                            (translate-linked-ref-filters prop-value page-names-to-uuids)
+                           :ls-type
+                           [[:logseq.property/ls-type (keyword prop-value)]]
                            ;; else
                            [[(built-in-property-name-to-idents prop) prop-value]]))))
              (into {}))]

+ 34 - 29
deps/outliner/src/logseq/outliner/core.cljs

@@ -207,32 +207,39 @@
 
 (defn- fix-tag-ids
   "Fix or remove tags related when entered via `Escape`"
-  [m {:keys [db-graph?]}]
-  (let [refs (set (map :block/name (seq (:block/refs m))))
+  [m db {:keys [db-graph?]}]
+  (let [refs (set (keep :block/name (seq (:block/refs m))))
         tags (seq (:block/tags m))]
-    (if (and refs tags)
+    (if (and (seq refs) tags)
       (update m :block/tags
               (fn [tags]
-                (cond->>
-                 ;; Update :block/tag to reference ids from :block/refs
-                 (map (fn [tag]
-                        (if (contains? refs (:block/name tag))
-                          (assoc tag :block/uuid
-                                 (:block/uuid
-                                  (first (filter (fn [r] (= (:block/name tag)
-                                                            (:block/name r)))
-                                                 (:block/refs m)))))
-                          tag))
-                      tags)
-
-                  db-graph?
-                  ;; Remove tags changing case with `Escape`
-                  ((fn [tags']
-                     (let [ref-titles (set (map :block/title (:block/refs m)))
-                           lc-ref-titles (set (map string/lower-case ref-titles))]
-                       (remove #(and (not (contains? ref-titles (:block/title %)))
-                                     (contains? lc-ref-titles (string/lower-case (:block/title %))))
-                               tags')))))))
+                (let [tags (map (fn [tag] (or (and (:db/id tag)
+                                                   (let [e (d/entity db (:db/id tag))]
+                                                     (select-keys e [:db/id :block/uuid :block/title :block/name])))
+                                              tag))
+                                tags)]
+                  (cond->>
+                   ;; Update :block/tag to reference ids from :block/refs
+                   (map (fn [tag]
+                          (if (contains? refs (:block/name tag))
+                            (assoc tag :block/uuid
+                                   (:block/uuid
+                                    (first (filter (fn [r] (= (:block/name tag)
+                                                              (:block/name r)))
+                                                   (:block/refs m)))))
+                            tag))
+                        tags)
+
+                    db-graph?
+                    ;; Remove tags changing case with `Escape`
+                    ((fn [tags']
+                       (let [ref-titles (set (map :block/title (:block/refs m)))
+                             lc-ref-titles (set (map string/lower-case ref-titles))]
+                         (remove (fn [tag]
+                                   (when-let [title (:block/title tag)]
+                                     (and (not (contains? ref-titles title))
+                                          (contains? lc-ref-titles (string/lower-case title)))))
+                                 tags'))))))))
       m)))
 
 (defn- remove-tags-when-title-changed
@@ -264,13 +271,16 @@
                          :block.temp/ast-title :block.temp/ast-body :block/level :block.temp/fully-loaded?)
                  common-util/remove-nils
                  block-with-updated-at
-                 (fix-tag-ids {:db-graph? db-based?}))
+                 (fix-tag-ids @conn {:db-graph? db-based?}))
           db @conn
           db-id (:db/id this)
           block-uuid (:block/uuid this)
           eid (or db-id (when block-uuid [:block/uuid block-uuid]))
           block-entity (d/entity db eid)
           page? (ldb/page? block-entity)
+          m* (if (and db-based? (:block/title m*))
+               (update m* :block/title common-util/clear-markdown-heading)
+               m*)
           block-title (:block/title m*)
           page-title-changed? (and page? block-title
                                    (not= block-title (:block/title block-entity)))
@@ -328,11 +338,6 @@
         (swap! txs-state conj
                (dissoc m :db/other-tx)))
 
-      ;; delete heading property for db-based-graphs
-      (when (and db-based? (integer? (:logseq.property/heading block-entity))
-                 (not (some-> (:block/title data) (string/starts-with? "#"))))
-        (swap! txs-state (fn [txs] (conj (vec txs) [:db/retract (:db/id block-entity) :logseq.property/heading]))))
-
       ;; delete tags when title changed
       (when (and db-based? (:block/tags block-entity) block-entity)
         (let [tx-data (remove-tags-when-title-changed block-entity (:block/title m))]

+ 4 - 0
deps/shui/src/logseq/shui/dialog/core.cljs

@@ -108,6 +108,10 @@
   []
   (some-> (last @*modals) (:id)))
 
+(defn get-first-modal-id
+  []
+  (some-> (first @*modals) (:id)))
+
 (defn close!
   ([] (close! (get-last-modal-id)))
   ([id] (update-modal! id :open? false)))

+ 96 - 94
deps/shui/src/logseq/shui/table/core.cljc

@@ -112,116 +112,116 @@
   [& prop-and-children]
   (let [[prop children] (get-prop-and-children prop-and-children)]
     [:div (merge {:class "ls-table w-full caption-bottom text-sm table-fixed"}
-                   prop)
+                 prop)
      children]))
 
 ;; FIXME: ux
 (defn- use-sticky-element!
   [^js/HTMLElement container target-ref]
   (rum/use-effect!
-    (fn []
-      (let [^js el (rum/deref target-ref)
-            ^js cls (.-classList el)
-            *ticking? (volatile! false)
-            el-top (-> el (.getBoundingClientRect) (.-top))
-            head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
-            translate (fn [offset]
-                        (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)"))
-                        (if (zero? offset)
-                          (.remove cls "translated")
-                          (.add cls "translated")))
-            *last-offset (volatile! 0)
-            handle (fn []
-                     (let [scroll-top (js/parseInt (.-scrollTop container))
-                           offset (if (> (+ scroll-top head-top) el-top)
-                                    (+ (- scroll-top el-top) head-top 1) 0)
-                           offset (js/parseInt offset)
-                           last-offset @*last-offset]
-                       (if (and (not (zero? last-offset))
-                             (not= offset last-offset))
-                         (let [dir (if (neg? (- offset last-offset)) -1 1)]
-                           (loop [offset' (+ last-offset dir)]
-                             (translate offset')
-                             (if (and (not= offset offset')
-                                   (< (abs (- offset offset')) 100))
-                               (recur (+ offset' dir))
-                               (translate offset))))
-                         (translate offset))
-                       (vreset! *last-offset offset)))
-            handler (fn [^js e]
-                      (when (not @*ticking?)
-                        (js/window.requestAnimationFrame
-                          #(do (handle) (vreset! *ticking? false)))
-                        (vreset! *ticking? true)))]
-        (.addEventListener container "scroll" handler)
-        #(.removeEventListener container "scroll" handler)))
-    []))
+   (fn []
+     (let [^js el (rum/deref target-ref)
+           ^js cls (.-classList el)
+           *ticking? (volatile! false)
+           el-top (-> el (.getBoundingClientRect) (.-top))
+           head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
+           translate (fn [offset]
+                       (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)"))
+                       (if (zero? offset)
+                         (.remove cls "translated")
+                         (.add cls "translated")))
+           *last-offset (volatile! 0)
+           handle (fn []
+                    (let [scroll-top (js/parseInt (.-scrollTop container))
+                          offset (if (> (+ scroll-top head-top) el-top)
+                                   (+ (- scroll-top el-top) head-top 1) 0)
+                          offset (js/parseInt offset)
+                          last-offset @*last-offset]
+                      (if (and (not (zero? last-offset))
+                               (not= offset last-offset))
+                        (let [dir (if (neg? (- offset last-offset)) -1 1)]
+                          (loop [offset' (+ last-offset dir)]
+                            (translate offset')
+                            (if (and (not= offset offset')
+                                     (< (abs (- offset offset')) 100))
+                              (recur (+ offset' dir))
+                              (translate offset))))
+                        (translate offset))
+                      (vreset! *last-offset offset)))
+           handler (fn [^js e]
+                     (when (not @*ticking?)
+                       (js/window.requestAnimationFrame
+                        #(do (handle) (vreset! *ticking? false)))
+                       (vreset! *ticking? true)))]
+       (.addEventListener container "scroll" handler)
+       #(.removeEventListener container "scroll" handler)))
+   []))
 
 ;; FIXME: another solution for the sticky header
 (defn- use-sticky-element2!
   [^js/HTMLDivElement target-ref]
   (rum/use-effect!
-    (fn []
-      (let [^js target (rum/deref target-ref)
-            ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container))
-            ^js target-cls (.-classList target)
-            ^js table (.closest target ".ls-table-rows")
-            ^js table-footer (some-> table (.querySelector ".ls-table-footer"))
-            ^js page-el (.closest target ".page-inner")
-            *ticking? (volatile! false)
-            *el-top (volatile! (-> target (.getBoundingClientRect) (.-top)))
-            head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
-            update-target-top! (fn []
-                                 (when (not (.contains target-cls "ls-fixed"))
-                                   (vreset! *el-top (+ (-> target (.getBoundingClientRect) (.-top))
+   (fn []
+     (let [^js target (rum/deref target-ref)
+           ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container))
+           ^js target-cls (.-classList target)
+           ^js table (.closest target ".ls-table-rows")
+           ^js table-footer (some-> table (.querySelector ".ls-table-footer"))
+           ^js page-el (.closest target ".page-inner")
+           *ticking? (volatile! false)
+           *el-top (volatile! (-> target (.getBoundingClientRect) (.-top)))
+           head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
+           update-target-top! (fn []
+                                (when (not (.contains target-cls "ls-fixed"))
+                                  (vreset! *el-top (+ (-> target (.getBoundingClientRect) (.-top))
                                                       (.-scrollTop container)))))
-            update-footer! (fn []
-                             (when table-footer
-                               (set! (. (.-style table-footer) -width) (str (.-scrollWidth table) "px"))))
-            update-target! (fn []
-                             (if (.contains target-cls "ls-fixed")
-                               (let [^js rect (-> table (.getBoundingClientRect))
-                                     width (.-clientWidth table)
-                                     left (.-left rect)]
-                                 (set! (. (.-style target) -width) (str width "px"))
-                                 (set! (. (.-style target) -left) (str left "px")))
-                               (do
-                                 (set! (. (.-style target) -width) "auto")
-                                 (set! (. (.-style target) -left) "0px")))
+           update-footer! (fn []
+                            (when table-footer
+                              (set! (. (.-style table-footer) -width) (str (.-scrollWidth table) "px"))))
+           update-target! (fn []
+                            (if (.contains target-cls "ls-fixed")
+                              (let [^js rect (-> table (.getBoundingClientRect))
+                                    width (.-clientWidth table)
+                                    left (.-left rect)]
+                                (set! (. (.-style target) -width) (str width "px"))
+                                (set! (. (.-style target) -left) (str left "px")))
+                              (do
+                                (set! (. (.-style target) -width) "auto")
+                                (set! (. (.-style target) -left) "0px")))
                              ;; update scroll
-                             (set! (. target -scrollLeft) (.-scrollLeft table)))
+                            (set! (. target -scrollLeft) (.-scrollLeft table)))
             ;; target observer
-            target-observe! (fn []
-                              (let [scroll-top (js/parseInt (.-scrollTop container))
-                                    table-in-top (+ scroll-top head-top)
-                                    table-bottom (.-bottom (.getBoundingClientRect table))
-                                    fixed? (and (> table-bottom (+ head-top 90))
-                                             (> table-in-top @*el-top))]
-                                (if fixed?
-                                  (.add target-cls "ls-fixed")
-                                  (.remove target-cls "ls-fixed"))
-                                (update-target!)))
-            target-observe-handle! (fn [^js _e]
-                                     (when (not @*ticking?)
-                                       (js/window.requestAnimationFrame
-                                         #(do (target-observe!) (vreset! *ticking? false)))
-                                       (vreset! *ticking? true)))
-            resize-observer (js/ResizeObserver. update-target!)
-            page-resize-observer (js/ResizeObserver. (fn [] (update-target-top!)))]
+           target-observe! (fn []
+                             (let [scroll-top (js/parseInt (.-scrollTop container))
+                                   table-in-top (+ scroll-top head-top)
+                                   table-bottom (.-bottom (.getBoundingClientRect table))
+                                   fixed? (and (> table-bottom (+ head-top 90))
+                                               (> table-in-top @*el-top))]
+                               (if fixed?
+                                 (.add target-cls "ls-fixed")
+                                 (.remove target-cls "ls-fixed"))
+                               (update-target!)))
+           target-observe-handle! (fn [^js _e]
+                                    (when (not @*ticking?)
+                                      (js/window.requestAnimationFrame
+                                       #(do (target-observe!) (vreset! *ticking? false)))
+                                      (vreset! *ticking? true)))
+           resize-observer (js/ResizeObserver. update-target!)
+           page-resize-observer (js/ResizeObserver. (fn [] (update-target-top!)))]
         ;; events
-        (.observe resize-observer container)
-        (.observe resize-observer table)
-        (some->> page-el (.observe page-resize-observer))
-        (.addEventListener container "scroll" target-observe-handle!)
-        (.addEventListener table "scroll" update-target!)
-        (.addEventListener table "resize" update-target!)
-        (update-footer!)
+       (.observe resize-observer container)
+       (.observe resize-observer table)
+       (some->> page-el (.observe page-resize-observer))
+       (.addEventListener container "scroll" target-observe-handle!)
+       (.addEventListener table "scroll" update-target!)
+       (.addEventListener table "resize" update-target!)
+       (update-footer!)
 
         ;; teardown
-        #(do (.removeEventListener container "scroll" target-observe!)
-           (.disconnect resize-observer)
-           (.disconnect page-resize-observer))))
-    []))
+       #(do (.removeEventListener container "scroll" target-observe!)
+            (.disconnect resize-observer)
+            (.disconnect page-resize-observer))))
+   []))
 
 (rum/defc table-header < rum/static
   [& prop-and-children]
@@ -244,7 +244,7 @@
   [& prop-and-children]
   (let [[prop children] (get-prop-and-children prop-and-children)]
     [:div.ls-table-row.flex.flex-row.items-center (merge {:class "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted bg-gray-01 items-stretch"}
-                               prop)
+                                                         prop)
      children]))
 
 (rum/defc table-cell < rum/static
@@ -272,3 +272,5 @@
              :style {:z-index 101}}
             prop)
      children]))
+
+(def table-sort-rows impl/sort-rows)

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

@@ -58,9 +58,7 @@
   (sort-rows rows sorting columns))
 
 (defn rows
-  [{:keys [columns sorting row-filter]
+  [{:keys [row-filter]
     :as opts}]
-  (let [rows' (:rows opts)
-        rows' (if row-filter (filter row-filter rows') rows')]
-    (cond-> rows'
-      (seq sorting) (sort-rows sorting columns))))
+  (let [rows' (:rows opts)]
+    (if row-filter (filter row-filter rows') rows')))

+ 8 - 5
scripts/src/logseq/tasks/db_graph/create_graph_with_properties.cljs

@@ -57,7 +57,8 @@
                                (swap! closed-values assoc % (:value val))
                                [:block/uuid (:uuid val)])
         object-uuid (random-uuid)
-        get-closed-value #(get @closed-values %)]
+        get-closed-value #(get @closed-values %)
+        timestamp (common-util/time-ms)]
     {:pages-and-blocks
      (vec
       (concat
@@ -102,7 +103,7 @@
           {:block/title "date property block" :build/properties {:date [:page (date-journal-title today)]}}
           {:block/title "date-many property block" :build/properties {:date-many #{[:page (date-journal-title today)]
                                                                                    [:page (date-journal-title yesterday)]}}}
-          {:block/title "datetime property block" :build/properties {:datetime (common-util/time-ms)}}]}
+          {:block/title "datetime property block" :build/properties {:datetime timestamp}}]}
         {:page {:block/title "Block Property Queries"}
          :blocks
          [(query "(property default \"haha\")")
@@ -119,7 +120,8 @@
           (query "(property node-without-classes [[Page 1]])")
           (query "(property node-many [[Page object]])")
           (query (str "(property date " (page-ref/->page-ref (string/capitalize (date-journal-title today))) ")"))
-          (query (str "(property date-many " (page-ref/->page-ref (string/capitalize (date-journal-title yesterday))) ")"))]}
+          (query (str "(property date-many " (page-ref/->page-ref (string/capitalize (date-journal-title yesterday))) ")"))
+          (query (str "(property datetime "  timestamp ")"))]}
 
         ;; Page property pages and queries
         {:page {:block/title "default page" :build/properties {:default "yolo"}}}
@@ -138,7 +140,7 @@
         {:page {:block/title "date page" :build/properties {:date [:page (date-journal-title today)]}}}
         {:page {:block/title "date-many page" :build/properties {:date-many #{[:page (date-journal-title today)]
                                                                               [:page (date-journal-title yesterday)]}}}}
-        {:page {:block/title "datetime page" :build/properties {:datetime (common-util/time-ms)}}}
+        {:page {:block/title "datetime page" :build/properties {:datetime timestamp}}}
 
         {:page {:block/title "Page Property Queries"}
          :blocks
@@ -156,7 +158,8 @@
           (query "(page-property node-without-classes [[Page 1]])")
           (query "(page-property node-many [[Page object]])")
           (query (str "(page-property date " (page-ref/->page-ref (string/capitalize (date-journal-title today))) ")"))
-          (query (str "(page-property date-many " (page-ref/->page-ref (string/capitalize (date-journal-title yesterday))) ")"))]}]))
+          (query (str "(page-property date-many " (page-ref/->page-ref (string/capitalize (date-journal-title yesterday))) ")"))
+          (query (str "(page-property datetime "  timestamp ")"))]}]))
 
      :classes {:TestClass {}}
 

+ 14 - 19
src/main/frontend/commands.cljs

@@ -348,10 +348,11 @@
        ["Quote"
         (quote-block-steps)
         "Create a quote block"
-        :icon/quote-block]
+        :icon/quote]
        ["Math block"
         (math-block-steps)
-        "Create a latex block"]]
+        "Create a latex block"
+        :icon/math]]
 
       (headings)
 
@@ -817,33 +818,27 @@
         (property-file/goto-properties-end-when-file-based format current-input)
         (cursor/move-cursor-backward current-input 3)))))
 
-(defonce markdown-heading-pattern #"^#+\s+")
-(defn set-markdown-heading
+(defn file-based-set-markdown-heading
   [content heading]
   (let [heading-str (apply str (repeat heading "#"))]
-    (if (util/safe-re-find markdown-heading-pattern content)
-      (string/replace-first content
-                            markdown-heading-pattern
-                            (str heading-str " "))
+    (if (util/safe-re-find common-util/markdown-heading-pattern content)
+      (common-util/clear-markdown-heading content)
       (str heading-str " " (string/triml content)))))
 
-(defn clear-markdown-heading
-  [content]
-  [:pre (string? content)]
-  (string/replace-first content
-                        markdown-heading-pattern
-                        ""))
+(def clear-markdown-heading common-util/clear-markdown-heading)
 
 (defmethod handle-step :editor/set-heading [[_ heading]]
   (when-let [input-id (state/get-edit-input-id)]
     (when-let [current-input (gdom/getElement input-id)]
       (let [current-block (state/get-edit-block)
             format (:block/format current-block)]
-        (if (= format :markdown)
-          (let [edit-content (gobj/get current-input "value")
-                new-content (set-markdown-heading edit-content heading)]
-            (state/set-edit-content! input-id new-content))
-          (state/pub-event! [:editor/set-org-mode-heading current-block heading]))))))
+        (if (config/db-based-graph?)
+          (state/pub-event! [:editor/set-heading current-block heading])
+          (if (= format :markdown)
+            (let [edit-content (gobj/get current-input "value")
+                  new-content (file-based-set-markdown-heading edit-content heading)]
+              (state/set-edit-content! input-id new-content))
+            (state/pub-event! [:editor/set-heading current-block heading])))))))
 
 (defmethod handle-step :editor/search-page [_]
   (state/set-editor-action! :page-search))

+ 26 - 17
src/main/frontend/components/all_pages.cljs

@@ -2,22 +2,26 @@
   "All pages"
   (:require [frontend.components.block :as component-block]
             [frontend.components.page :as component-page]
+            [frontend.components.icon :as icon-component]
             [frontend.components.views :as views]
             [frontend.context.i18n :refer [t]]
+            [frontend.db :as db]
             [frontend.handler.page :as page-handler]
             [frontend.state :as state]
             [logseq.db :as ldb]
-            [frontend.db :as db]
+            [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [rum.core :as rum]
-            [logseq.shui.ui :as shui]))
+            [frontend.ui :as ui]))
 
 (defn- columns
   []
   (->> [{:id :block/title
          :name (t :block/name)
          :cell (fn [_table row _column]
-                 (component-block/page-cp {:show-icon? true} row))
+                 [:div.flex.flex-row.items-center.gap-1
+                  [:span.opacity-50 (icon-component/get-node-icon-cp row {:color? true})]
+                  (component-block/page-cp {} row)])
          :type :string}
         {:id :block/type
          :name "Type"
@@ -41,7 +45,8 @@
 (rum/defc all-pages < rum/static
   []
   (let [db (db/get-db)
-        [data set-data!] (rum/use-state (get-all-pages))
+        [data set-data!] (rum/use-state nil)
+        [loading? set-loading!] (rum/use-state true)
         columns' (views/build-columns {} (columns)
                                       {:with-object-name? false})
         view-entity (first (ldb/get-all-pages-views db))]
@@ -50,19 +55,23 @@
        (when-let [^js worker @state/*db-worker]
          (p/let [result-str (.get-page-refs-count worker (state/get-current-repo))
                  result (ldb/read-transit-str result-str)
+                 data (get-all-pages)
                  data (map (fn [row] (assoc row :block.temp/refs-count (get result (:db/id row) 0))) data)]
-           (set-data! data))))
+           (set-data! data)
+           (set-loading! false))))
      [])
     [:div.ls-all-pages.w-full.mx-auto
-     (views/view view-entity {:data data
-                              :set-data! set-data!
-                              :title-key :all-pages/table-title
-                              :columns columns'
-                              :on-delete-rows (fn [table selected-rows]
-                                                (shui/dialog-open!
-                                                 (component-page/batch-delete-dialog
-                                                  selected-rows false
-                                                  (fn []
-                                                    (when-let [f (get-in table [:data-fns :set-row-selection!])]
-                                                      (f {}))
-                                                    (set-data! (get-all-pages))))))})]))
+     (if loading?
+       (ui/skeleton)
+       (views/view view-entity {:data data
+                                :set-data! set-data!
+                                :title-key :all-pages/table-title
+                                :columns columns'
+                                :on-delete-rows (fn [table selected-rows]
+                                                  (shui/dialog-open!
+                                                   (component-page/batch-delete-dialog
+                                                    selected-rows false
+                                                    (fn []
+                                                      (when-let [f (get-in table [:data-fns :set-row-selection!])]
+                                                        (f {}))
+                                                      (set-data! (get-all-pages))))))}))]))

+ 70 - 44
src/main/frontend/components/block.cljs

@@ -89,8 +89,7 @@
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]
-            [shadow.loader :as loader]
-            [frontend.storage :as storage]))
+            [shadow.loader :as loader]))
 
 ;; local state
 (defonce *dragging?
@@ -381,11 +380,12 @@
                              (reset! size value))
             :onPointerUp (fn []
                            (when (and @size @*resizing-image?)
-                             (when-let [block-id (:block/uuid config)]
+                             (when-let [block-id (or (:block/uuid config)
+                                                     (some-> config :block (:block/uuid)))]
                                (let [size (bean/->clj @size)]
                                  (editor-handler/resize-image! config block-id metadata full-text size))))
                            (when @*resizing-image?
-                           ;; TODO:​ need a better way to prevent the clicking to edit current block
+                             ;; TODO:​ need a better way to prevent the clicking to edit current block
                              (js/setTimeout #(reset! *resizing-image? false) 200)))
             :onClick (fn [e]
                        (when @*resizing-image? (util/stop e)))}
@@ -471,6 +471,13 @@
           (asset-loader @src
                         #(resizable-image config title @src metadata full_text true))
 
+          (and db-based? (contains? (common-config/text-formats) ext) (:asset-block config))
+          (let [file-name (str (:block/title (:asset-block config)) "." (name ext))]
+            [:a.asset-ref.is-plaintext
+             {:href @src
+              :download file-name}
+             file-name])
+
           (contains? (common-config/text-formats) ext)
           [:a.asset-ref.is-plaintext {:href (rfe/href :file {:path path})
                                       :on-click (fn [_event]
@@ -598,6 +605,8 @@
              (state/get-left-sidebar-open?))
     (ui-handler/close-left-sidebar!)))
 
+(declare block-title)
+
 (rum/defcs ^:large-vars/cleanup-todo page-inner <
   (rum/local false ::mouse-down?)
   (rum/local false ::hover?)
@@ -657,10 +666,12 @@
       :on-key-up (fn [e] (when (and e (= (.-key e) "Enter") (not meta-click?))
                            (state/clear-edit!)
                            (open-page-ref config page-entity e page-name contents-page?)))}
-     (when show-icon?
-       (when-let [icon (icon-component/get-node-icon-cp page-entity {:color? true :not-text-or-page? true})]
-         [:span.mr-1
-          icon]))
+     (when (and show-icon? (not tag?))
+       (let [own-icon (get page-entity (pu/get-pid :logseq.property/icon))
+             emoji? (and (map? own-icon) (= (:type own-icon) :emoji))]
+         (when-let [icon (icon-component/get-node-icon-cp page-entity {:color? true :not-text-or-page? true})]
+           [:span {:class (when emoji? "mr-1")}
+            icon])))
      [:span
       (if (and (coll? children) (seq children))
         (for [child children]
@@ -715,9 +726,7 @@
                                      s (if tag? (str "#" s) s)]
                                  (if (ldb/page? page-entity)
                                    s
-                                   (let [inline-list (gp-mldoc/inline->edn (first (string/split-lines s))
-                                                                           (mldoc/get-default-config (get page-entity :block/format :markdown)))]
-                                     (->elem :span (map-inline config inline-list))))))]
+                                   (block-title config page-entity))))]
           page-component))]]))
 
 (rum/defc popup-preview-impl
@@ -830,21 +839,30 @@
      (->ref id)]))
 
 (rum/defcs page-cp-inner < db-mixins/query rum/reactive
+  {:init (fn [state]
+           (let [page (last (:rum/args state))
+                 *result (atom nil)]
+             (p/let [result (if (e/entity? page)
+                              page
+                              (p/let [page-name (or (:block/uuid page)
+                                                    (when-let [s (:block/name page)]
+                                                      (let [s (string/trim s)
+                                                            s (if (string/starts-with? s db-content/page-ref-special-chars)
+                                                                (common-util/safe-subs s 2)
+                                                                s)]
+                                                        s)))
+                                      query-result (db-async/<get-block (state/get-current-repo) page-name {:children? false})]
+                                (if (e/entity? query-result)
+                                  query-result
+                                  (:block query-result))))]
+               (reset! *result result))
+             (assoc state :*entity *result)))}
   "Component for a page. `page` argument contains :block/name which can be (un)sanitized page name.
    Keys for `config`:
    - `:preview?`: Is this component under preview mode? (If true, `page-preview-trigger` won't be registered to this `page-cp`)"
   [state {:keys [label children preview? disable-preview? show-non-exists-page? tag?] :as config} page]
-  (let [entity (if (e/entity? page)
-                 page
-                 ;; Use uuid when available to uniquely identify case sensitive contexts
-                 (db/get-page (or (:block/uuid page)
-                                  (when-let [s (:block/name page)]
-                                    (let [s (string/trim s)
-                                          s (if (string/starts-with? s db-content/page-ref-special-chars)
-                                              (common-util/safe-subs s 2)
-                                              s)]
-                                      s)))))]
-    (let [entity (when entity (db/sub-block (:db/id entity)))]
+  (when-let [entity' (rum/react (:*entity state))]
+    (let [entity (db/sub-block (:db/id entity'))]
       (cond
         entity
         (if (or (ldb/page? entity) (:block/tags entity))
@@ -951,16 +969,17 @@
 
 (rum/defc page-reference < rum/reactive
   "Component for page reference"
-  [html-export? s {:keys [nested-link? show-brackets? id] :as config} label]
-  (when s
-    (let [s (string/trim s)
-          s (if (string/starts-with? s db-content/page-ref-special-chars)
-              (common-util/safe-subs s 2)
-              s)
+  [html-export? uuid-or-title* {:keys [nested-link? show-brackets? id] :as config} label]
+  (when uuid-or-title*
+    (let [uuid-or-title (if (string? uuid-or-title*)
+                          (as-> (string/trim uuid-or-title*) s
+                            (if (string/starts-with? s db-content/page-ref-special-chars)
+                              (common-util/safe-subs s 2)
+                              s))
+                          uuid-or-title*)
           show-brackets? (if (some? show-brackets?) show-brackets? (state/show-brackets?))
-          block-uuid (:block/uuid config)
           contents-page? (= "contents" (string/lower-case (str id)))
-          block (db/get-page s)
+          block (db/get-page uuid-or-title)
           config' (assoc config
                          :label (mldoc/plain->text label)
                          :contents-page? contents-page?
@@ -970,26 +989,30 @@
         (and asset? (img-audio-video? block))
         (asset-cp config block)
 
-        (string/ends-with? s ".excalidraw")
-        [:div.draw {:on-click (fn [e]
-                                (.stopPropagation e))}
-         (excalidraw s block-uuid)]
-
         (or (ldb/page? block) (:block/tags block))
         [:span.page-reference
-         {:data-ref s}
+         {:data-ref (str uuid-or-title)}
          (when (and (or show-brackets? nested-link?)
                     (not html-export?)
                     (not contents-page?))
            [:span.text-gray-500.bracket page-ref/left-brackets])
-         (page-cp config' {:block/name s})
+         (page-cp config' (if (uuid? uuid-or-title)
+                            {:block/uuid uuid-or-title}
+                            {:block/name uuid-or-title}))
          (when (and (or show-brackets? nested-link?)
                     (not html-export?)
                     (not contents-page?))
            [:span.text-gray-500.bracket page-ref/right-brackets])]
 
+        (and (string? uuid-or-title) (string/ends-with? uuid-or-title ".excalidraw"))
+        [:div.draw {:on-click (fn [e]
+                                (.stopPropagation e))}
+         (excalidraw uuid-or-title (:block/uuid config))]
+
         :else
-        (page-cp config' {:block/name s})))))
+        (page-cp config' (if (uuid? uuid-or-title)
+                           {:block/uuid uuid-or-title}
+                           {:block/name uuid-or-title}))))))
 
 (defn- latex-environment-content
   [name option content]
@@ -1182,7 +1205,7 @@
 
                    (if (and (not (util/mobile?))
                             (not (:preview? config))
-                            (not (:modal/show? @state/state))
+                            (not (shui-dialog/has-modal?))
                             (nil? block-type))
                      (block-reference-preview inner
                                               {:repo repo :config config :id block-id})
@@ -2092,11 +2115,14 @@
 (declare block-content)
 
 (declare src-cp)
-(declare block-title)
 
 (rum/defc ^:large-vars/cleanup-todo text-block-title
-  [config {:block/keys [marker pre-block? properties] :as block}]
-  (let [block-ast-title (:block.temp/ast-title block)
+  [config {:block/keys [format marker pre-block? properties] :as block}]
+  (let [block (if-not (:block.temp/ast-title block)
+                (merge block (block/parse-title-and-body uuid format pre-block?
+                                                         (:block/title block)))
+                block)
+        block-ast-title (:block.temp/ast-title block)
         config (assoc config :block block)
         level (:level config)
         slide? (boolean (:slide? config))
@@ -2208,7 +2234,7 @@
       (:raw-title? config)
       (text-block-title (dissoc config :raw-title?) block)
 
-      (= "asset" (:block/type block))
+      (ldb/asset? block)
       [:div.grid.grid-cols-1.justify-items-center
        (asset-cp config block)
        (when (img-audio-video? block)
@@ -3687,7 +3713,7 @@
                                                                            (.setOption cm "mode" mode)
                                                                            (throw (ex-info "code mode not found"
                                                                                            {:lang lang})))
-                                                                         (storage/set :latest-code-lang lang)
+                                                                         (db/transact! [(ldb/kv :logseq.kv/latest-code-lang lang)])
                                                                          (db-property-handler/set-block-property!
                                                                           (:db/id block) :logseq.property.code/lang lang))))
                                                    {:align :end})))}

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

@@ -2,7 +2,6 @@
   @apply grid grid-cols-[repeat(auto-fill,18rem)] gap-4 content;
 
   .ls-card-item {
-    @apply shadow-md rounded p-2 overflow-y-scroll;
-    height: 15rem;
+    @apply dark:shadow-gray-700 shadow-md rounded-md p-2 overflow-y-scroll h-[15rem];
   }
 }

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

@@ -13,7 +13,7 @@
     (when (seq children)
       [:ul
        (for [child (sort-by :block/title children)]
-         (let [title [:li.ml-2 (block/page-reference false (:block/title child) {:show-brackets? false} nil)]]
+         (let [title [:li.ml-2 (block/page-reference false (:block/uuid child) {:show-brackets? false} nil)]]
            (if (seq (:logseq.property/_parent child))
              (ui/foldable
               title

+ 4 - 0
src/main/frontend/components/cmdk/cmdk.css

@@ -3,4 +3,8 @@
     @apply flex w-full px-3 py-2 gap-2 justify-between
     bg-gray-03 border-t border-gray-05;
   }
+}
+
+.ls-dialog-cmdk {
+  @apply p-0 w-auto !max-w-fit overflow-hidden;
 }

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

@@ -381,7 +381,7 @@
 (defn- copy-block-ref [state]
   (when-let [block-uuid (some-> state state->highlighted-item :source-block :block/uuid)]
     (editor-handler/copy-block-ref! block-uuid block-ref/->block-ref)
-    (state/close-modal!)))
+    (shui/dialog-close! :ls-dialog-cmdk)))
 
 (defmulti handle-action (fn [action _state _event] action))
 
@@ -395,7 +395,7 @@
   (when-let [page-name (get-highlighted-page-uuid-or-name state)]
     (let [page (db/get-page page-name)]
       (route-handler/redirect-to-page! (:block/uuid page)))
-    (state/close-modal!)))
+    (shui/dialog-close! :ls-dialog-cmdk)))
 
 (defmethod handle-action :open-block [_ state _event]
   (when-let [block-id (some-> state state->highlighted-item :source-block :block/uuid)]
@@ -421,21 +421,21 @@
               (route-handler/redirect-to-page! block-id)
               :else
               (route-handler/redirect-to-page! (:block/uuid page) {:anchor (str "ls-block-" block-id)}))
-            (state/close-modal!)))))))
+            (shui/dialog-close! :ls-dialog-cmdk)))))))
 
 (defmethod handle-action :open-page-right [_ state _event]
   (when-let [page-name (get-highlighted-page-uuid-or-name state)]
     (let [page (db/get-page page-name)]
       (when page
         (editor-handler/open-block-in-sidebar! (:block/uuid page))))
-    (state/close-modal!)))
+    (shui/dialog-close! :ls-dialog-cmdk)))
 
 (defmethod handle-action :open-block-right [_ state _event]
   (when-let [block-uuid (some-> state state->highlighted-item :source-block :block/uuid)]
     (p/let [repo (state/get-current-repo)
             _ (db-async/<get-block repo block-uuid :children? false)]
       (editor-handler/open-block-in-sidebar! block-uuid)
-      (state/close-modal!))))
+      (shui/dialog-close! :ls-dialog-cmdk))))
 
 (defn- open-file
   [file-path]
@@ -466,7 +466,7 @@
       (cond
         (:file-path item) (do
                             (open-file (:file-path item))
-                            (state/close-modal!))
+                            (shui/dialog-close! :ls-dialog-cmdk))
         (and graph-view? page? (not shift?)) (do
                                                (state/add-graph-search-filter! @(::input state))
                                                (reset! (::input state) ""))
@@ -485,7 +485,7 @@
     (when-let [action (:action command)]
       (action)
       (when-not (contains? #{:graph/open :graph/remove :dev/replace-graph-with-db-file :ui/toggle-settings :go/flashcards} (:id command))
-        (state/close-modal!)))))
+        (shui/dialog-close! :ls-dialog-cmdk)))))
 
 (defmethod handle-action :create [_ state _event]
   (let [item (state->highlighted-item state)
@@ -501,7 +501,7 @@
                                                       :create-first-block? false})
                      create-whiteboard? (whiteboard-handler/<create-new-whiteboard-and-redirect! @!input)
                      create-page? (page-handler/<create! @!input {:redirect? true}))]
-      (state/close-modal!)
+      (shui/dialog-close! :ls-dialog-cmdk)
       (when (and create-class? result)
         (state/pub-event! [:class/configure result])))))
 
@@ -527,7 +527,7 @@
 (defmethod handle-action :theme [_ state]
   (when-let [item (some-> state state->highlighted-item)]
     (js/LSPluginCore.selectTheme (bean/->js (:source-theme item)))
-    (state/close-modal!)))
+    (shui/dialog-close!)))
 
 (defmethod handle-action :default [_ state event]
   (when-let [action (state->action state)]
@@ -726,7 +726,7 @@
     (cond
       (and meta? enter?)
       (let [repo (state/get-current-repo)]
-        (state/close-modal!)
+        (shui/dialog-close! :ls-dialog-cmdk)
         (state/sidebar-add-block! repo input :search))
       as-keydown? (if meta?
                     (show-more)

+ 22 - 11
src/main/frontend/components/container.cljs

@@ -8,7 +8,6 @@
             [frontend.components.plugins :as plugins]
             [frontend.components.repo :as repo]
             [frontend.components.right-sidebar :as right-sidebar]
-            [frontend.components.select :as select]
             [frontend.components.theme :as theme]
             [frontend.components.dnd :as dnd-component]
             [frontend.components.icon :as icon]
@@ -355,6 +354,24 @@
                :icon "calendar"
                :shortcut :go/journals})))
 
+         (when db-based?
+           (let [tag-uuid (:block/uuid (db/entity :logseq.class/Task))]
+             (sidebar-item
+              {:class "task-view-nav"
+               :title (t :left-side-bar/tasks)
+               :href (rfe/href :page {:name tag-uuid})
+               :active (= (str tag-uuid) (get-in route-match [:path-params :name]))
+               :icon "hash"})))
+
+         (when db-based?
+           (let [tag-uuid (:block/uuid (db/entity :logseq.class/Asset))]
+             (sidebar-item
+              {:class "asset-view-nav"
+               :title (t :left-side-bar/assets)
+               :href (rfe/href :page {:name tag-uuid})
+               :active (= (str tag-uuid) (get-in route-match [:path-params :name]))
+               :icon "hash"})))
+
          (when enable-whiteboards?
            (when (or config/dev? (not db-based?))
              (sidebar-item
@@ -832,9 +849,7 @@
   []
   nil)
 
-(rum/defcs ^:large-vars/cleanup-todo root-container <
-  (mixins/modal :modal/show?)
-  rum/reactive
+(rum/defcs ^:large-vars/cleanup-todo root-container < rum/reactive
   (mixins/event-mixin
    (fn [state]
      (mixins/listen state js/window "pointerdown" hide-context-menu-and-clear-selection)
@@ -855,8 +870,7 @@
                     (fn [_e]
                       (state/set-state! :editor/latest-shortcut nil)))))
   [state route-match main-content']
-  (let [{:keys [open-fn]} state
-        current-repo (state/sub :git/current-repo)
+  (let [current-repo (state/sub :git/current-repo)
         granted? (state/sub [:nfs/user-granted? (state/get-current-repo)])
         theme (state/sub :ui/theme)
         accent-color (some-> (state/sub :ui/radix-color) (name))
@@ -929,8 +943,7 @@
       [:div.#app-container
        [:div#left-container
         {:class (if (state/sub :ui/sidebar-open?) "overflow-hidden" "w-full")}
-        (header/header {:open-fn        open-fn
-                        :light?         light?
+        (header/header {:light?         light?
                         :current-repo   current-repo
                         :logged?        logged?
                         :page?          page?
@@ -960,13 +973,11 @@
        [:div#app-single-container]]
 
       (ui/notification)
-      (ui/modal)
-      (ui/sub-modal)
+
       (shui-toaster/install-toaster)
       (shui-dialog/install-modals)
       (shui-popup/install-popups)
 
-      (select/select-modal)
       (custom-context-menu)
       (plugins/custom-js-installer
        {:t t

+ 5 - 6
src/main/frontend/components/header.cljs

@@ -117,7 +117,7 @@
                      :icon (ui/icon "apps")})
 
                   {:title (t :appearance)
-                   :options {:on-click #(state/pub-event! [:modal/toggle-appearance-modal])}
+                   :options {:on-click #(state/pub-event! [:ui/toggle-appearance])}
                    :icon (ui/icon "color-swatch")}
 
                   (when current-repo
@@ -153,7 +153,7 @@
                                :class "w-full"}})]
                  (concat page-menu-and-hr)
                  (remove nil?)))]
-    [:button.button.icon.toolbar-dots-btn
+    [:button#dots-menu.button.icon.toolbar-dots-btn
      {:title (t :header/more)
       :on-pointer-down (fn [^js e]
                          (shui/popup-show! (.-target e)
@@ -203,8 +203,8 @@
   [t]
   (let [[downloaded, set-downloaded] (rum/use-state nil)
         _ (rum/use-effect!
-            (fn []
-              (when-let [channel (and (util/electron?) "auto-updater-downloaded")]
+           (fn []
+             (when-let [channel (and (util/electron?) "auto-updater-downloaded")]
                (let [callback (fn [_ args]
                                 (js/console.debug "[new-version downloaded] args:" args)
                                 (let [args (bean/->clj args)]
@@ -223,11 +223,10 @@
          (svg/reload 16) [:strong (t :updater/quit-and-install)]]]])))
 
 (rum/defc ^:large-vars/cleanup-todo header < rum/reactive
-  [{:keys [open-fn current-repo default-home new-block-mode]}]
+  [{:keys [current-repo default-home new-block-mode]}]
   (let [_ (state/sub [:user/info :UserGroups])
         electron-mac? (and util/mac? (util/electron?))
         left-menu (left-menu-button {:on-click (fn []
-                                                 (open-fn)
                                                  (state/set-left-sidebar-open!
                                                   (not (:ui/left-sidebar-open? @state/state))))})
         custom-home-page? (and (state/custom-home-page?)

+ 32 - 17
src/main/frontend/components/objects.cljs

@@ -19,7 +19,8 @@
             [frontend.util :as util]
             [frontend.ui :as ui]
             [logseq.common.config :as common-config]
-            [frontend.components.filepicker :as filepicker]))
+            [frontend.components.filepicker :as filepicker]
+            [clojure.string :as string]))
 
 (defn- get-class-objects
   [class]
@@ -99,18 +100,36 @@
      :on-click (fn [] (create-view! class "" views set-view-entity! set-views!))}
     (ui/icon "plus" {}))])
 
+(defn- build-asset-file-column
+  [config]
+  {:id :file
+   :name "File"
+   :type :string
+   :header views/header-cp
+   :cell (fn [_table row _column]
+           (when-let [asset-cp (state/get-component :block/asset-cp)]
+             [:div.block-content (asset-cp (assoc config :disable-resize? true) row)]))
+   :disable-hide? true})
+
 (rum/defc class-objects-inner < rum/static
   [config class objects properties]
   (let [[loading? set-loading?] (rum/use-state nil)
         [view-entity set-view-entity!] (rum/use-state class)
         [views set-views!] (rum/use-state [class])
         [data set-data!] (rum/use-state objects)
-        columns (views/build-columns (assoc config :class class) properties)]
-
-    (rum/use-effect!
-     (fn []
-       (set-data! objects))
-     [objects])
+        columns* (views/build-columns config properties {:add-tags-column? (= (:db/ident class) :logseq.class/Root)})
+        columns (cond
+                  (= (:db/ident class) :logseq.class/Pdf-annotation)
+                  (remove #(contains? #{:logseq.property/ls-type} (:id %)) columns*)
+                  (= (:db/ident class) :logseq.class/Asset)
+                  (remove #(contains? #{:logseq.property.asset/checksum} (:id %)) columns*)
+                  :else
+                  columns*)
+        columns (if (= (:db/ident class) :logseq.class/Asset)
+                  ;; Insert in front of tag's properties
+                  (let [[before-cols after-cols] (split-with #(not (string/starts-with? (str (namespace (:id %))) "logseq.property")) columns)]
+                    (concat before-cols [(build-asset-file-column config)] after-cols))
+                  columns)]
 
     (rum/use-effect!
      (fn []
@@ -130,7 +149,8 @@
              (set-loading? false)))))
      [])
 
-    (when (false? loading?)
+    (if loading?
+      (ui/skeleton)
       [:div.flex.flex-col.gap-2.mt-2
 
        (ui/foldable
@@ -149,7 +169,9 @@
                                                            [:div.font-medium "Add assets"]
                                                            (filepicker/picker
                                                             {:on-change (fn [_e files]
-                                                                          (editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true))})])))
+                                                                          (p/do!
+                                                                           (editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true)
+                                                                           (shui/dialog-close!)))})])))
                                                      #(add-new-class-object! class set-data!))
                                   :show-add-property? true
                                   :add-property! (fn []
@@ -177,9 +199,7 @@
   (when class
     (let [class (db/sub-block (:db/id class))
           config {:container-id (:container-id state)}
-          properties (cond->> (outliner-property/get-class-properties class)
-                       (= :logseq.class/Root (:db/ident class))
-                       (concat [(db/entity :block/tags)]))
+          properties (outliner-property/get-class-properties class)
           repo (state/get-current-repo)
           objects (->> (db-model/sub-class-objects repo (:db/id class))
                        (map (fn [row] (assoc row :id (:db/id row)))))]
@@ -206,11 +226,6 @@
         [data set-data!] (rum/use-state objects)
         columns (views/build-columns config properties)]
 
-    (rum/use-effect!
-     (fn []
-       (set-data! objects))
-     [objects])
-
     (rum/use-effect!
      (fn []
        (set-loading? true)

+ 25 - 26
src/main/frontend/components/page.cljs

@@ -47,7 +47,8 @@
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]
-            [frontend.rum :as frontend-rum]))
+            [frontend.rum :as frontend-rum]
+            [dommy.core :as dom]))
 
 (defn- get-page-name
   [state]
@@ -171,20 +172,26 @@
           "Click here to edit..."]]]))))
 
 (rum/defc add-button
-  [args]
-  [:div.flex-1.flex-col.rounded-sm.add-button-link-wrap
-   {:on-click (fn [] (editor-handler/api-insert-new-block! "" args))
-    :on-key-down (fn [e]
-                   (when (= "Enter" (util/ekey e))
-                     (editor-handler/api-insert-new-block! "" args))
-                   (util/stop e))
-    :tab-index 0}
-   [:div.flex.flex-row
-    [:div.block {:style {:height      20
-                         :width       20
-                         :margin-left 2}}
-     [:a.add-button-link.block
-      (ui/icon "circle-plus")]]]])
+  [args container-id]
+  (let [*bullet-ref (rum/use-ref nil)]
+    [:div.flex-1.flex-col.rounded-sm.add-button-link-wrap
+     {:on-click (fn [e]
+                  (util/stop e)
+                  (state/set-state! :editor/container-id container-id)
+                  (editor-handler/api-insert-new-block! "" args))
+      :on-mouse-over #(dom/add-class! (rum/deref *bullet-ref) "opacity-100")
+      :on-mouse-leave #(dom/remove-class! (rum/deref *bullet-ref) "opacity-100")
+      :on-key-down (fn [e]
+                     (util/stop e)
+                     (when (= "Enter" (util/ekey e))
+                       (state/set-state! :editor/container-id container-id)
+                       (editor-handler/api-insert-new-block! "" args)))
+      :tab-index 0}
+     [:div.flex.flex-row
+      [:div.flex.items-center {:style {:height 28
+                                       :margin-left 2}}
+       [:span.bullet-container.cursor.opacity-0.transition-opacity.ease-in.duration-100 {:ref *bullet-ref}
+        [:span.bullet]]]]]))
 
 (rum/defcs page-blocks-cp < rum/reactive db-mixins/query
   {:will-mount (fn [state]
@@ -249,7 +256,7 @@
                (let [args (if block-id
                             {:block-uuid block-id}
                             {:page page-name})]
-                 (add-button args)))]))]])))
+                 (add-button args (:container-id config))))]))]])))
 
 (rum/defc today-queries < rum/reactive
   [repo today? sidebar?]
@@ -532,14 +539,6 @@
   (rum/local false ::all-collapsed?)
   (rum/local false ::control-show?)
   (rum/local nil   ::current-page)
-  (rum/local false ::main-ready?)
-  {:did-mount (fn [state]
-                (assoc state ::main-ready-timer
-                       (js/setTimeout #(reset! (::main-ready? state) true) 32)))
-   :will-unmount (fn [state]
-                   (some-> (::main-ready-timer state)
-                           (js/clearTimeout))
-                   state)}
   [state {:keys [repo page-name preview? sidebar? linked-refs? unlinked-refs? config] :as option}]
   (when-let [path-page-name (get-path-page-name state page-name)]
     (let [current-repo (state/sub :git/current-repo)
@@ -620,8 +619,8 @@
                                                       :container-id (:container-id state)
                                                       :whiteboard? whiteboard?}))]])
 
-         (when (and (not preview?) @(::main-ready? state))
-           [:div.ls-page-appendix-els {:style {:padding-left 9}}
+         (when (not preview?)
+           [:div {:style {:padding-left 9}}
             (when today?
               (today-queries repo today? sidebar?))
 

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

@@ -1,13 +1,4 @@
 .cp__page {
-  &-inner-wrap {
-    &.is-node-page:not([data-ready]) {
-      .ls-page-blocks,
-      .ls-page-appendix-els {
-        @apply invisible;
-      }
-    }
-  }
-
   &-publish-actions {
     background-color: var(--ls-primary-background-color);
     padding: 50px 0;

File diff suppressed because it is too large
+ 349 - 350
src/main/frontend/components/plugins.cljs


+ 3 - 9
src/main/frontend/components/plugins.css

@@ -628,19 +628,13 @@
 
 .cp__themes {
   &-installed {
-    margin: -2rem;
-    outline: none;
-    padding: .5rem;
+    @apply outline-none -m-4;
 
     .it {
-      user-select: none;
-      border: 1px solid transparent;
-      margin-bottom: 4px;
-      cursor: pointer;
-      opacity: .8;
+      @apply select-none border-transparent mb-1 cursor-pointer opacity-80;
 
       .name {
-        font-weight: 600;
+        @apply font-semibold;
       }
 
       &:hover, &.is-active {

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

@@ -580,8 +580,7 @@
         {:keys [all-classes classes-properties]} (outliner-property/get-block-classes-properties (db/get-db) (:db/id block))
         classes-properties-set (set classes-properties)
         block-own-properties (->> properties
-                                  (remove (fn [[id _]] (classes-properties-set id)))
-                                  remove-built-in-or-other-position-properties)
+                                  (remove (fn [[id _]] (classes-properties-set id))))
         root-block? (= (:id opts) (str (:block/uuid block)))
         state-hide-empty-properties? (:ui/hide-empty-properties? (state/get-config))
         ;; This section produces own-properties and full-hidden-properties
@@ -619,8 +618,7 @@
                              (let [cur-properties (->> (db-property/get-class-ordered-properties class)
                                                        (map :db/ident)
                                                        (remove properties)
-                                                       (remove hide-with-property-id)
-                                                       remove-built-in-or-other-position-properties)]
+                                                       (remove hide-with-property-id))]
                                (recur (rest classes)
                                       (set/union properties (set cur-properties))
                                       (if (seq cur-properties)

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

@@ -414,7 +414,7 @@
   {:properties {:icon :layout-distribute-horizontal :title "Block properties"}
    :block-left {:icon :layout-align-right :title "Beginning of the block"}
    :block-right {:icon :layout-align-left :title "End of the block"}
-   :block-below {:icon :layout-align-top :title "Below of the block"}})
+   :block-below {:icon :layout-align-top :title "Below the block"}})
 
 (rum/defc ui-position-sub-pane
   [property {:keys [id set-sub-open! _position]}]

+ 5 - 9
src/main/frontend/components/property/value.cljs

@@ -880,7 +880,7 @@
        (and (= type :default) (nil? (:block/title value)))
        [:div.jtrigger (property-empty-btn-value property)]
 
-       (= type :default)
+       (#{:default :entity} type)
        (property-block-value value block property page-cp)
 
        :else
@@ -906,14 +906,10 @@
       (icon-row block editing?)
       (if (and select-type?'
                (not (and (not closed-values?) (= type :date))))
-        (let [value (if (and (nil? value) (= :logseq.property.view/type (:db/ident property)))
-                      ;; TODO: remove this hack once default value is supported
-                      (db/entity :logseq.property.view/type.table)
-                      value)]
-          (single-value-select block property value
-                               (fn [] (select-item property type value opts))
-                               select-opts
-                               (assoc opts :editing? editing?)))
+        (single-value-select block property value
+                             (fn [] (select-item property type value opts))
+                             select-opts
+                             (assoc opts :editing? editing?))
         (case type
           (:date :datetime)
           (property-value-date-picker block property value (merge opts {:editing? editing?}))

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

@@ -59,7 +59,7 @@
            (util/hiccup-keywordize result))
 
          (and db-graph? (not (:built-in-query? config)))
-         (query-view/query-result (assoc config :id (:db/id current-block))
+         (query-view/query-result (assoc config :id (str (:block/uuid current-block)))
                                   current-block result)
 
          (and (not db-graph?)

+ 3 - 2
src/main/frontend/components/query/view.cljs

@@ -12,7 +12,7 @@
        distinct
        (map db/entity)
        (ldb/sort-by-order)
-       (views/build-columns config)))
+       ((fn [cs] (views/build-columns config cs {:add-tags-column? false})))))
 
 (defn- result->entities
   [result]
@@ -39,5 +39,6 @@
      (views/view view-entity
                  {:title-key :views.table/live-query-title
                   :data result'
-                  :set-data! #(reset! *result %)
+                  :set-data! (fn [data]
+                               (when (seq data) (reset! *result data)))
                   :columns columns'})]))

+ 17 - 11
src/main/frontend/components/select.cljs

@@ -1,13 +1,14 @@
 (ns frontend.components.select
   "Generic component for fuzzy searching items to select an item. See
   select-config to add a new use or select-type for this component. To use the
-  new select-type, set :ui/open-select to the select-type. See
-  :graph/open command for an example."
+  new select-type, create an event that calls `select/dialog-select!` with the
+  select-type. See the :graph/open command for a full example."
   (:require [frontend.modules.shortcut.core :as shortcut]
             [frontend.context.i18n :refer [t]]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.ui :as ui]
+            [logseq.shui.ui :as shui]
             [frontend.util :as util]
             [frontend.util.text :as text-util]
             [rum.core :as rum]
@@ -58,7 +59,7 @@
                        (reset! (::selected-choices new-state) choices))
                      new-state))
    :will-unmount (fn [state]
-                   (state/set-state! [:ui/open-select] nil)
+                   (shui/dialog-close! :ls-select-modal)
                    state)}
   [state {:keys [items limit on-chosen empty-placeholder
                  prompt-key input-default-placeholder close-modal?
@@ -233,14 +234,19 @@
                                :original-graph original-graph})))))
     :on-chosen #(dev-common-handler/import-chosen-graph (:graph %))}})
 
-(rum/defc select-modal < rum/reactive
-  []
-  (when-let [select-type (state/sub [:ui/open-select])]
-    (let [select-type-config (get (select-config) select-type)]
-      (state/set-modal!
+(defn dialog-select!
+  [select-type]
+  (when select-type
+    (let [select-type-config (get (select-config) select-type)
+          on-chosen' (:on-chosen select-type-config)]
+      (shui/dialog-open!
        #(select (-> select-type-config
+                    (assoc :on-chosen (fn [it]
+                                        (on-chosen' it)
+                                        (shui/dialog-close-all!)))
                     (select-keys [:on-chosen :empty-placeholder :prompt-key])
                     (assoc :items ((:items-fn select-type-config)))))
-       {:fullscreen? false
-        :close-btn?  false}))
-    nil))
+       {:id :ls-select-modal
+        :close-btn?  false
+        :align :top
+        :content-props {:class "ls-dialog-select"}}))))

+ 129 - 134
src/main/frontend/components/settings.cljs

@@ -185,11 +185,11 @@
 
 (defn edit-global-config-edn []
   (row-with-button-action
-    {:left-label   (t :settings-page/custom-global-configuration)
-     :button-label (t :settings-page/edit-global-config-edn)
-     :href         (rfe/href :file {:path (global-config-handler/global-config-path)})
-     :on-click     ui-handler/toggle-settings-modal!
-     :-for         "global_config_edn"}))
+   {:left-label   (t :settings-page/custom-global-configuration)
+    :button-label (t :settings-page/edit-global-config-edn)
+    :href         (rfe/href :file {:path (global-config-handler/global-config-path)})
+    :on-click     ui-handler/toggle-settings-modal!
+    :-for         "global_config_edn"}))
 
 (defn edit-custom-css []
   (row-with-button-action
@@ -229,8 +229,8 @@
    [:div
     [:div.rounded-md.sm:max-w-xs
      (ui/toggle wide-mode?
-       ui-handler/toggle-wide-mode!
-       true)]]
+                ui-handler/toggle-wide-mode!
+                true)]]
    (when (not (or (util/mobile?) (mobile-util/native-platform?)))
      [:div {:style {:text-align "right"}}
       (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-wide-mode))])])
@@ -246,13 +246,13 @@
                 tt (string/capitalize t)
                 active? (= font t)]]
       (shui/button
-        {:variant :secondary
-         :class (when active? "border-primary border-[2px]")
-         :on-click #(state/set-editor-font! t)}
-        [:span.flex.flex-col
-         {:class (str "ls-font-" t)}
-         [:strong "Ag"]
-         [:small tt]]))]])
+       {:variant :secondary
+        :class (when active? "border-primary border-[2px]")
+        :on-click #(state/set-editor-font! t)}
+       [:span.flex.flex-col
+        {:class (str "ls-font-" t)}
+        [:strong "Ag"]
+        [:small tt]]))]])
 
 (rum/defcs switch-spell-check-row < rum/reactive
   [state t]
@@ -263,13 +263,13 @@
      [:div
       [:div.rounded-md.sm:max-w-xs
        (ui/toggle
-         enabled?
-         (fn []
-           (state/set-state! [:electron/user-cfgs :spell-check] (not enabled?))
-           (p/then (ipc/ipc :userAppCfgs :spell-check (not enabled?))
-                   #(when (js/confirm (t :relaunch-confirm-to-work))
-                      (js/logseq.api.relaunch))))
-         true)]]]))
+        enabled?
+        (fn []
+          (state/set-state! [:electron/user-cfgs :spell-check] (not enabled?))
+          (p/then (ipc/ipc :userAppCfgs :spell-check (not enabled?))
+                  #(when (js/confirm (t :relaunch-confirm-to-work))
+                     (js/logseq.api.relaunch))))
+        true)]]]))
 
 (rum/defcs switch-git-auto-commit-row < rum/reactive
   [state t]
@@ -280,13 +280,13 @@
      [:div
       [:div.rounded-md.sm:max-w-xs
        (ui/toggle
-         enabled?
-         (fn []
-           (state/set-state! [:electron/user-cfgs :git/disable-auto-commit?] enabled?)
-           (p/do!
-            (ipc/ipc :userAppCfgs :git/disable-auto-commit? enabled?)
-            (ipc/ipc :setGitAutoCommit)))
-         true)]]]))
+        enabled?
+        (fn []
+          (state/set-state! [:electron/user-cfgs :git/disable-auto-commit?] enabled?)
+          (p/do!
+           (ipc/ipc :userAppCfgs :git/disable-auto-commit? enabled?)
+           (ipc/ipc :setGitAutoCommit)))
+        true)]]]))
 
 (rum/defcs switch-git-commit-on-close-row < rum/reactive
   [state t]
@@ -297,11 +297,11 @@
      [:div
       [:div.rounded-md.sm:max-w-xs
        (ui/toggle
-         enabled?
-         (fn []
-           (state/set-state! [:electron/user-cfgs :git/commit-on-close?] (not enabled?))
-           (ipc/ipc :userAppCfgs :git/commit-on-close? (not enabled?)))
-         true)]]]))
+        enabled?
+        (fn []
+          (state/set-state! [:electron/user-cfgs :git/commit-on-close?] (not enabled?))
+          (ipc/ipc :userAppCfgs :git/commit-on-close? (not enabled?)))
+        true)]]]))
 
 (rum/defcs git-auto-commit-seconds < rum/reactive
   [state t]
@@ -319,9 +319,9 @@
                             (if (and (number? value)
                                      (< 0 value (inc 86400)))
                               (p/do!
-                                (state/set-state! [:electron/user-cfgs :git/auto-commit-seconds] value)
-                                (ipc/ipc :userAppCfgs :git/auto-commit-seconds value)
-                                (ipc/ipc :setGitAutoCommit))
+                               (state/set-state! [:electron/user-cfgs :git/auto-commit-seconds] value)
+                               (ipc/ipc :userAppCfgs :git/auto-commit-seconds value)
+                               (ipc/ipc :setGitAutoCommit))
                               (when-let [elem (gobj/get event "target")]
                                 (notification/show!
                                  [:div "Invalid value! Must be a number between 1 and 86400"]
@@ -381,44 +381,43 @@
                                 none? (= color :none)]]
                       [:div.flex.items-center {:style {:height 28}}
                        (ui/tippy
-                         {:html (case color
-                                  :none [:p {:style {:max-width "300px"}}
-                                         "Cancel accent color. This is currently in beta stage and mainly used for compatibility with custom themes."]
-                                  :logseq "Logseq classical color"
-                                  (str (name color) " color"))
-                          :delay [1000, 100]}
-                         (shui/button
-                           {:class "w-5 h-5 px-1 rounded-full flex justify-center items-center transition ease-in duration-100 hover:cursor-pointer hover:opacity-100"
-                            :auto-focus (and _in-modal? active?)
-                            :style {:background-color (colors/variable color :09)
-                                    :outline-color (colors/variable color (if active? :07 :06))
-                                    :outline-width (if active? "4px" "1px")
-                                    :outline-style :solid
-                                    :opacity (if active? 1 0.5)}
-                            :variant :text
-                            :on-click (fn [_e] (state/set-color-accent! color))}
-                           [:strong
-                            {:class (if none? "h-0.5 w-full bg-red-700"
-                                              "w-2 h-2 rounded-full transition ease-in duration-100")
-                             :style {:background-color (if-not none? (str "var(--rx-" (name color) "-07)") "")
-                                     :opacity (if (or none? active?) 1 0)}}]))
-                       ])]]
-
-    [:<>
+                        {:html (case color
+                                 :none [:p {:style {:max-width "300px"}}
+                                        "Cancel accent color. This is currently in beta stage and mainly used for compatibility with custom themes."]
+                                 :logseq "Logseq classical color"
+                                 (str (name color) " color"))
+                         :delay [1000, 100]}
+                        (shui/button
+                         {:class "w-5 h-5 px-1 rounded-full flex justify-center items-center transition ease-in duration-100 hover:cursor-pointer hover:opacity-100"
+                          :auto-focus (and _in-modal? active?)
+                          :style {:background-color (colors/variable color :09)
+                                  :outline-color (colors/variable color (if active? :07 :06))
+                                  :outline-width (if active? "4px" "1px")
+                                  :outline-style :solid
+                                  :opacity (if active? 1 0.5)}
+                          :variant :text
+                          :on-click (fn [_e] (state/set-color-accent! color))}
+                         [:strong
+                          {:class (if none? "h-0.5 w-full bg-red-700"
+                                      "w-2 h-2 rounded-full transition ease-in duration-100")
+                           :style {:background-color (if-not none? (str "var(--rx-" (name color) "-07)") "")
+                                   :opacity (if (or none? active?) 1 0)}}]))])]]
+
+    [:div
      (row-with-button-action
-       {:left-label (t :settings-page/accent-color)
-        :description (t :settings-page/accent-color-alert)
-        :-for "toggle_radix_theme"
-        :desc (when-not _in-modal?
-                [:span.pl-6 (ui/render-keyboard-shortcut
-                              (shortcut-helper/gen-shortcut-seq :ui/customize-appearance))])
-        :stretch (boolean _in-modal?)
-        :action pick-theme})]))
-
-(rum/defc modal-appearance-inner < rum/reactive
+      {:left-label (t :settings-page/accent-color)
+       :-for "toggle_radix_theme"
+       :desc (when-not _in-modal?
+               [:span.pl-6 (ui/render-keyboard-shortcut
+                            (shortcut-helper/gen-shortcut-seq :ui/customize-appearance))])
+       :stretch (boolean _in-modal?)
+       :action pick-theme})
+     [:div.text-sm.opacity-50
+      (t :settings-page/accent-color-alert)]]))
+
+(rum/defc appearance < rum/reactive
   []
-  [:div.cp__settings-appearance-modal-inner
-   [:h1.text-2xl.font-bold.pb-2.pt-1 (t :appearance)]
+  [:div#appearance_settings.cp__settings-appearance-modal-inner.w-96.p-4.shadow-xl
    (theme-modes-row t)
    (editor-font-family-row t (state/sub :ui/editor-font))
    (toggle-wide-mode-row t (state/sub :ui/wide-mode?))
@@ -431,11 +430,11 @@
     {:for "custom_date_format"}
     (t :settings-page/custom-date-format)
     (when-not (config/db-based-graph? (state/get-current-repo))
-     (ui/tippy {:html        (t :settings-page/custom-date-format-warning)
-                :class       "tippy-hover ml-2"
-                :interactive true
-                :disabled    false}
-               (svg/info)))]
+      (ui/tippy {:html        (t :settings-page/custom-date-format-warning)
+                 :class       "tippy-hover ml-2"
+                 :interactive true
+                 :disabled    false}
+                (svg/info)))]
    [:div.mt-1.sm:mt-0.sm:col-span-2
     [:div.max-w-lg.rounded-md
      [:select.form-select.is-small
@@ -447,18 +446,16 @@
                       (when-not (string/blank? format)
                         (if db-based?
                           (p/do!
-                            (property-handler/set-block-property! repo
-                                                                :logseq.class/Journal
-                                                                :logseq.property.journal/title-format
-                                                                format)
-                            (notification/show! "Please refresh the app for this change to take effect"))
+                           (property-handler/set-block-property! repo
+                                                                 :logseq.class/Journal
+                                                                 :logseq.property.journal/title-format
+                                                                 format)
+                           (notification/show! "Please refresh the app for this change to take effect"))
                           (do
                             (config-handler/set-config! :journal/page-title-format format)
                             (notification/show!
                              [:div (t :settings-page/custom-date-format-notification)]
                              :warning false)))
-
-                        (state/close-modal!)
                         (shui/dialog-close-all!)
                         (when-not db-based? (route-handler/redirect! {:to :graphs})))))}
       (for [format (sort (date/journal-title-formatters))]
@@ -589,13 +586,13 @@
    [:div.mt-1.sm:mt-0.sm:col-span-2
     [:div
      (ui/button
-       (t :settings)
-       :class "text-sm"
-       :style {:margin-top "0px"}
-       :on-click
-       (fn []
-         (state/close-settings!)
-         (route-handler/redirect! {:to :zotero-setting})))]]])
+      (t :settings)
+      :class "text-sm"
+      :style {:margin-top "0px"}
+      :on-click
+      (fn []
+        (state/close-settings!)
+        (route-handler/redirect! {:to :zotero-setting})))]]])
 
 (defn auto-push-row [_t current-repo enable-git-auto-push?]
   (when (and current-repo (string/starts-with? current-repo "https://"))
@@ -611,7 +608,7 @@
           (t :settings-page/disable-sentry)
           (not instrument-disabled?)
           (fn [] (instrument/disable-instrument
-                   (not instrument-disabled?)))
+                  (not instrument-disabled?)))
           [:span.text-sm.opacity-50 (t :settings-page/disable-sentry-desc)]))
 
 (defn clear-cache-row [t]
@@ -712,8 +709,8 @@
      (t :settings-page/auto-chmod)
      enabled?
      #(do
-       (state/set-state! [:electron/user-cfgs :feature/enable-automatic-chmod?] (not enabled?))
-       (ipc/ipc :userAppCfgs :feature/enable-automatic-chmod? (not enabled?)))
+        (state/set-state! [:electron/user-cfgs :feature/enable-automatic-chmod?] (not enabled?))
+        (ipc/ipc :userAppCfgs :feature/enable-automatic-chmod? (not enabled?)))
      [:span.text-sm.opacity-50 (t :settings-page/auto-chmod-desc)])))
 
 (rum/defcs native-titlebar-row < rum/reactive
@@ -930,18 +927,17 @@
      ; storage-usage-formatted "GB of " storage-limit "GB total storage"
      ; [:strong.text-white " (" storage-percent-formatted "%)"]]))
 
-
 (rum/defc settings-account-usage-graphs [_pro-account? graph-usage]
   (when (< 0 (count graph-usage))
-   [:div.grid.gap-3 {:style {:grid-template-columns (str "repeat(" (count graph-usage) ", 1fr)")}}
-    (for [{:keys [name used-percent]} graph-usage
-          :let [color (if (<= 100 used-percent) "bg-red-500" "bg-blue-500")]]
-     [:div.rounded-full.w-full.h-2 {:class "bg-black/50"
-                                    :tooltip name}
-      [:div.rounded-full.h-2 {:class color
-                              :style {:width (str used-percent "%")
-                                      :min-width "0.5rem"
-                                      :max-width "100%"}}]])]))
+    [:div.grid.gap-3 {:style {:grid-template-columns (str "repeat(" (count graph-usage) ", 1fr)")}}
+     (for [{:keys [name used-percent]} graph-usage
+           :let [color (if (<= 100 used-percent) "bg-red-500" "bg-blue-500")]]
+       [:div.rounded-full.w-full.h-2 {:class "bg-black/50"
+                                      :tooltip name}
+        [:div.rounded-full.h-2 {:class color
+                                :style {:width (str used-percent "%")
+                                        :min-width "0.5rem"
+                                        :max-width "100%"}}]])]))
 
 (rum/defc ^:large-vars/cleanup-todo settings-account < rum/reactive
   []
@@ -991,30 +987,30 @@
                                             :icon "cloud"
                                             :on-click #(fs/maybe-onboarding-show :sync-initiate)}))]]
          (when has-subscribed?
-          [:<>
-           [:div "Billing"]
-           [:div.col-span-2.flex.flex-col.gap-4
-            (cond
+           [:<>
+            [:div "Billing"]
+            [:div.col-span-2.flex.flex-col.gap-4
+             (cond
               ;; If there is no expiration date, print the renewal date
-              (and renewal-date (nil? expiration-date))
-              [:div
-               [:strong.font-semibold "Next billing date: "
-                (date/get-locale-string renewal-date)]]
+               (and renewal-date (nil? expiration-date))
+               [:div
+                [:strong.font-semibold "Next billing date: "
+                 (date/get-locale-string renewal-date)]]
               ;; If the expiration date is in the future, word it as such
-              (< (js/Date.) expiration-date)
-              [:div
-               [:strong.font-semibold "Pro plan expires on: "
-                (date/get-locale-string expiration-date)]]
+               (< (js/Date.) expiration-date)
+               [:div
+                [:strong.font-semibold "Pro plan expires on: "
+                 (date/get-locale-string expiration-date)]]
               ;; Otherwise, ind
-              :else
-              [:div
-               [:strong.font-semibold "Pro plan expired on: "
-                (date/get-locale-string expiration-date)]])
-
-            [:div (ui/button "Open invoices" {:class "w-full h-8 p-1 justify-center"
-                                              :disabled true
-                                              :background "gray"
-                                              :icon "receipt"})]]])
+               :else
+               [:div
+                [:strong.font-semibold "Pro plan expired on: "
+                 (date/get-locale-string expiration-date)]])
+
+             [:div (ui/button "Open invoices" {:class "w-full h-8 p-1 justify-center"
+                                               :disabled true
+                                               :background "gray"
+                                               :icon "receipt"})]]])
          [:div "Profile"]
          [:div.col-span-2.grid.grid-cols-2.gap-4
           [:div.flex.flex-col.gap-2.box-border {:class "basis-1/2"}
@@ -1169,7 +1165,6 @@
      ;;     ;; features
      ;;     ]])
 
-
 (def DEFAULT-ACTIVE-TAB-STATE (if config/ENABLE-SETTINGS-ACCOUNT-TAB [:account :account] [:general :general]))
 
 (rum/defc settings-effect
@@ -1177,14 +1172,14 @@
   [active]
 
   (rum/use-effect!
-    (fn []
-      (let [active (and (sequential? active) (name (first active)))
-            ^js ds (.-dataset js/document.body)]
-        (if active
-          (set! (.-settingsTab ds) active)
-          (js-delete ds "settingsTab"))
-        #(js-delete ds "settingsTab")))
-    [active])
+   (fn []
+     (let [active (and (sequential? active) (name (first active)))
+           ^js ds (.-dataset js/document.body)]
+       (if active
+         (set! (.-settingsTab ds) active)
+         (js-delete ds "settingsTab"))
+       #(js-delete ds "settingsTab")))
+   [active])
 
   [:<>])
 

+ 78 - 78
src/main/frontend/components/shortcut.cljs

@@ -1,34 +1,34 @@
 (ns frontend.components.shortcut
-  (:require [clojure.string :as string]
-            [rum.core :as rum]
+  (:require [cljs-bean.core :as bean]
+            [clojure.string :as string]
             [frontend.context.i18n :refer [t]]
-            [cljs-bean.core :as bean]
-            [frontend.state :as state]
-            [frontend.search :as search]
-            [frontend.ui :as ui]
-            [logseq.shui.ui :as shui]
-            [frontend.rum :as r]
-            [goog.events :as events]
-            [promesa.core :as p]
             [frontend.handler.notification :as notification]
+            [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.data-helper :as dh]
-            [frontend.util :as util]
             [frontend.modules.shortcut.utils :as shortcut-utils]
-            [frontend.modules.shortcut.config :as shortcut-config])
+            [frontend.rum :as r]
+            [frontend.search :as search]
+            [frontend.ui :as ui]
+            [frontend.util :as util]
+            [goog.events :as events]
+            [logseq.shui.dialog.core :as shui-dialog]
+            [logseq.shui.ui :as shui]
+            [promesa.core :as p]
+            [rum.core :as rum])
   (:import [goog.events KeyHandler]))
 
 (defonce categories
-         (vector :shortcut.category/basics
-                 :shortcut.category/navigating
-                 :shortcut.category/block-editing
-                 :shortcut.category/block-command-editing
-                 :shortcut.category/block-selection
-                 :shortcut.category/formatting
-                 :shortcut.category/toggle
-                 :shortcut.category/whiteboard
-                 :shortcut.category/plugins
-                 :shortcut.category/others))
+  (vector :shortcut.category/basics
+          :shortcut.category/navigating
+          :shortcut.category/block-editing
+          :shortcut.category/block-command-editing
+          :shortcut.category/block-selection
+          :shortcut.category/formatting
+          :shortcut.category/toggle
+          :shortcut.category/whiteboard
+          :shortcut.category/plugins
+          :shortcut.category/others))
 
 (defonce *refresh-sentry (atom 0))
 (defn refresh-shortcuts-list! [] (reset! *refresh-sentry (inc @*refresh-sentry)))
@@ -47,24 +47,24 @@
   (let [keypressed? (not= "" keystroke)]
 
     (rum/use-effect!
-      (fn []
-        (let [key-handler (KeyHandler. js/document)]
+     (fn []
+       (let [key-handler (KeyHandler. js/document)]
           ;; setup
-          (util/profile
-            "[shortcuts] unlisten*"
-            (shortcut/unlisten-all! true))
-          (events/listen key-handler "key"
-                         (fn [^js e]
-                           (.preventDefault e)
-                           (set-keystroke! #(util/trim-safe (str % (shortcut/keyname e))))))
+         (util/profile
+          "[shortcuts] unlisten*"
+          (shortcut/unlisten-all! true))
+         (events/listen key-handler "key"
+                        (fn [^js e]
+                          (.preventDefault e)
+                          (set-keystroke! #(util/trim-safe (str % (shortcut/keyname e))))))
 
           ;; teardown
-          #(do
-             (util/profile
-               "[shortcuts] listen*"
-               (shortcut/listen-all!))
-             (.dispose key-handler))))
-      [])
+         #(do
+            (util/profile
+             "[shortcuts] listen*"
+             (shortcut/listen-all!))
+            (.dispose key-handler))))
+     [])
 
     [:div.keyboard-filter-record
      [:h2
@@ -118,36 +118,36 @@
 
      ;; keyboard filter
      (ui/dropdown
-       (fn [{:keys [toggle-fn]}]
-         [:a.flex.items-center.icon-link
-          {:on-click toggle-fn} (ui/icon "keyboard")
+      (fn [{:keys [toggle-fn]}]
+        [:a.flex.items-center.icon-link
+         {:on-click toggle-fn} (ui/icon "keyboard")
 
-          (when-not (string/blank? keystroke)
-            (ui/point "bg-red-600.absolute" 4 {:style {:right -2 :top -2}}))])
-       (fn [{:keys [close-fn]}]
-         (keyboard-filter-record-inner keystroke set-keystroke! close-fn))
-       {:outside?      true
-        :trigger-class "keyboard-filter"})
+         (when-not (string/blank? keystroke)
+           (ui/point "bg-red-600.absolute" 4 {:style {:right -2 :top -2}}))])
+      (fn [{:keys [close-fn]}]
+        (keyboard-filter-record-inner keystroke set-keystroke! close-fn))
+      {:outside?      true
+       :trigger-class "keyboard-filter"})
 
      ;; other filter
      (ui/dropdown-with-links
-       (fn [{:keys [toggle-fn]}]
-         [:a.flex.items-center.icon-link.relative
-          {:on-click toggle-fn}
-          (ui/icon "filter")
+      (fn [{:keys [toggle-fn]}]
+        [:a.flex.items-center.icon-link.relative
+         {:on-click toggle-fn}
+         (ui/icon "filter")
 
-          (when (seq filters)
-            (ui/point "bg-red-600.absolute" 4 {:style {:right -2 :top -2}}))])
+         (when (seq filters)
+           (ui/point "bg-red-600.absolute" 4 {:style {:right -2 :top -2}}))])
 
-       (for [k [:All :Disabled :Unset :Custom]
-             :let [all? (= k :All)
-                   checked? (or (contains? filters k) (and all? (nil? (seq filters))))]]
+      (for [k [:All :Disabled :Unset :Custom]
+            :let [all? (= k :All)
+                  checked? (or (contains? filters k) (and all? (nil? (seq filters))))]]
 
-         {:title   (if all? (t :keymap/all) (t (keyword :keymap (string/lower-case (name k)))))
-          :icon    (ui/icon (if checked? "checkbox" "square"))
-          :options {:on-click #(set-filters! (if all? #{} (let [f (if checked? disj conj)] (f filters k))))}})
+        {:title   (if all? (t :keymap/all) (t (keyword :keymap (string/lower-case (name k)))))
+         :icon    (ui/icon (if checked? "checkbox" "square"))
+         :options {:on-click #(set-filters! (if all? #{} (let [f (if checked? disj conj)] (f filters k))))}})
 
-       nil)]))
+      nil)]))
 
 (rum/defc shortcut-desc-label
   [id binding-map]
@@ -170,10 +170,10 @@
                 {:saved-cb (fn [] (-> (p/delay 500) (p/then refresh-shortcuts-list!)))
                  :modal-id modal-id}]]
       (shui/dialog-open!
-        (fn [] (apply customize-shortcut-dialog-inner args))
-        {:id      modal-id
-         :class "w-auto md:max-w-2xl"
-         :payload args}))))
+       (fn [] (apply customize-shortcut-dialog-inner args))
+       {:id      modal-id
+        :class "w-auto md:max-w-2xl"
+        :payload args}))))
 
 (rum/defc shortcut-conflicts-display
   [_k conflicts-map]
@@ -237,8 +237,8 @@
     ;; TODO: back interaction for the shui dialog
     (rum/use-effect!
      (fn []
-       (let [mid (state/sub :modal/id)
-             mid' (some-> (state/sub :modal/subsets) (last) (:modal/id))
+       (let [mid (shui-dialog/get-first-modal-id)
+             mid' (shui-dialog/get-last-modal-id)
              el (rum/deref *ref-el)]
          (when (or (and (not mid') (= mid modal-id))
                    (= mid' modal-id))
@@ -269,7 +269,7 @@
                           (set-keystroke! #(util/trim-safe (str % (shortcut/keyname e))))))
 
           ;; active
-          (js/setTimeout #(.focus el) 128)
+         (js/setTimeout #(.focus el) 128)
 
           ;; teardown
          #(do (some-> teardown-global! (apply nil))
@@ -374,10 +374,10 @@
           (->> categories-list-map
                (map (fn [[c binding-map]]
                       [c (search/fuzzy-search
-                           binding-map q
-                           :extract-fn
-                           #(let [[id m] %]
-                              (str (name id) " " (dh/get-shortcut-desc (assoc m :id id)))))]))))
+                          binding-map q
+                          :extract-fn
+                          #(let [[id m] %]
+                             (str (name id) " " (dh/get-shortcut-desc (assoc m :id id)))))]))))
 
         result-list-map (or matched-list-map categories-list-map)
         toggle-categories! #(if (= folded-categories all-categories)
@@ -385,9 +385,9 @@
                               (set-folded-categories! all-categories))]
 
     (rum/use-effect!
-      (fn []
-        (js/setTimeout #(set-ready! true) 100))
-      [])
+     (fn []
+       (js/setTimeout #(set-ready! true) 100))
+     [])
 
     [:div.cp__shortcut-page-x
      [:header.relative
@@ -470,12 +470,12 @@
                                 (if disabled?
                                   (t :keymap/disabled)
                                   (bean/->js
-                                    (map #(if (false? %)
-                                            (t :keymap/disabled)
-                                            (shortcut-utils/decorate-binding %)) user-binding)))))]
+                                   (map #(if (false? %)
+                                           (t :keymap/disabled)
+                                           (shortcut-utils/decorate-binding %)) user-binding)))))]
 
                         (not unset?)
                         [:code.flex.items-center.bg-transparent
                          (shui/shortcut
-                           (string/join " | " (map #(dh/binding-for-display id %) binding))
-                           {:size :md :interactive? true})])]]))))])])]]))
+                          (string/join " | " (map #(dh/binding-for-display id %) binding))
+                          {:size :md :interactive? true})])]]))))])])]]))

+ 178 - 164
src/main/frontend/components/views.cljs

@@ -13,18 +13,24 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.handler.property :as property-handler]
-            [frontend.common.search-fuzzy :as fuzzy]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [goog.dom :as gdom]
             [dommy.core :as dom]
-            [goog.functions :refer [debounce]]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.shui.ui :as shui]
             [rum.core :as rum]
-            [frontend.mixins :as mixins]))
+            [frontend.mixins :as mixins]
+            [logseq.shui.table.core :as table-core]
+            [logseq.db :as ldb]))
+
+(defn- get-latest-entity
+  [e]
+  (assoc (db/entity (:db/id e))
+         :id (:id e)
+         :block.temp/refs-count (:block.temp/refs-count e)))
 
 (rum/defc header-checkbox < rum/static
   [{:keys [selected-all? selected-some? toggle-selected-all!]}]
@@ -58,7 +64,7 @@
        :class (str "flex transition-opacity "
                    (if (or show? checked?) "opacity-100" "opacity-0"))})]))
 
-(defn- header-cp
+(defn header-cp
   [{:keys [column-toggle-sorting! state]} column]
   (let [sorting (:sorting state)
         [asc?] (some (fn [item] (when (= (:id item) (:id column))
@@ -112,10 +118,10 @@
      (container config row)]))
 
 (defn build-columns
-  [config properties & {:keys [with-object-name?]
-                        :or {with-object-name? true}}]
-  (let [asset-class? (= :logseq.class/Asset (:db/ident (:class config)))
-        properties (if (some #(= (:db/ident %) :block/tags) properties)
+  [config properties & {:keys [with-object-name? add-tags-column?]
+                        :or {with-object-name? true
+                             add-tags-column? true}}]
+  (let [properties (if (or (some #(= (:db/ident %) :block/tags) properties) (not add-tags-column?))
                      properties
                      (conj properties (db/entity :block/tags)))]
     (->> (concat
@@ -133,46 +139,42 @@
               :header header-cp
               :cell (fn [_table row _column]
                       (block-container (assoc config
-                                              :raw-title? true
+                                              :raw-title? (ldb/asset? row)
                                               :table? true) row))
-              :disable-hide? true})
-           (when asset-class?
-             {:id :file
-              :name "File"
-              :type :string
-              :header header-cp
-              :cell (fn [_table row _column]
-                      (when-let [asset-cp (state/get-component :block/asset-cp)]
-                        [:div.block-content (asset-cp (assoc config :disable-resize? true) row)]))
               :disable-hide? true})]
           (keep
            (fn [column]
              (let [ident (or (:db/ident column) (:id column))]
+               ;; Hide properties that shouldn't ever be editable or that do not display well in a table
                (when-not (or (contains? #{:logseq.property/built-in? :logseq.property.asset/checksum :logseq.property.class/properties
-                                          :block/created-at :block/updated-at :block/order :block/collapsed?}
+                                          :block/created-at :block/updated-at :block/order :block/collapsed?
+                                          :logseq.property/created-from-property}
                                         ident)
                              (and with-object-name? (= :block/title ident))
                              (contains? #{:map :entity} (get-in column [:block/schema :type])))
                  (let [property (if (de/entity? column)
                                   column
                                   (or (db/entity ident) column))
-                       get-value (or (:get-value property)
-                                     (when (de/entity? property)
-                                       (fn [row] (get-property-value-for-search row property))))
+                       get-value (if-let [f (:get-value property)]
+                                   (fn [row]
+                                     (f (get-latest-entity row)))
+                                   (when (de/entity? property)
+                                     (fn [row] (get-property-value-for-search (get-latest-entity row) property))))
                        closed-values (seq (:property/closed-values property))
                        closed-value->sort-number (when closed-values
                                                    (->> (zipmap (map :db/id closed-values) (range 0 (count closed-values)))
                                                         (into {})))
                        get-value-for-sort (fn [row]
-                                            (cond
-                                              (= (:db/ident property) :logseq.task/deadline)
-                                              (:block/journal-day (get row :logseq.task/deadline))
-                                              closed-values
-                                              (closed-value->sort-number (:db/id (get row (:db/ident property))))
-                                              :else
-                                              (if (fn? get-value)
-                                                (get-value row)
-                                                (get row ident))))]
+                                            (let [row (get-latest-entity row)]
+                                              (cond
+                                                (= (:db/ident property) :logseq.task/deadline)
+                                                (:block/journal-day (get row :logseq.task/deadline))
+                                                closed-values
+                                                (closed-value->sort-number (:db/id (get row (:db/ident property))))
+                                                :else
+                                                (if (fn? get-value)
+                                                  (get-value row)
+                                                  (get row ident)))))]
                    {:id ident
                     :name (or (:name column)
                               (:block/title property))
@@ -485,7 +487,7 @@
   [rows property]
   (let [property-ident (:db/ident property)
         block-type? (= property-ident :block/type)
-        values (->> (mapcat (fn [e] (let [e' (if (de/entity? e) e (db/entity (:db/id e)))
+        values (->> (mapcat (fn [e] (let [e' (db/entity (:db/id e))
                                           v (get e' property-ident)]
                                       (if (set? v) v #{v}))) rows)
                     (remove nil?)
@@ -817,7 +819,7 @@
 
       (:text-contains :text-not-contains :number-gt :number-lt :number-gte :number-lte)
       (shui/input
-       {:auto-focus true
+       {:auto-focus false
         :value (or value "")
         :onChange (fn [e]
                     (let [value (util/evalue e)
@@ -868,98 +870,101 @@
               (ui/icon "x"))]))
         filters)])))
 
-(defn- fuzzy-matched?
-  [input s]
-  (pos? (fuzzy/score (string/lower-case (str input))
-                     (string/lower-case (str s)))))
-
 (defn- row-matched?
   [row input filters]
-  (and
-   ;; full-text-search match
-   (if (string/blank? input)
-     true
-     (when row
-       (fuzzy-matched? input (:block/title row))))
-   ;; filters check
-   (every?
-    (fn [[property-ident operator match]]
-      (let [value (get row property-ident)
-            value' (cond
-                     (set? value) value
-                     (nil? value) #{}
-                     :else #{value})
-            entity? (de/entity? (first value'))
-            result
-            (case operator
-              :is
-              (if (boolean? match)
-                (= (boolean (get-property-value-content (get row property-ident))) match)
-                (cond
-                  (empty? match)
-                  true
-                  (and (empty? match) (empty? value'))
-                  true
-                  :else
-                  (if entity?
-                    (boolean (seq (set/intersection (set (map :block/uuid value')) match)))
-                    (boolean (seq (set/intersection (set value') match))))))
-
-              :is-not
-              (if (boolean? match)
-                (not= (boolean (get-property-value-content (get row property-ident))) match)
-                (cond
-                  (and (empty? match) (seq value'))
-                  true
-                  (and (seq match) (empty? value'))
-                  true
-                  :else
-                  (if entity?
-                    (boolean (empty? (set/intersection (set (map :block/uuid value')) match)))
-                    (boolean (empty? (set/intersection (set value') match))))))
-
-              :text-contains
-              (some #(fuzzy-matched? match (get-property-value-content %)) value')
-
-              :text-not-contains
-              (not-any? #(string/includes? (str (get-property-value-content %)) match) value')
-
-              :number-gt
-              (if match (some #(> (get-property-value-content %) match) value') true)
-              :number-gte
-              (if match (some #(>= (get-property-value-content %) match) value') true)
-              :number-lt
-              (if match (some #(< (get-property-value-content %) match) value') true)
-              :number-lte
-              (if match (some #(<= (get-property-value-content %) match) value') true)
-
-              :between
-              (if (seq match)
-                (some (fn [value-entity]
-                        (let [[start end] match
-                              value (get-property-value-content value-entity)
-                              conditions [(if start (<= start value) true)
-                                          (if end (<= value end) true)]]
-                          (if (seq match) (every? true? conditions) true))) value')
-                true)
-
-              :date-before
-              (if match (some #(< (:block/journal-day %) (:block/journal-day match)) value') true)
-
-              :date-after
-              (if match (some #(> (:block/journal-day %) (:block/journal-day match)) value') true)
-
-              :before
-              (let [search-value (get-timestamp match)]
-                (if search-value (<= (get row property-ident) search-value) true))
-
-              :after
-              (let [search-value (get-timestamp match)]
-                (if search-value (>= (get row property-ident) search-value) true))
-
-              true)]
-        result))
-    filters)))
+  (let [row (get-latest-entity row)]
+    (and
+     ;; full-text-search match
+     (if (string/blank? input)
+       true
+       (when row
+       ;; fuzzy search is too slow
+         (string/includes? (string/lower-case (:block/title row)) (string/lower-case input))))
+     ;; filters check
+     (every?
+      (fn [[property-ident operator match]]
+        (if (nil? match)
+          true
+          (let [value (get row property-ident)
+                value' (cond
+                         (set? value) value
+                         (nil? value) #{}
+                         :else #{value})
+                entity? (de/entity? (first value'))
+                result
+                (case operator
+                  :is
+                  (if (boolean? match)
+                    (= (boolean (get-property-value-content (get row property-ident))) match)
+                    (cond
+                      (empty? match)
+                      true
+                      (and (empty? match) (empty? value'))
+                      true
+                      :else
+                      (if entity?
+                        (boolean (seq (set/intersection (set (map :block/uuid value')) match)))
+                        (boolean (seq (set/intersection (set value') match))))))
+
+                  :is-not
+                  (if (boolean? match)
+                    (not= (boolean (get-property-value-content (get row property-ident))) match)
+                    (cond
+                      (and (empty? match) (seq value'))
+                      true
+                      (and (seq match) (empty? value'))
+                      true
+                      :else
+                      (if entity?
+                        (boolean (empty? (set/intersection (set (map :block/uuid value')) match)))
+                        (boolean (empty? (set/intersection (set value') match))))))
+
+                  :text-contains
+                  (some (fn [v]
+                          (if-let [property-value (get-property-value-content v)]
+                            (string/includes? (string/lower-case property-value) (string/lower-case match))
+                            false))
+                        value')
+
+                  :text-not-contains
+                  (not-any? #(string/includes? (str (get-property-value-content %)) match) value')
+
+                  :number-gt
+                  (if match (some #(> (get-property-value-content %) match) value') true)
+                  :number-gte
+                  (if match (some #(>= (get-property-value-content %) match) value') true)
+                  :number-lt
+                  (if match (some #(< (get-property-value-content %) match) value') true)
+                  :number-lte
+                  (if match (some #(<= (get-property-value-content %) match) value') true)
+
+                  :between
+                  (if (seq match)
+                    (some (fn [value-entity]
+                            (let [[start end] match
+                                  value (get-property-value-content value-entity)
+                                  conditions [(if start (<= start value) true)
+                                              (if end (<= value end) true)]]
+                              (if (seq match) (every? true? conditions) true))) value')
+                    true)
+
+                  :date-before
+                  (if match (some #(< (:block/journal-day %) (:block/journal-day match)) value') true)
+
+                  :date-after
+                  (if match (some #(> (:block/journal-day %) (:block/journal-day match)) value') true)
+
+                  :before
+                  (let [search-value (get-timestamp match)]
+                    (if search-value (<= (get row property-ident) search-value) true))
+
+                  :after
+                  (let [search-value (get-timestamp match)]
+                    (if search-value (>= (get row property-ident) search-value) true))
+
+                  true)]
+            result)))
+      filters))))
 
 (rum/defc new-record-button < rum/static
   [table]
@@ -1028,13 +1033,12 @@
        (property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/sized-columns sized-columns))}))
 
 (rum/defc table-view < rum/static
-  [table option row-selection add-new-object! ready?]
+  [table option row-selection add-new-object!]
   (let [selected-rows (shui/table-get-selection-rows row-selection (:rows table))]
     (shui/table
      (let [columns' (:columns table)
            rows (:rows table)]
        [:div.ls-table-rows.content.overflow-x-auto.force-visible-scrollbar
-        {:class (when (not ready?) "invisible")}
         [:div.relative
          (table-header table columns' option selected-rows)
 
@@ -1069,7 +1073,7 @@
                        :group-by-page? group-by-page?
                        :ref? true)))))
 
-(rum/defcs card-view < rum/static mixins/container-id
+(rum/defcs gallery-view < rum/static mixins/container-id
   [state config view-entity result]
   (let [config' (assoc config :container-id (:container-id state))]
     [:div.ls-cards
@@ -1079,6 +1083,29 @@
         [:div.-ml-4
          (block-container (assoc config' :id (str (:block/uuid block))) block)]])]))
 
+(defn- run-effects!
+  [{:keys [data columns state data-fns]} input input-filters set-input-filters!]
+  (let [{:keys [filters sorting]} state
+        {:keys [set-row-filter! set-data!]} data-fns]
+    (rum/use-effect!
+     (fn []
+       (let [new-input-filters [input filters]]
+         (when-not (= input-filters new-input-filters)
+           (set-input-filters! [input filters])
+           (set-row-filter!
+            (fn []
+              (fn [row]
+                (row-matched? row input filters)))))))
+     [input filters])
+
+    (rum/use-effect!
+     (fn []
+       ;; Entities might be outdated
+       (let [new-data (map get-latest-entity data)
+             data' (table-core/table-sort-rows new-data sorting columns)]
+         (set-data! data')))
+     [sorting])))
+
 (rum/defc view-inner < rum/static
   [view-entity {:keys [data set-data! columns add-new-object! views-title title-key render-empty-title?] :as option
                 :or {render-empty-title? false}}]
@@ -1103,38 +1130,33 @@
                         (fn [row]
                           (row-matched? row input filters)))
         [row-filter set-row-filter!] (rum/use-state row-filter-fn)
-        debounced-set-row-filter! (debounce set-row-filter! 200)
+        [input-filters set-input-filters!] (rum/use-state [input filters])
         [row-selection set-row-selection!] (rum/use-state {})
         columns (sort-columns columns ordered-columns)
-        table (shui/table-option {:data data
-                                  :columns columns
-                                  :state {:sorting sorting
-                                          :filters filters
-                                          :row-filter row-filter
-                                          :row-selection row-selection
-                                          :visible-columns visible-columns
-                                          :sized-columns sized-columns
-                                          :ordered-columns ordered-columns}
-                                  :data-fns {:set-data! set-data!
-                                             :set-filters! set-filters!
-                                             :set-sorting! set-sorting!
-                                             :set-visible-columns! set-visible-columns!
-                                             :set-ordered-columns! set-ordered-columns!
-                                             :set-sized-columns! set-sized-columns!
-                                             :set-row-selection! set-row-selection!
-                                             :add-new-object! add-new-object!}})
-
-        [ready?, set-ready!] (rum/use-state false)
+        table-map {:data data
+                   :columns columns
+                   :state {:sorting sorting
+                           :filters filters
+                           :row-filter row-filter
+                           :row-selection row-selection
+                           :visible-columns visible-columns
+                           :sized-columns sized-columns
+                           :ordered-columns ordered-columns}
+                   :data-fns {:set-data! set-data!
+                              :set-row-filter! set-row-filter!
+                              :set-filters! set-filters!
+                              :set-sorting! set-sorting!
+                              :set-visible-columns! set-visible-columns!
+                              :set-ordered-columns! set-ordered-columns!
+                              :set-sized-columns! set-sized-columns!
+                              :set-row-selection! set-row-selection!
+                              :add-new-object! add-new-object!}}
+        table (shui/table-option table-map)
         *view-ref (rum/use-ref nil)
         display-type (or (:db/ident (get view-entity :logseq.property.view/type))
                          :logseq.property.view/type.table)]
 
-    (rum/use-effect!
-     (fn [] (debounced-set-row-filter!
-             (fn []
-               (fn [row]
-                 (row-matched? row input filters)))))
-     [input filters])
+    (run-effects! table-map input input-filters set-input-filters!)
 
     [:div.flex.flex-col.gap-2.grid
      {:ref *view-ref}
@@ -1155,7 +1177,7 @@
 
        [:div.text-muted-foreground.text-sm
         (pv/property-value view-entity (db/entity :logseq.property.view/type)
-                           (get view-entity :logseq.property.view/type) {})]
+                           (db/entity display-type) {})]
 
        (more-actions columns table)
 
@@ -1163,22 +1185,14 @@
 
      (filters-row table)
 
-     (rum/use-effect! #(js/setTimeout (fn [] (set-ready! true)) 16) [])
-     (rum/use-effect!
-      (fn []
-        (when-let [^js cnt (and ready? (some-> (rum/deref *view-ref) (.closest ".is-node-page")))]
-          (.setAttribute cnt "data-ready" true)
-          #(.removeAttribute cnt "data-ready")))
-      [ready?])
-
      (case display-type
        :logseq.property.view/type.list
        (list-view (:config option) view-entity (:rows table))
 
-       :logseq.property.view/type.card
-       (card-view (:config option) view-entity (:rows table))
+       :logseq.property.view/type.gallery
+       (gallery-view (:config option) view-entity (:rows table))
 
-       (table-view table option row-selection add-new-object! ready?))]))
+       (table-view table option row-selection add-new-object!))]))
 
 (rum/defc view
   "Provides a view for data like query results and tagged objects, multiple

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

@@ -285,13 +285,6 @@
         [?b :logseq.property/view-for ?class-id]]
       class-id))
 
-(defn <get-tags
-  [graph]
-  (<q graph {:transact-db? false}
-      '[:find [(pull ?tag [:db/id :block/title]) ...]
-        :where
-        [?tag :block/type "class"]]))
-
 (defn <get-asset-with-checksum
   [graph checksum]
   (p/let [result (<q graph {:transact-db? true}

+ 15 - 4
src/main/frontend/extensions/fsrs.cljs

@@ -274,10 +274,21 @@
                                 (:block/title card-entity)))))))
 
         [:span.text-sm.opacity-50 (str (min (inc @*card-index) (count @*block-ids)) "/" (count @*block-ids))]]
-       (if-let [block-id (nth block-ids @*card-index nil)]
-         [:div.flex.flex-col
-          (card-view repo block-id *card-index *phase)]
-         [:p (t :flashcards/modal-finished)])])))
+       (let [block-id (nth block-ids @*card-index nil)]
+         (cond
+           block-id
+           [:div.flex.flex-col
+            (card-view repo block-id *card-index *phase)]
+
+           (empty? block-ids)
+           [:div.ls-card.content.ml-2
+            [:h2.font-medium (t :flashcards/modal-welcome-title)]
+
+            [:div
+             [:p (t :flashcards/modal-welcome-desc-1)]]]
+
+           :else
+           [:p (t :flashcards/modal-finished)]))])))
 
 (defonce ^:private *last-update-due-cards-count-canceler (atom nil))
 (def ^:private new-task--update-due-cards-count

+ 3 - 2
src/main/frontend/extensions/pdf/assets.cljs

@@ -156,7 +156,8 @@
           ref-block)
         (let [text       (:text content)
               properties (cond->
-                          {:logseq.property/ls-type  :annotation
+                          {:block/tags :logseq.class/Pdf-annotation
+                           :logseq.property/ls-type  :annotation
                            :logseq.property/hl-color (:color properties)
                            :logseq.property/asset (:db/id pdf-block)
                            :logseq.property.pdf/hl-page  page
@@ -170,7 +171,7 @@
                           :sibling? false
                           :custom-uuid id
                           :properties properties}
-                         insert-opts))))))))
+                         (assoc insert-opts :edit-block? false)))))))))
 
 (defn ensure-ref-block!
   [pdf-current hl insert-opts]

+ 3 - 1
src/main/frontend/extensions/tldraw.cljs

@@ -28,6 +28,7 @@
             [cljs-bean.core :as bean]
             [frontend.db.async :as db-async]
             [logseq.common.util :as common-util]
+            [logseq.shui.ui :as shui]
             [frontend.util.text :as text-util]))
 
 (def tldraw (r/adapt-class (gobj/get TldrawLogseq "App")))
@@ -126,7 +127,8 @@
                           (str (:block/uuid (db/get-page block-id-str)))))
    :exportToImage (fn [page-uuid-str options]
                     (assert (common-util/uuid-string? page-uuid-str))
-                    (state/set-modal! #(export/export-blocks (uuid page-uuid-str) (merge (js->clj options :keywordize-keys true) {:whiteboard? true}))))
+                    (shui/dialog-open!
+                     #(export/export-blocks (uuid page-uuid-str) (merge (js->clj options :keywordize-keys true) {:whiteboard? true}))))
    :isWhiteboardPage (fn [page-name]
                        (when-let [entity (db/get-page page-name)]
                          (model/whiteboard-page? entity)))

+ 0 - 1
src/main/frontend/handler/command_palette.cljs

@@ -56,7 +56,6 @@
 
 (defn invoke-command [{:keys [id action] :as cmd}]
   (add-history cmd)
-  (state/close-modal!)
   (plugin-handler/hook-lifecycle-fn! id action))
 
 (defn top-commands [limit]

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

@@ -95,4 +95,4 @@
     (state/pub-event! [:graph/switch repo])))
 
 (defn ^:export replace-graph-with-db-file []
-  (state/set-state! :ui/open-select :db-graph-replace))
+  (state/pub-event! [:dialog-select/db-graph-replace]))

+ 6 - 39
src/main/frontend/handler/db_based/editor.cljs

@@ -8,7 +8,6 @@
             [frontend.format.mldoc :as mldoc]
             [frontend.handler.common.config-edn :as config-edn-common-handler]
             [frontend.handler.property :as property-handler]
-            [frontend.handler.property.util :as pu]
             [frontend.handler.repo-config :as repo-config-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.modules.outliner.op :as outliner-op]
@@ -91,46 +90,14 @@
              (= path "logseq/custom.css")
              (ui-handler/add-style-if-exists!))))))
 
-(defn- set-heading-aux!
-  [block-id heading]
-  (let [block (db/pull [:block/uuid block-id])
-        old-heading (pu/lookup (:block/properties block) :logseq.property/heading)]
-    (cond
-      ;; nothing changed for first two cases
-      (or (and (nil? old-heading) (nil? heading))
-          (and (true? old-heading) (true? heading))
-          (= old-heading heading))
-      nil
-
-      (or (and (nil? old-heading) (true? heading))
-          (and (true? old-heading) (nil? heading)))
-      nil
-
-      (and (or (nil? heading) (true? heading))
-           (number? old-heading))
-      (let [content (commands/clear-markdown-heading (:block/title block))]
-        {:block/title content
-         :block/uuid (:block/uuid block)})
-
-      (and (or (nil? old-heading) (true? old-heading))
-           (number? heading))
-      (let [content (commands/set-markdown-heading (:block/title block) heading)]
-        {:block/title content
-         :block/uuid (:block/uuid block)})
-
-        ;; heading-num1 -> heading-num2
-      :else
-      (let [content (-> block
-                        :block/title
-                        commands/clear-markdown-heading
-                        (commands/set-markdown-heading heading))]
-        {:block/uuid (:block/uuid block)
-         :block/title content}))))
-
 (defn batch-set-heading!
   [repo block-ids heading]
   (ui-outliner-tx/transact!
    {:outliner-op :save-block}
-   (doseq [block (keep #(set-heading-aux! % heading) block-ids)]
-     (outliner-op/save-block! block))
+   (doseq [id block-ids]
+     (let [e (db/entity [:block/uuid id])
+           title (commands/clear-markdown-heading (:block/title e))
+           block {:block/uuid (:block/uuid e)
+                  :block/title title}]
+       (outliner-op/save-block! block)))
    (property-handler/batch-set-block-property! repo block-ids :logseq.property/heading heading)))

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

@@ -1552,8 +1552,7 @@
                   file-rpath  (str asset-dir-rpath "/" file-path)
                   dir repo-dir
                   asset (db/entity :logseq.class/Asset)
-                  properties {:block/type "asset"
-                              :logseq.property.asset/type ext
+                  properties {:logseq.property.asset/type ext
                               :logseq.property.asset/size (.-size file)
                               :logseq.property.asset/checksum checksum
                               :block/tags (:db/id asset)}
@@ -1571,13 +1570,14 @@
                   result (api-insert-new-block! file-name-without-ext insert-opts')
                   new-entity (db/entity [:block/uuid (:block/uuid result)])]
             (if (util/electron?)
-              (let [from (not-empty (.-path file))]
-                (js/console.debug "Debug: Copy Asset #" dir file-rpath from)
+              (if-let [from (not-empty (.-path file))]
                 (-> (js/window.apis.copyFileToAssets dir file-rpath from)
-                    (p/then
-                     (fn [_dest]
-                       new-entity))
-                    (p/catch #(js/console.error "Debug: Copy Asset Error#" %))))
+                    (p/then (fn [_dest] new-entity))
+                    (p/catch #(js/console.error "Debug: Copy Asset Error#" %)))
+                (-> (p/let [buffer (.arrayBuffer file)]
+                      (fs/write-file! repo dir file-rpath buffer {:skip-compare? false}))
+                    (p/then (fn [_] new-entity))
+                    (p/catch #(js/console.error "Debug: Writing Asset #" %))))
               (->
                (p/do! (js/console.debug "Debug: Writing Asset #" dir file-rpath)
                       (cond
@@ -2946,7 +2946,8 @@
           pos (cursor/pos input)
           hashtag? (or (surround-by? input "#" " ")
                        (surround-by? input "#" :end)
-                       (= key "#"))]
+                       (= key "#"))
+          db-based? (config/db-based-graph? (state/get-current-repo))]
       (when (or (not @(:editor/start-pos @state/state))
                 (and key (string/starts-with? key "Arrow")))
         (state/set-state! :editor/start-pos pos))
@@ -3033,7 +3034,7 @@
 
         ; `;;` to add or change property for db graphs
         (let [sym ";"]
-          (and (config/db-based-graph? (state/get-current-repo)) (double-chars-typed? value pos key sym)))
+          (and db-based? (double-chars-typed? value pos key sym)))
         (state/pub-event! [:editor/new-property])
 
         (let [sym "$"]
@@ -3113,7 +3114,8 @@
   [_state input]
   (fn [e key-code]
     (when-not (util/goog-event-is-composing? e)
-      (let [current-pos (cursor/pos input)
+      (let [db-based? (config/db-based-graph?)
+            current-pos (cursor/pos input)
             value (gobj/get input "value")
             c (util/nth-safe value (dec current-pos))
             [key-code k code is-processed?]
@@ -3141,6 +3143,20 @@
                 ;; #3440
                (util/goog-event-is-composing? e true)])]
         (cond
+          (and db-based? (= value "``````")) ; turn this block into a code block
+          (do
+            (state/set-edit-content! (.-id input) "")
+            (state/pub-event! [:editor/upsert-type-block {:block (assoc (state/get-edit-block) :block/title "")
+                                                          :type :code
+                                                          :update-current-block? true}]))
+
+          (and db-based? (= value ">")) ; turn this block into a quote block
+          (do
+            (state/set-edit-content! (.-id input) "")
+            (state/pub-event! [:editor/upsert-type-block {:block (assoc (state/get-edit-block) :block/title "")
+                                                          :type :quote
+                                                          :update-current-block? true}]))
+
           ;; When you type something after /
           (and (= :commands (state/get-editor-action)) (not= k commands/command-trigger))
           (if (= commands/command-trigger (second (re-find #"(\S+)\s+$" value)))

+ 58 - 44
src/main/frontend/handler/events.cljs

@@ -10,39 +10,47 @@
             [clojure.core.async.interop :refer [p->c]]
             [clojure.string :as string]
             [frontend.commands :as commands]
-            [frontend.components.cmdk.core :as cmdk]
             [frontend.components.block :as block]
-            [frontend.components.settings :as settings]
+            [frontend.components.cmdk.core :as cmdk]
             [frontend.components.diff :as diff]
             [frontend.components.encryption :as encryption]
             [frontend.components.file-sync :as file-sync]
             [frontend.components.git :as git-component]
             [frontend.components.plugins :as plugin]
+            [frontend.components.property.dialog :as property-dialog]
+            [frontend.components.repo :as repo]
+            [frontend.components.select :as select]
+            [frontend.components.settings :as settings]
             [frontend.components.shell :as shell]
-            [frontend.components.whiteboard :as whiteboard]
             [frontend.components.user.login :as login]
-            [frontend.components.repo :as repo]
-            [frontend.components.property.dialog :as property-dialog]
+            [frontend.components.whiteboard :as whiteboard]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
-            [logseq.shui.ui :as shui]
+            [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db.conn :as conn]
             [frontend.db.model :as db-model]
             [frontend.db.persist :as db-persist]
             [frontend.db.transact :as db-transact]
+            [frontend.extensions.fsrs :as fsrs]
             [frontend.extensions.srs :as srs]
             [frontend.fs :as fs]
             [frontend.fs.capacitor-fs :as capacitor-fs]
             [frontend.fs.nfs :as nfs]
             [frontend.fs.sync :as sync]
             [frontend.fs.watcher-handler :as fs-watcher]
+            [frontend.handler.code :as code-handler]
+            [frontend.handler.common.page :as page-common-handler]
+            [frontend.handler.db-based.property :as db-property-handler]
+            [frontend.handler.db-based.rtc :as rtc-handler]
             [frontend.handler.editor :as editor-handler]
+            [frontend.handler.export :as export]
             [frontend.handler.file :as file-handler]
+            [frontend.handler.file-based.nfs :as nfs-handler]
             [frontend.handler.file-sync :as file-sync-handler]
+            [frontend.handler.graph :as graph-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
-            [frontend.handler.common.page :as page-common-handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.repo-config :as repo-config-handler]
@@ -51,39 +59,31 @@
             [frontend.handler.shell :as shell-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
-            [frontend.handler.file-based.nfs :as nfs-handler]
-            [frontend.handler.code :as code-handler]
-            [frontend.handler.db-based.rtc :as rtc-handler]
-            [frontend.handler.graph :as graph-handler]
-            [frontend.handler.db-based.property :as db-property-handler]
             [frontend.mobile.core :as mobile]
             [frontend.mobile.graph-picker :as graph-picker]
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.instrumentation.posthog :as posthog]
             [frontend.modules.instrumentation.sentry :as sentry-event]
+            [frontend.modules.outliner.pipeline :as pipeline]
+            [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.shortcut.core :as st]
+            [frontend.persist-db :as persist-db]
+            [frontend.persist-db.browser :as db-browser]
             [frontend.quick-capture :as quick-capture]
+            [frontend.rum :as r]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util.persist-var :as persist-var]
             [goog.dom :as gdom]
+            [lambdaisland.glogi :as log]
             [logseq.common.config :as common-config]
             [logseq.common.util :as common-util]
-            [promesa.core :as p]
-            [lambdaisland.glogi :as log]
-            [rum.core :as rum]
-            [frontend.rum :as r]
-            [frontend.persist-db.browser :as db-browser]
-            [frontend.modules.outliner.pipeline :as pipeline]
-            [frontend.date :as date]
             [logseq.db :as ldb]
-            [frontend.persist-db :as persist-db]
-            [frontend.handler.export :as export]
-            [frontend.extensions.fsrs :as fsrs]
-            [frontend.storage :as storage]
-            [frontend.modules.outliner.ui :as ui-outliner-tx]))
+            [logseq.shui.ui :as shui]
+            [promesa.core :as p]
+            [rum.core :as rum]))
 
 ;; TODO: should we move all events here?
 
@@ -309,15 +309,16 @@
     (plugin/open-select-theme!)
     (route-handler/go-to-search! :themes)))
 
-(defmethod handle :modal/toggle-appearance-modal [_]
-  (let [label "customize-appearance"]
-    (if (shui/dialog-get label)
-      (shui/dialog-close! label)
-      (shui/dialog-open!
-       #(settings/modal-appearance-inner)
-       {:id      label
-        :overlay-props {:label label}
-        :label   label}))))
+(defmethod handle :ui/toggle-appearance [_]
+  (let [popup-id "appearance_settings"]
+    (if (gdom/getElement popup-id)
+      (shui/popup-hide! popup-id)
+      (shui/popup-show!
+       (gdom/getElement "dots-menu")
+       (fn []
+         (settings/appearance))
+       {:id popup-id
+        :align :end}))))
 
 (defmethod handle :modal/set-git-username-and-email [[_ _content]]
   (shui/dialog-open! git-component/set-git-username-and-email))
@@ -394,11 +395,12 @@
     (shui/dialog-open! shell/shell)))
 
 (defmethod handle :go/search [_]
-  (state/set-modal! cmdk/cmdk-modal
-                    {:fullscreen? true
-                     :close-btn?  false
-                     :panel?      false
-                     :label "ls-modal-search"}))
+  (shui/dialog-open!
+   cmdk/cmdk-modal
+   {:id :ls-dialog-cmdk
+    :align :top
+    :content-props {:class "ls-dialog-cmdk"}
+    :close-btn? false}))
 
 (defmethod handle :go/plugins [_]
   (plugin/open-plugins-modal!))
@@ -666,7 +668,7 @@
            template
            {:target page}))))))
 
-(defmethod handle :editor/set-org-mode-heading [[_ block heading]]
+(defmethod handle :editor/set-heading [[_ block heading]]
   (when-let [id (:block/uuid block)]
     (editor-handler/set-heading! id heading)))
 
@@ -784,6 +786,15 @@
     :title [:h2 "Create a new graph"]
     :style {:max-width "500px"}}))
 
+(defmethod handle :dialog-select/graph-open []
+  (select/dialog-select! :graph-open))
+
+(defmethod handle :dialog-select/graph-remove []
+  (select/dialog-select! :graph-remove))
+
+(defmethod handle :dialog-select/db-graph-replace []
+  (select/dialog-select! :db-graph-replace))
+
 (defmethod handle :graph/save-db-to-disk [[_ _opts]]
   (persist-db/export-current-graph! {:succ-notification? true}))
 
@@ -964,14 +975,17 @@
                               {:id :property-dialog
                                :align "start"})))))))
 
-(defmethod handle :editor/upsert-type-block [[_ {:keys [block type lang]}]]
+(defmethod handle :editor/upsert-type-block [[_ {:keys [block type lang update-current-block?]}]]
   (p/do!
-   (editor-handler/save-current-block!)
-   (p/delay 16)
+   (when-not update-current-block?
+     (editor-handler/save-current-block!))
+   (when-not update-current-block?
+     (p/delay 16))
    (let [block (db/entity (:db/id block))
          block-type (:logseq.property.node/display-type block)
          block-title (:block/title block)
-         latest-code-lang (or lang (storage/get :latest-code-lang))
+         latest-code-lang (or lang
+                              (:kv/value (db/entity :logseq.kv/latest-code-lang)))
          turn-type! #(if (and (= (keyword type) :code) latest-code-lang)
                        (db-property-handler/set-block-properties!
                         (:block/uuid %)
@@ -980,7 +994,7 @@
                        (db-property-handler/set-block-property!
                         (:block/uuid %) :logseq.property.node/display-type (keyword type)))]
      (p/let [block (if (or (not (nil? block-type))
-                           (not (string/blank? block-title)))
+                           (and (not update-current-block?) (not (string/blank? block-title))))
                      (p/let [result (ui-outliner-tx/transact!
                                      {:outliner-op :insert-blocks}
                                      ;; insert a new block

+ 2 - 2
src/main/frontend/handler/file_based/editor.cljs

@@ -182,7 +182,7 @@
              (number? heading))
         (let [block' (set-block-property-aux! block :heading nil)
               properties (assoc (:block/properties block) :heading heading)
-              content (commands/set-markdown-heading (:block/title block') heading)]
+              content (commands/file-based-set-markdown-heading (:block/title block') heading)]
           (merge block' {:block/title content :block/properties properties}))
 
         ;; heading-num1 -> heading-num2
@@ -191,7 +191,7 @@
               content (-> block
                           :block/title
                           commands/clear-markdown-heading
-                          (commands/set-markdown-heading heading))]
+                          (commands/file-based-set-markdown-heading heading))]
           {:block/uuid (:block/uuid block)
            :block/properties properties
            :block/title content}))

+ 0 - 10
src/main/frontend/mixins.cljs

@@ -130,16 +130,6 @@
               :toggle-fn (fn []
                            (swap! open? not)))))))
 
-(def component-editing-mode
-  {:will-mount
-   (fn [state]
-     (state/set-block-component-editing-mode! true)
-     state)
-   :will-unmount
-   (fn [state]
-     (state/set-block-component-editing-mode! false)
-     state)})
-
 (def container-id
   "Notice: the first parameter needs to be a `config` with `id`, optional `sidebar?`, `whiteboard?`"
   {:init (fn [state]

+ 10 - 2
src/main/frontend/modules/outliner/pipeline.cljs

@@ -4,10 +4,13 @@
             [frontend.state :as state]
             [datascript.core :as d]
             [frontend.handler.ui :as ui-handler]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [frontend.fs :as fs]
+            [logseq.common.path :as path]
+            [frontend.config :as config]))
 
 (defn invoke-hooks
-  [{:keys [_request-id repo tx-meta tx-data deleted-block-uuids affected-keys blocks]}]
+  [{:keys [_request-id repo tx-meta tx-data deleted-block-uuids deleted-assets affected-keys blocks]}]
   ;; (prn :debug
   ;;      :request-id request-id
   ;;      :tx-meta tx-meta
@@ -45,6 +48,10 @@
                               tx-data))]
               (d/transact! conn tx-data' tx-meta))
 
+            (when (seq deleted-assets)
+              (doseq [asset deleted-assets]
+                (fs/unlink! repo (path/path-join (config/get-current-repo-assets-root) (str (:block/uuid asset) "." (:ext asset))) {})))
+
             (state/set-state! :editor/start-pos nil)
 
             (when-not (:graph/importing @state/state)
@@ -55,6 +62,7 @@
                          (<= (count blocks) 1000))
                 (state/pub-event! [:plugin/hook-db-tx
                                    {:blocks  blocks
+                                    :deleted-assets deleted-assets
                                     :deleted-block-uuids deleted-block-uuids
                                     :tx-data (:tx-data tx-report)
                                     :tx-meta (:tx-meta tx-report)}])))))))

+ 363 - 363
src/main/frontend/modules/shortcut/config.cljs

@@ -444,12 +444,12 @@
 
    :graph/open                              {:fn      #(do
                                                          (editor-handler/escape-editing)
-                                                         (state/set-state! :ui/open-select :graph-open))
+                                                         (state/pub-event! [:dialog-select/graph-open]))
                                              :binding "alt+shift+g"}
 
    :graph/remove                            {:fn      #(do
                                                          (editor-handler/escape-editing)
-                                                         (state/set-state! :ui/open-select :graph-remove))
+                                                         (state/pub-event! [:dialog-select/graph-remove]))
                                              :binding []}
 
    :graph/add                               {:fn      (fn [] (route-handler/redirect! {:to :graphs}))
@@ -577,7 +577,7 @@
                                              :fn      editor-handler/toggle-open!}
 
    :ui/customize-appearance                 {:binding "c c"
-                                             :fn      #(state/pub-event! [:modal/toggle-appearance-modal])}
+                                             :fn      #(state/pub-event! [:ui/toggle-appearance])}
 
    :git/commit {:binding "mod+g c"
                 :inactive (not (util/electron?))
@@ -607,8 +607,8 @@
       {::commands (set (keys all-built-in-keyboard-shortcuts))
        ::dicts/commands dicts/abbreviated-commands}]
   (assert (= (::commands keyboard-commands) (::dicts/commands keyboard-commands))
-    (str "Keyboard commands must have an english label"
-      (data/diff (::commands keyboard-commands) (::commands keyboard-commands)))))
+          (str "Keyboard commands must have an english label"
+               (data/diff (::commands keyboard-commands) (::commands keyboard-commands)))))
 
 (defn- resolve-fn
   "Converts a keyword fn to the actual fn. The fn to be resolved needs to be
@@ -616,7 +616,7 @@
   [keyword-fn]
   (fn []
     (if-let [resolved-fn (some-> (find-ns-obj (namespace keyword-fn))
-                           (aget (munge (name keyword-fn))))]
+                                 (aget (munge (name keyword-fn))))]
       (resolved-fn)
       (throw (ex-info (str "Unable to resolve " keyword-fn " to a fn") {})))))
 
@@ -633,376 +633,376 @@
   (->> (if (sequential? ks)
          ks (let [{:keys [ns includes excludes]} ks]
               (->> (keys all-built-in-keyboard-shortcuts)
-                (filter (fn [k]
-                          (and (or (and ns (keyword? k)
-                                     (contains? (->> (if (seqable? ns) (seq ns) [ns]) (map #(name %)) (set))
-                                       (namespace k)))
-                                 (and includes (contains? (set includes) k)))
-                            (if (not (seq excludes)) true (not (contains? (set excludes) k)))))))))
-    (select-keys all-built-in-keyboard-shortcuts)
-    (remove (comp :inactive val))
+                   (filter (fn [k]
+                             (and (or (and ns (keyword? k)
+                                           (contains? (->> (if (seqable? ns) (seq ns) [ns]) (map #(name %)) (set))
+                                                      (namespace k)))
+                                      (and includes (contains? (set includes) k)))
+                                  (if (not (seq excludes)) true (not (contains? (set excludes) k)))))))))
+       (select-keys all-built-in-keyboard-shortcuts)
+       (remove (comp :inactive val))
     ;; Convert keyword fns to real fns
-    (map (fn [[k v]]
-           [k (if (keyword? (:fn v))
-                (assoc v :fn (resolve-fn (:fn v)))
-                v)]))
-    (map (fn [[k v]]
-           [k (if (:file-graph? v)
-                (update v :fn wrap-fn-with-file-graph-only-warning)
-                v)]))
-    (into {})))
+       (map (fn [[k v]]
+              [k (if (keyword? (:fn v))
+                   (assoc v :fn (resolve-fn (:fn v)))
+                   v)]))
+       (map (fn [[k v]]
+              [k (if (:file-graph? v)
+                   (update v :fn wrap-fn-with-file-graph-only-warning)
+                   v)]))
+       (into {})))
 
 ;; This is the only var that should be publicly expose :fn functionality
 (defonce ^:large-vars/data-var *config
   (atom
-    {:shortcut.handler/date-picker
-     (build-category-map {:ns :date-picker})
-
-     :shortcut.handler/pdf
-     (-> (build-category-map {:ns :pdf})
-       (with-meta {:before m/enable-when-not-editing-mode!}))
-
-     :shortcut.handler/whiteboard
-     (-> (build-category-map {:ns :whiteboard})
-       (with-meta {:before m/enable-when-not-editing-mode!}))
-
-     :shortcut.handler/auto-complete
-     (build-category-map {:ns :auto-complete})
-
-     :shortcut.handler/cards
-     (-> (build-category-map {:ns :cards})
-       (with-meta {:before m/enable-when-not-editing-mode!}))
-
-     :shortcut.handler/block-editing-only
-     (-> (build-category-map
-           [:editor/escape-editing
-            :editor/backspace
-            :editor/delete
-            :editor/zoom-in
-            :editor/zoom-out
-            :editor/new-block
-            :editor/new-line
-            :editor/follow-link
-            :editor/open-link-in-sidebar
-            :editor/bold
-            :editor/italics
-            :editor/highlight
-            :editor/strike-through
-            :editor/clear-block
-            :editor/kill-line-before
-            :editor/kill-line-after
-            :editor/beginning-of-block
-            :editor/end-of-block
-            :editor/forward-word
-            :editor/backward-word
-            :editor/forward-kill-word
-            :editor/backward-kill-word
-            :editor/replace-block-reference-at-point
-            :editor/copy-embed
-            :editor/paste-text-in-one-block-at-point
-            :editor/insert-youtube-timestamp])
-       (with-meta {:before m/enable-when-editing-mode!}))
-
-     :shortcut.handler/editor-global
-     (-> (build-category-map
-           [:graph/export-as-html
-            :graph/open
-            :graph/remove
-            :graph/add
-            :graph/db-add
-            :graph/db-save
-            :graph/re-index
-            :editor/cycle-todo
-            :editor/up
-            :editor/down
-            :editor/left
-            :editor/right
-            :editor/select-up
-            :editor/select-down
-            :editor/move-block-up
-            :editor/move-block-down
-            :editor/open-edit
-            :editor/select-block-up
-            :editor/select-block-down
-            :editor/select-parent
-            :editor/delete-selection
-            :editor/expand-block-children
-            :editor/collapse-block-children
-            :editor/toggle-block-children
-            :editor/indent
-            :editor/outdent
-            :editor/copy
-            :editor/copy-text
-            :editor/cut
-            :command/toggle-favorite
-            :editor/jump])
-       (with-meta {:before m/enable-when-not-component-editing!}))
-
-     :shortcut.handler/global-prevent-default
-     (-> (build-category-map
-           [:editor/insert-link
-            :editor/select-all-blocks
-            :editor/toggle-number-list
-            :editor/undo
-            :editor/redo
-            :ui/toggle-brackets
-            :go/search-in-page
-            :go/search
-            :go/search-themes
-            :go/electron-find-in-page
-            :go/electron-jump-to-the-next
-            :go/electron-jump-to-the-previous
-            :go/backward
-            :go/forward
-            :search/re-index
-            :sidebar/open-today-page
-            :sidebar/clear
-            :command/run
-            :command-palette/toggle
-            :editor/add-property
-            :window/close])
-       (with-meta {:before m/prevent-default-behavior}))
-
-     :shortcut.handler/global-non-editing-only
-     (-> (build-category-map
-           [:go/home
-            :go/journals
-            :go/all-pages
-            :go/flashcards
-            :go/graph-view
-            :go/all-graphs
-            :go/whiteboards
-            :go/keyboard-shortcuts
-            :go/tomorrow
-            :go/next-journal
-            :go/prev-journal
-            :ui/toggle-document-mode
-            :ui/toggle-settings
-            :ui/toggle-right-sidebar
-            :ui/toggle-left-sidebar
-            :ui/toggle-help
-            :ui/toggle-theme
-            :ui/toggle-contents
-            :editor/open-file-in-default-app
-            :editor/open-file-in-directory
-            :editor/copy-current-file
-            :editor/copy-page-url
-            :editor/new-whiteboard
-            :editor/add-property-deadline
-            :editor/add-property-status
-            :editor/add-property-priority
-            :editor/add-property-icon
-            :editor/toggle-display-all-properties
-            :ui/toggle-wide-mode
-            :ui/select-theme-color
-            :ui/goto-plugins
-            :ui/install-plugins-from-file
-            :editor/toggle-open-blocks
-            :ui/clear-all-notifications
-            :git/commit
-            :sidebar/close-top
-            :dev/show-block-data
-            :dev/show-block-ast
-            :dev/show-page-data
-            :dev/show-page-ast
-            :dev/replace-graph-with-db-file
-            :ui/customize-appearance])
-       (with-meta {:before m/enable-when-not-editing-mode!}))
-
-     :shortcut.handler/misc
+   {:shortcut.handler/date-picker
+    (build-category-map {:ns :date-picker})
+
+    :shortcut.handler/pdf
+    (-> (build-category-map {:ns :pdf})
+        (with-meta {:before m/enable-when-not-editing-mode!}))
+
+    :shortcut.handler/whiteboard
+    (-> (build-category-map {:ns :whiteboard})
+        (with-meta {:before m/enable-when-not-editing-mode!}))
+
+    :shortcut.handler/auto-complete
+    (build-category-map {:ns :auto-complete})
+
+    :shortcut.handler/cards
+    (-> (build-category-map {:ns :cards})
+        (with-meta {:before m/enable-when-not-editing-mode!}))
+
+    :shortcut.handler/block-editing-only
+    (-> (build-category-map
+         [:editor/escape-editing
+          :editor/backspace
+          :editor/delete
+          :editor/zoom-in
+          :editor/zoom-out
+          :editor/new-block
+          :editor/new-line
+          :editor/follow-link
+          :editor/open-link-in-sidebar
+          :editor/bold
+          :editor/italics
+          :editor/highlight
+          :editor/strike-through
+          :editor/clear-block
+          :editor/kill-line-before
+          :editor/kill-line-after
+          :editor/beginning-of-block
+          :editor/end-of-block
+          :editor/forward-word
+          :editor/backward-word
+          :editor/forward-kill-word
+          :editor/backward-kill-word
+          :editor/replace-block-reference-at-point
+          :editor/copy-embed
+          :editor/paste-text-in-one-block-at-point
+          :editor/insert-youtube-timestamp])
+        (with-meta {:before m/enable-when-editing-mode!}))
+
+    :shortcut.handler/editor-global
+    (-> (build-category-map
+         [:graph/export-as-html
+          :graph/open
+          :graph/remove
+          :graph/add
+          :graph/db-add
+          :graph/db-save
+          :graph/re-index
+          :editor/cycle-todo
+          :editor/up
+          :editor/down
+          :editor/left
+          :editor/right
+          :editor/select-up
+          :editor/select-down
+          :editor/move-block-up
+          :editor/move-block-down
+          :editor/open-edit
+          :editor/select-block-up
+          :editor/select-block-down
+          :editor/select-parent
+          :editor/delete-selection
+          :editor/expand-block-children
+          :editor/collapse-block-children
+          :editor/toggle-block-children
+          :editor/indent
+          :editor/outdent
+          :editor/copy
+          :editor/copy-text
+          :editor/cut
+          :command/toggle-favorite
+          :editor/jump])
+        (with-meta {:before m/enable-when-not-component-editing!}))
+
+    :shortcut.handler/global-prevent-default
+    (-> (build-category-map
+         [:editor/insert-link
+          :editor/select-all-blocks
+          :editor/toggle-number-list
+          :editor/undo
+          :editor/redo
+          :ui/toggle-brackets
+          :go/search-in-page
+          :go/search
+          :go/search-themes
+          :go/electron-find-in-page
+          :go/electron-jump-to-the-next
+          :go/electron-jump-to-the-previous
+          :go/backward
+          :go/forward
+          :search/re-index
+          :sidebar/open-today-page
+          :sidebar/clear
+          :command/run
+          :command-palette/toggle
+          :editor/add-property
+          :window/close])
+        (with-meta {:before m/prevent-default-behavior}))
+
+    :shortcut.handler/global-non-editing-only
+    (-> (build-category-map
+         [:go/home
+          :go/journals
+          :go/all-pages
+          :go/flashcards
+          :go/graph-view
+          :go/all-graphs
+          :go/whiteboards
+          :go/keyboard-shortcuts
+          :go/tomorrow
+          :go/next-journal
+          :go/prev-journal
+          :ui/toggle-document-mode
+          :ui/toggle-settings
+          :ui/toggle-right-sidebar
+          :ui/toggle-left-sidebar
+          :ui/toggle-help
+          :ui/toggle-theme
+          :ui/toggle-contents
+          :editor/open-file-in-default-app
+          :editor/open-file-in-directory
+          :editor/copy-current-file
+          :editor/copy-page-url
+          :editor/new-whiteboard
+          :editor/add-property-deadline
+          :editor/add-property-status
+          :editor/add-property-priority
+          :editor/add-property-icon
+          :editor/toggle-display-all-properties
+          :ui/toggle-wide-mode
+          :ui/select-theme-color
+          :ui/goto-plugins
+          :ui/install-plugins-from-file
+          :editor/toggle-open-blocks
+          :ui/clear-all-notifications
+          :git/commit
+          :sidebar/close-top
+          :dev/show-block-data
+          :dev/show-block-ast
+          :dev/show-page-data
+          :dev/show-page-ast
+          :dev/replace-graph-with-db-file
+          :ui/customize-appearance])
+        (with-meta {:before m/enable-when-not-editing-mode!}))
+
+    :shortcut.handler/misc
      ;; always overrides the copy due to "mod+c mod+s"
-     {:misc/copy (:misc/copy all-built-in-keyboard-shortcuts)}}))
+    {:misc/copy (:misc/copy all-built-in-keyboard-shortcuts)}}))
 
 ;; To add a new entry to this map, first add it here and then
 ;; a description for it in frontend.dicts.en/dicts
 ;; Full list of categories for docs purpose
 (defonce ^:large-vars/data-var *category
   (atom
-    {:shortcut.category/basics
-     [:go/search
-      :go/search-themes
-      :editor/new-block
-      :editor/new-line
-      :editor/indent
-      :editor/outdent
-      :editor/select-all-blocks
-      :editor/select-parent
-      :go/search-in-page
-      :command-palette/toggle
-      :go/electron-find-in-page
-      :go/electron-jump-to-the-next
-      :go/electron-jump-to-the-previous
-      :editor/undo
-      :editor/redo
-      :editor/copy
-      :editor/copy-text
-      :editor/cut]
-
-     :shortcut.category/formatting
-     [:editor/bold
-      :editor/insert-link
-      :editor/italics
-      :editor/strike-through
-      :editor/highlight]
-
-     :shortcut.category/navigating
-     [:editor/up
-      :editor/down
-      :editor/left
-      :editor/right
-      :editor/collapse-block-children
-      :editor/expand-block-children
-      :editor/toggle-block-children
-      :editor/toggle-open-blocks
-      :editor/jump
-      :go/backward
-      :go/forward
-      :go/home
-      :go/journals
-      :go/all-pages
-      :go/graph-view
-      :go/all-graphs
-      :go/whiteboards
-      :go/flashcards
-      :go/tomorrow
-      :go/next-journal
-      :go/prev-journal
-      :go/keyboard-shortcuts]
-
-     :shortcut.category/block-editing
-     [:editor/backspace
-      :editor/delete
-      :editor/indent
-      :editor/outdent
-      :editor/new-block
-      :editor/new-line
-      :editor/zoom-in
-      :editor/zoom-out
-      :editor/cycle-todo
-      :editor/follow-link
-      :editor/open-link-in-sidebar
-      :editor/move-block-up
-      :editor/move-block-down
-      :editor/escape-editing]
-
-     :shortcut.category/block-command-editing
-     [:editor/backspace
-      :editor/clear-block
-      :editor/kill-line-before
-      :editor/kill-line-after
-      :editor/beginning-of-block
-      :editor/end-of-block
-      :editor/forward-word
-      :editor/backward-word
-      :editor/forward-kill-word
-      :editor/backward-kill-word
-      :editor/replace-block-reference-at-point
-      :editor/copy-embed
-      :editor/paste-text-in-one-block-at-point
-      :editor/select-up
-      :editor/select-down]
-
-     :shortcut.category/block-selection
-     [:editor/open-edit
-      :editor/select-all-blocks
-      :editor/select-parent
-      :editor/select-block-up
-      :editor/select-block-down
-      :editor/delete-selection
-      :editor/add-property
-      :editor/add-property-deadline
-      :editor/add-property-status
-      :editor/add-property-priority
-      :editor/add-property-icon
-      :editor/toggle-display-all-properties]
-
-     :shortcut.category/toggle
-     [:ui/toggle-help
-      :editor/toggle-open-blocks
-      :editor/toggle-number-list
-      :ui/toggle-wide-mode
-      :ui/toggle-document-mode
-      :ui/toggle-brackets
-      :ui/toggle-theme
-      :ui/toggle-left-sidebar
-      :ui/toggle-right-sidebar
-      :ui/toggle-settings
-      :ui/toggle-contents
-      :ui/customize-appearance]
-
-     :shortcut.category/whiteboard
-     [:editor/new-whiteboard
-      :whiteboard/select
-      :whiteboard/pan
-      :whiteboard/portal
-      :whiteboard/pencil
-      :whiteboard/highlighter
-      :whiteboard/eraser
-      :whiteboard/connector
-      :whiteboard/text
-      :whiteboard/rectangle
-      :whiteboard/ellipse
-      :whiteboard/reset-zoom
-      :whiteboard/zoom-to-fit
-      :whiteboard/zoom-to-selection
-      :whiteboard/zoom-out
-      :whiteboard/zoom-in
-      :whiteboard/send-backward
-      :whiteboard/send-to-back
-      :whiteboard/bring-forward
-      :whiteboard/bring-to-front
-      :whiteboard/lock
-      :whiteboard/unlock
-      :whiteboard/group
-      :whiteboard/ungroup
-      :whiteboard/toggle-grid
-      :whiteboard/clone-left
-      :whiteboard/clone-right
-      :whiteboard/clone-top
-      :whiteboard/clone-bottom]
-
-     :shortcut.category/others
-     [:pdf/previous-page
-      :pdf/next-page
-      :pdf/close
-      :pdf/find
-      :command/toggle-favorite
-      :command/run
-      :graph/export-as-html
-      :graph/open
-      :graph/remove
-      :graph/add
-      :graph/re-index
-      :sidebar/close-top
-      :sidebar/clear
-      :sidebar/open-today-page
-      :search/re-index
-      :editor/insert-youtube-timestamp
-      :editor/open-file-in-default-app
-      :editor/open-file-in-directory
-      :editor/copy-page-url
-      :window/close
-      :auto-complete/prev
-      :auto-complete/next
-      :auto-complete/complete
-      :auto-complete/shift-complete
-      :auto-complete/meta-complete
-      :git/commit
-      :dev/show-block-data
-      :dev/show-block-ast
-      :dev/show-page-data
-      :dev/show-page-ast
-      :dev/replace-graph-with-db-file
-      :ui/clear-all-notifications]
-
-     :shortcut.category/plugins
-     []}))
+   {:shortcut.category/basics
+    [:go/search
+     :go/search-themes
+     :editor/new-block
+     :editor/new-line
+     :editor/indent
+     :editor/outdent
+     :editor/select-all-blocks
+     :editor/select-parent
+     :go/search-in-page
+     :command-palette/toggle
+     :go/electron-find-in-page
+     :go/electron-jump-to-the-next
+     :go/electron-jump-to-the-previous
+     :editor/undo
+     :editor/redo
+     :editor/copy
+     :editor/copy-text
+     :editor/cut]
+
+    :shortcut.category/formatting
+    [:editor/bold
+     :editor/insert-link
+     :editor/italics
+     :editor/strike-through
+     :editor/highlight]
+
+    :shortcut.category/navigating
+    [:editor/up
+     :editor/down
+     :editor/left
+     :editor/right
+     :editor/collapse-block-children
+     :editor/expand-block-children
+     :editor/toggle-block-children
+     :editor/toggle-open-blocks
+     :editor/jump
+     :go/backward
+     :go/forward
+     :go/home
+     :go/journals
+     :go/all-pages
+     :go/graph-view
+     :go/all-graphs
+     :go/whiteboards
+     :go/flashcards
+     :go/tomorrow
+     :go/next-journal
+     :go/prev-journal
+     :go/keyboard-shortcuts]
+
+    :shortcut.category/block-editing
+    [:editor/backspace
+     :editor/delete
+     :editor/indent
+     :editor/outdent
+     :editor/new-block
+     :editor/new-line
+     :editor/zoom-in
+     :editor/zoom-out
+     :editor/cycle-todo
+     :editor/follow-link
+     :editor/open-link-in-sidebar
+     :editor/move-block-up
+     :editor/move-block-down
+     :editor/escape-editing]
+
+    :shortcut.category/block-command-editing
+    [:editor/backspace
+     :editor/clear-block
+     :editor/kill-line-before
+     :editor/kill-line-after
+     :editor/beginning-of-block
+     :editor/end-of-block
+     :editor/forward-word
+     :editor/backward-word
+     :editor/forward-kill-word
+     :editor/backward-kill-word
+     :editor/replace-block-reference-at-point
+     :editor/copy-embed
+     :editor/paste-text-in-one-block-at-point
+     :editor/select-up
+     :editor/select-down]
+
+    :shortcut.category/block-selection
+    [:editor/open-edit
+     :editor/select-all-blocks
+     :editor/select-parent
+     :editor/select-block-up
+     :editor/select-block-down
+     :editor/delete-selection
+     :editor/add-property
+     :editor/add-property-deadline
+     :editor/add-property-status
+     :editor/add-property-priority
+     :editor/add-property-icon
+     :editor/toggle-display-all-properties]
+
+    :shortcut.category/toggle
+    [:ui/toggle-help
+     :editor/toggle-open-blocks
+     :editor/toggle-number-list
+     :ui/toggle-wide-mode
+     :ui/toggle-document-mode
+     :ui/toggle-brackets
+     :ui/toggle-theme
+     :ui/toggle-left-sidebar
+     :ui/toggle-right-sidebar
+     :ui/toggle-settings
+     :ui/toggle-contents
+     :ui/customize-appearance]
+
+    :shortcut.category/whiteboard
+    [:editor/new-whiteboard
+     :whiteboard/select
+     :whiteboard/pan
+     :whiteboard/portal
+     :whiteboard/pencil
+     :whiteboard/highlighter
+     :whiteboard/eraser
+     :whiteboard/connector
+     :whiteboard/text
+     :whiteboard/rectangle
+     :whiteboard/ellipse
+     :whiteboard/reset-zoom
+     :whiteboard/zoom-to-fit
+     :whiteboard/zoom-to-selection
+     :whiteboard/zoom-out
+     :whiteboard/zoom-in
+     :whiteboard/send-backward
+     :whiteboard/send-to-back
+     :whiteboard/bring-forward
+     :whiteboard/bring-to-front
+     :whiteboard/lock
+     :whiteboard/unlock
+     :whiteboard/group
+     :whiteboard/ungroup
+     :whiteboard/toggle-grid
+     :whiteboard/clone-left
+     :whiteboard/clone-right
+     :whiteboard/clone-top
+     :whiteboard/clone-bottom]
+
+    :shortcut.category/others
+    [:pdf/previous-page
+     :pdf/next-page
+     :pdf/close
+     :pdf/find
+     :command/toggle-favorite
+     :command/run
+     :graph/export-as-html
+     :graph/open
+     :graph/remove
+     :graph/add
+     :graph/re-index
+     :sidebar/close-top
+     :sidebar/clear
+     :sidebar/open-today-page
+     :search/re-index
+     :editor/insert-youtube-timestamp
+     :editor/open-file-in-default-app
+     :editor/open-file-in-directory
+     :editor/copy-page-url
+     :window/close
+     :auto-complete/prev
+     :auto-complete/next
+     :auto-complete/complete
+     :auto-complete/shift-complete
+     :auto-complete/meta-complete
+     :git/commit
+     :dev/show-block-data
+     :dev/show-block-ast
+     :dev/show-page-data
+     :dev/show-page-ast
+     :dev/replace-graph-with-db-file
+     :ui/clear-all-notifications]
+
+    :shortcut.category/plugins
+    []}))
 
 (let [category-maps {::category (set (keys @*category))
                      ::dicts/category dicts/categories}]
   (assert (= (::category category-maps) (::dicts/category category-maps))
-    (str "Keys for category maps must have an english label "
-      (data/diff (::category category-maps) (::dicts/category category-maps)))))
+          (str "Keys for category maps must have an english label "
+               (data/diff (::category category-maps) (::dicts/category category-maps)))))
 
 (defn get-category-shortcuts
   "Active list of categories for docs purpose"
@@ -1019,9 +1019,9 @@
      (swap! *shortcut-cmds assoc id (:cmd shortcut-map))
      (let [plugin? (str/starts-with? (str id) ":plugin.")
            category (or (:category shortcut-map)
-                      (if plugin?
-                        :shortcut.category/plugins
-                        :shortcut.category/others))]
+                        (if plugin?
+                          :shortcut.category/plugins
+                          :shortcut.category/others))]
        (swap! *category update category #(conj % id))))))
 
 (defn remove-shortcut!

+ 8 - 6
src/main/frontend/persist_db/browser.cljs

@@ -40,11 +40,11 @@
                    (.sync-app-state worker (ldb/write-transit-str new-state)))))))
 
 (defn get-route-data
-    [route-match]
-    (when (seq route-match)
-      {:to (get-in route-match [:data :name])
-       :path-params (:path-params route-match)
-       :query-params (:query-params route-match)}))
+  [route-match]
+  (when (seq route-match)
+    {:to (get-in route-match [:data :name])
+     :path-params (:path-params route-match)
+     :query-params (:query-params route-match)}))
 
 (defn- sync-ui-state!
   [^js worker]
@@ -133,7 +133,9 @@
 (defn- sqlite-error-handler
   [error]
   (if (= "NoModificationAllowedError"  (.-name error))
-    (state/pub-event! [:show/multiple-tabs-error-dialog])
+    (do
+      (js/console.error error)
+      (state/pub-event! [:show/multiple-tabs-error-dialog]))
     (notification/show! [:div (str "SQLiteDB error: " error)] :error)))
 
 (defrecord InBrowser []

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

@@ -5,7 +5,6 @@
             [frontend.components.journal :as journal]
             [frontend.components.page :as page]
             [frontend.components.all-pages :as all-pages]
-            ;; [frontend.components.all-pages2 :as all-pages]
             [frontend.components.plugins :as plugins]
             [frontend.components.repo :as repo]
             [frontend.components.settings :as settings]

+ 15 - 123
src/main/frontend/state.cljs

@@ -2,12 +2,17 @@
   "Provides main application state, fns associated to set and state based rum
   cursors"
   (:require [cljs-bean.core :as bean]
-            [cljs.core.async :as async :refer [<! >!]]
+            [cljs.core.async :as async :refer [>!]]
             [cljs.spec.alpha :as s]
+            [clojure.set :as set]
             [clojure.string :as string]
+            [datascript.core :as d]
             [dommy.core :as dom]
             [electron.ipc :as ipc]
+            [frontend.db.conn-state :as db-conn-state]
+            [frontend.db.transact :as db-transact]
             [frontend.mobile.util :as mobile-util]
+            [frontend.rum :as r]
             [frontend.spec.storage :as storage-spec]
             [frontend.storage :as storage]
             [frontend.util :as util]
@@ -15,17 +20,12 @@
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [logseq.common.config :as common-config]
-            [frontend.db.transact :as db-transact]
-            [medley.core :as medley]
-            [promesa.core :as p]
-            [rum.core :as rum]
-            [frontend.rum :as r]
+            [logseq.db :as ldb]
             [logseq.db.sqlite.util :as sqlite-util]
+            [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.ui :as shui]
-            [clojure.set :as set]
-            [frontend.db.conn-state :as db-conn-state]
-            [datascript.core :as d]
-            [logseq.db :as ldb]))
+            [promesa.core :as p]
+            [rum.core :as rum]))
 
 (defonce *profile-state
   (atom {}))
@@ -70,14 +70,6 @@
       ;; modals
       :modal/dropdowns                       {}
       :modal/id                              nil
-      :modal/label                           ""
-      :modal/show?                           false
-      :modal/panel-content                   nil
-      :modal/payload                         nil
-      :modal/fullscreen?                     false
-      :modal/close-btn?                      nil
-      :modal/close-backdrop?                 true
-      :modal/subsets                         []
 
       ;; ui
       :ui/viewport                           {}
@@ -1519,110 +1511,10 @@ Similar to re-frame subscriptions"
 
 (defn modal-opened?
   []
-  (:modal/show? @state))
-
-(declare set-modal!)
-(declare close-modal!)
-
-(defn get-sub-modals
-  []
-  (:modal/subsets @state))
-
-(defn set-sub-modal!
-  ([panel-content]
-   (set-sub-modal! panel-content
-                   {:close-btn? true}))
-  ([panel-content {:keys [id label payload close-btn? close-backdrop? show? center?] :as opts}]
-   (if (not (modal-opened?))
-     (set-modal! panel-content opts)
-     (let [modals (:modal/subsets @state)
-           idx (and id (first (keep-indexed #(when (= (:modal/id %2) id) %1)
-                                            modals)))
-           input (medley/filter-vals
-                  #(not (nil? %1))
-                  {:modal/id            id
-                   :modal/label         (if label (name label) "")
-                   :modal/class         (if center? "as-center" "")
-                   :modal/payload       payload
-                   :modal/show?         (if (boolean? show?) show? true)
-                   :modal/panel-content panel-content
-                   :modal/close-btn?    close-btn?
-                   :modal/close-backdrop? (if (boolean? close-backdrop?) close-backdrop? true)})]
-       (swap! state update-in
-              [:modal/subsets (or idx (count modals))]
-              merge input)
-       (:modal/subsets @state)))))
-
-(defn close-sub-modal!
-  ([] (close-sub-modal! nil))
-  ([all?-a-id]
-   (if (true? all?-a-id)
-     (swap! state assoc :modal/subsets [])
-     (let [id     all?-a-id
-           mid    (:modal/id @state)
-           modals (:modal/subsets @state)]
-       (if (and id (not (string/blank? mid)) (= id mid))
-         (close-modal!)
-         (when-let [idx (if id (first (keep-indexed #(when (= (:modal/id %2) id) %1) modals))
-                            (dec (count modals)))]
-           (swap! state assoc :modal/subsets (into [] (medley/remove-nth idx modals)))))))
-   (:modal/subsets @state)))
-
-(defn set-modal!
-  ([modal-panel-content]
-   (set-modal! modal-panel-content
-               {:fullscreen? false
-                :close-btn?  true}))
-  ([modal-panel-content {:keys [id label payload fullscreen? close-btn? close-backdrop? center? panel?
-                                container-overflow-visible? style]}]
-   (let [opened? (modal-opened?)
-         style (if container-overflow-visible?
-                 (merge style {:overflow "visible"})
-                 style)]
-     (when opened?
-       (close-modal!))
-     (when (seq (get-sub-modals))
-       (close-sub-modal! true))
-
-     (async/go
-       (when opened?
-         (<! (async/timeout 100)))
-       (swap! state assoc
-              :modal/id id
-              :modal/label (if label (name label) "")
-              :modal/class (if center? "as-center" "")
-              :modal/show? (boolean modal-panel-content)
-              :modal/panel-content modal-panel-content
-              :modal/payload payload
-              :modal/fullscreen? fullscreen?
-              :modal/panel? (if (boolean? panel?) panel? true)
-              :modal/close-btn? close-btn?
-              :modal/close-backdrop? (if (boolean? close-backdrop?) close-backdrop? true)
-              :modal/style style)))
-   nil))
-
-(defn close-dropdowns!
-  []
-  (let [close-fns (vals (:modal/dropdowns @state))]
-    (doseq [f close-fns]
-      (try (f) (catch :default _e nil)))))
-
-(defn close-modal!
-  [& {:keys [force?]
-      :or {force? false}}]
-  (when (or force? (not (:error/multiple-tabs-access-opfs? @state)))
-    (close-dropdowns!)
-    (if (seq (get-sub-modals))
-      (close-sub-modal!)
-      (swap! state assoc
-             :modal/id nil
-             :modal/label ""
-             :modal/payload nil
-             :modal/show? false
-             :modal/fullscreen? false
-             :modal/panel-content nil
-             :modal/dropdowns {}
-             :ui/open-select nil))))
+  (shui-dialog/has-modal?))
+
+(defn close-modal! []
+  (shui/dialog-close!))
 
 (defn get-reactive-custom-queries-chan
   []
@@ -2215,7 +2107,7 @@ Similar to re-frame subscriptions"
 
 (defn get-modal-id
   []
-  (:modal/id @state))
+  (shui-dialog/get-last-modal-id))
 
 (defn set-auth-id-token
   [id-token]

+ 7 - 119
src/main/frontend/ui.cljs

@@ -576,125 +576,6 @@
       (first binding)
       (shortcut-utils/decorate-binding binding))))
 
-(rum/defc modal-overlay
-  [state close-fn close-backdrop?]
-  [:div.ui__modal-overlay
-   {:class    (case state
-                "entering" "ease-out duration-300 opacity-0"
-                "entered" "ease-out duration-300 opacity-100"
-                "exiting" "ease-in duration-200 opacity-100"
-                "exited" "ease-in duration-200 opacity-0")
-    :on-click #(when close-backdrop? (close-fn))}
-   [:div.absolute.inset-0.opacity-75]])
-
-(rum/defc modal-panel-content-cp <
-  mixins/component-editing-mode
-  [panel-content close-fn]
-  (panel-content close-fn))
-
-(rum/defc modal-panel
-  [show? panel-content transition-state close-fn fullscreen? close-btn? style]
-  [:div.ui__modal-panel.transform.transition-all.sm:min-w-lg.sm
-   (cond->
-    {:class (case transition-state
-              "entering" "ease-out duration-300 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
-              "entered" "ease-out duration-300 opacity-100 translate-y-0 sm:scale-100"
-              "exiting" "ease-in duration-200 opacity-100 translate-y-0 sm:scale-100"
-              "exited" "ease-in duration-200 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95")}
-     (seq style)
-     (assoc :style style))
-   [:div.ui__modal-close-wrap
-    (when-not (false? close-btn?)
-      [:a.ui__modal-close
-       {:aria-label "Close"
-        :type       "button"
-        :on-click   close-fn}
-       [:svg.h-6.w-6
-        {:stroke "currentColor", :view-box "0 0 24 24", :fill "none"}
-        [:path
-         {:d               "M6 18L18 6M6 6l12 12"
-          :stroke-width    "2"
-          :stroke-linejoin "round"
-          :stroke-linecap  "round"}]]])]
-   (when show?
-     [:div (cond-> {:class (if fullscreen? "" "panel-content")}
-             (seq style)
-             (assoc :style style))
-      (modal-panel-content-cp panel-content close-fn)])])
-
-(rum/defc modal < rum/reactive
-  (mixins/event-mixin
-   (fn [state]
-     (mixins/hide-when-esc-or-outside
-      state
-      :on-hide (fn []
-                 (some->
-                  (.querySelector (rum/dom-node state) "button.ui__modal-close")
-                  (.click)))
-      :outside? false)
-     (mixins/on-key-down
-      state
-      {;; enter
-       13 (fn [state e]
-            (when-let [^js enter-el (some-> (rum/dom-node state)
-                                            (.querySelector "button.ui__modal-enter"))]
-              (.preventDefault e)
-              (.click enter-el)))})))
-  []
-  (let [modal-panel-content (state/sub :modal/panel-content)
-        fullscreen? (state/sub :modal/fullscreen?)
-        close-btn? (state/sub :modal/close-btn?)
-        close-backdrop? (state/sub :modal/close-backdrop?)
-        show? (state/sub :modal/show?)
-        label (state/sub :modal/label)
-        style (state/sub :modal/style)
-        class (state/sub :modal/class)
-        close-fn (fn []
-                   (state/close-modal!)
-                   (state/close-settings!))
-        modal-panel-content (or modal-panel-content (fn [_close] [:div]))]
-    [:div.ui__modal
-     {:style {:z-index (if show? 999 -1)
-              :display (if show? "flex" "none")}
-      :label label
-      :class class}
-     (css-transition
-      {:in show? :timeout 0}
-      (fn [state]
-        (modal-overlay state close-fn close-backdrop?)))
-     (css-transition
-      {:in show? :timeout 0}
-      (fn [state]
-        (modal-panel show? modal-panel-content state close-fn fullscreen? close-btn? style)))]))
-
-(rum/defc sub-modal < rum/reactive
-  []
-  (when-let [modals (seq (state/sub :modal/subsets))]
-    (for [[idx modal'] (medley/indexed modals)]
-      (let [id (:modal/id modal')
-            modal-panel-content (:modal/panel-content modal')
-            close-btn? (:modal/close-btn? modal')
-            close-backdrop? (:modal/close-backdrop? modal')
-            show? (:modal/show? modal')
-            label (:modal/label modal')
-            style (:modal/style modal')
-            class (:modal/class modal')
-            close-fn (fn []
-                       (state/close-sub-modal! id))
-            modal-panel-content (or modal-panel-content (fn [_close] [:div]))]
-        [:div.ui__modal.is-sub-modal
-         {:style {:z-index (if show? (+ 999 idx) -1)}
-          :label label
-          :class class}
-         (css-transition
-          {:in show? :timeout 0}
-          (fn [state]
-            (modal-overlay state close-fn close-backdrop?)))
-         (css-transition
-          {:in show? :timeout 0}
-          (fn [state]
-            (modal-panel show? modal-panel-content state close-fn false close-btn? style)))]))))
-
 (defn loading
   ([] (loading (t :loading)))
   ([content] (loading content nil))
@@ -1256,6 +1137,13 @@
                                   (on-select' date))
                                 (notification/show! (str (pr-str value) " is not a valid date. Please try again") :warning)))))))})]))
 
+(rum/defc skeleton
+  []
+  [:div.space-y-2
+   (shui/skeleton {:class "h-8 w-1/3 mb-8"})
+   (shui/skeleton {:class "h-6 w-full"})
+   (shui/skeleton {:class "h-6 w-full"})])
+
 (comment
   (rum/defc emoji-picker
     [opts]

+ 15 - 1
src/main/frontend/ui.css

@@ -166,8 +166,10 @@
 
 .ui__dialog-content {
   &[label=flashcards__cp] {
+    @apply px-5 pt-6 pb-4;
+
     .ui__dialog-main-content {
-      @apply max-h-[85vh] min-h-[65vh] overflow-auto;
+      @apply max-h-[85vh] min-h-[65vh] overflow-auto p-1;
     }
   }
 }
@@ -312,3 +314,15 @@ html.is-mobile {
 input[type='range'] {
   accent-color: var(--lx-accent-10, var(--rx-blue-10));
 }
+
+.ls-dialog-select {
+  @apply w-auto !max-w-fit p-0;
+
+  .cp__select-main {
+    @apply m-0;
+  }
+
+  .item-results-wrap {
+    @apply px-2;
+  }
+}

+ 4 - 4
src/main/frontend/util.cljc

@@ -688,13 +688,13 @@
    ;; for widen char
    (defn safe-dec-current-pos-from-end
      [input current-pos]
-     (if-let [len (and (string? input) (.-length input))]
+     (if-let [len (and (number? current-pos) (string? input) (.-length input))]
        (if-let [input (and (>= len 2) (<= current-pos len)
                            (.substring input (max (- current-pos 20) 0) current-pos))]
          (try
            (let [^js splitter (GraphemeSplitter.)
-                 ^js input (.splitGraphemes splitter input)]
-             (- current-pos (.-length (.pop input))))
+                 ^js input' (.splitGraphemes splitter input)]
+             (- current-pos (.-length (.pop input'))))
            (catch :default e
              (js/console.error e)
              (dec current-pos)))
@@ -705,7 +705,7 @@
    ;; for widen char
    (defn safe-inc-current-pos-from-start
      [input current-pos]
-     (if-let [len (and (string? input) (.-length input))]
+     (if-let [len (and (number? current-pos) (string? input) (.-length input))]
        (if-let [input (and (>= len 2) (<= current-pos len)
                            (.substr input current-pos 20))]
          (try

+ 13 - 14
src/main/frontend/util/cursor.cljs

@@ -47,7 +47,6 @@
             :left js/Number.MAX_SAFE_INTEGER
             :top js/Number.MAX_SAFE_INTEGER}))))))
 
-
 (defn pos [input]
   (when input
     (util/get-selection-start input)))
@@ -66,10 +65,11 @@
 (defn move-cursor-to
   ([input n] (move-cursor-to input n false))
   ([input n delay?']
-   (.setSelectionRange input n n)
-   (when-not (= js/document.activeElement input)
-     (let [focus #(.focus input)]
-       (if delay?' (js/setTimeout focus 16) (focus))))))
+   (when (number? n)
+     (.setSelectionRange input n n)
+     (when-not (= js/document.activeElement input)
+       (let [focus #(.focus input)]
+         (if delay?' (js/setTimeout focus 16) (focus)))))))
 
 (defn move-cursor-forward
   ([input]
@@ -118,8 +118,8 @@
   (let [[content pos'] (get-input-content&pos input)]
     (when content
       (or (zero? pos')
-         (when-let [pre-char (subs content (dec pos') pos')]
-           (= pre-char \newline))))))
+          (when-let [pre-char (subs content (dec pos') pos')]
+            (= pre-char \newline))))))
 
 (defn move-cursor-to-line-end
   [input]
@@ -185,8 +185,8 @@
 
 (defn textarea-cursor-rect-first-row? [cursor]
   (let [elms   (some-> (gdom/getElement "mock-text")
-                   gdom/getChildren
-                   array-seq)
+                       gdom/getChildren
+                       array-seq)
         tops   (->> elms
                     (map mock-char-pos)
                     (map :top)
@@ -196,11 +196,10 @@
 (defn textarea-cursor-first-row? [input]
   (textarea-cursor-rect-first-row? (get-caret-pos input)))
 
-
 (defn textarea-cursor-rect-last-row? [cursor]
   (let [elms   (some-> (gdom/getElement "mock-text")
-                   gdom/getChildren
-                   array-seq)
+                       gdom/getChildren
+                       array-seq)
         tops   (->> elms
                     (map mock-char-pos)
                     (map :top)
@@ -215,8 +214,8 @@
                   gdom/getChildren
                   array-seq)
         chars' (->> elms
-                   (map mock-char-pos)
-                   (group-by :top))
+                    (map mock-char-pos)
+                    (group-by :top))
         tops  (sort (keys chars'))
         tops-p (partition-by #(== (:top cursor) %) tops)
         line-next

+ 106 - 36
src/main/frontend/worker/db/migrate.cljs

@@ -1,5 +1,5 @@
 (ns frontend.worker.db.migrate
-  "DB migration"
+  "Handles SQLite and datascript migrations for DB graphs"
   (:require [datascript.core :as d]
             [logseq.db.sqlite.create-graph :as sqlite-create-graph]
             [logseq.db.frontend.property :as db-property]
@@ -16,6 +16,8 @@
             [logseq.common.uuid :as common-uuid]))
 
 ;; TODO: fixes/rollback
+;; Frontend migrations
+;; ===================
 
 (defn- replace-original-name-content-with-title
   [conn search-db]
@@ -122,6 +124,18 @@
                                   [:db/add id new prop-value]]))))
                 props-to-rename)))))
 
+(defn- rename-classes
+  [classes-to-rename]
+  (fn [conn _search-db]
+    (when (ldb/db-based-graph? @conn)
+      (mapv (fn [[old new]]
+              (merge {:db/id (:db/id (d/entity @conn old))
+                      :db/ident new}
+                     (when-let [new-title (get-in db-class/built-in-classes [new :title])]
+                       {:block/title new-title
+                        :block/name (common-util/page-name-sanity-lc new-title)})))
+            classes-to-rename))))
+
 (defn- update-block-type-many->one
   [conn _search-db]
   (let [db @conn
@@ -244,43 +258,54 @@
             m (cond->
                (db-property-build/build-closed-value-block
                 uuid'
-                "Card view"
+                "Card View"
                 property
                 {:db-ident :logseq.property.view/type.card})
                 true
                 (assoc :block/order (db-order/gen-key)))]
         [m]))))
 
-(defn- add-addresses-in-kvs-table
-  [^Object sqlite-db]
-  (let [columns (->> (.exec sqlite-db #js {:sql "SELECT NAME FROM PRAGMA_TABLE_INFO('kvs')"
-                                           :rowMode "array"})
-                     bean/->clj
-                     (map first)
-                     set)]
-    (when-not (contains? columns "addresses")
-      (let [data (some->> (.exec sqlite-db #js {:sql "select addr, content from kvs"
-                                                :rowMode "array"})
-                          bean/->clj
-                          (map (fn [[addr content]]
-                                 (let [content' (sqlite-util/transit-read content)
-                                       [content' addresses] (if (map? content')
-                                                              [(dissoc content' :addresses)
-                                                               (when-let [addresses (:addresses content')]
-                                                                 (js/JSON.stringify (bean/->js addresses)))]
-                                                              [content' nil])
-                                       content' (sqlite-util/transit-write content')]
-                                   #js {:$addr addr
-                                        :$content content'
-                                        :$addresses addresses}))))]
-        (.exec sqlite-db #js {:sql "alter table kvs add column addresses JSON"})
-        (.transaction sqlite-db
-                      (fn [tx]
-                        (doseq [item data]
-                          (.exec tx #js {:sql "INSERT INTO kvs (addr, content, addresses) values ($addr, $content, $addresses) on conflict(addr) do update set content = $content, addresses = $addresses"
-                                         :bind item}))))))))
+(defn- add-tags-for-typed-display-blocks
+  [conn _search-db]
+  (let [db @conn]
+    (when (ldb/db-based-graph? db)
+      (let [property (d/entity db :logseq.property.node/display-type)
+            ;; fix property
+            _ (when-not (ldb/property? property)
+                (let [fix-tx-data (->>
+                                   (select-keys db-property/built-in-properties [:logseq.property.node/display-type])
+                                   (sqlite-create-graph/build-initial-properties*)
+                                   (map (fn [m]
+                                          (assoc m :db/id (:db/id property)))))]
+                  (d/transact! conn fix-tx-data)))
+            datoms (d/datoms @conn :avet :logseq.property.node/display-type)]
+        (map
+         (fn [d]
+           (when-let [tag-id (ldb/get-class-ident-by-display-type (:v d))]
+             [:db/add (:e d) :block/tags tag-id]))
+         datoms)))))
+
+(defn- rename-card-view-to-gallery-view
+  [conn _search-db]
+  (when (ldb/db-based-graph? @conn)
+    [{:db/id (:db/id (d/entity @conn :logseq.property.view/type.card))
+      :db/ident :logseq.property.view/type.gallery
+      :block/title "Gallery View"}]))
+
+(defn- add-pdf-annotation-class
+  [conn _search-db]
+  (let [db @conn]
+    (when (ldb/db-based-graph? db)
+      (let [datoms (d/datoms db :avet :logseq.property/ls-type :annotation)]
+        (map
+         (fn [d]
+           [:db/add (:e d) :block/tags :logseq.class/pdf-annotation])
+         datoms)))))
 
 (def schema-version->updates
+  "A vec of tuples defining datascript migrations. Each tuple consists of the
+   schema version integer and a migration map. A migration map can have keys of :properties, :classes
+   and :fix."
   [[3 {:properties [:logseq.property/table-sorting :logseq.property/table-filters
                     :logseq.property/table-hidden-columns :logseq.property/table-ordered-columns]
        :classes    []}]
@@ -329,7 +354,17 @@
    [33 {:properties [:logseq.property.pdf/hl-image]}]
    [34 {:properties [:logseq.property.asset/resize-metadata]}]
    [35 {:fix add-card-view}]
-   [36 {:properties [:kv/value :block/type :block/schema :block/parent
+   [37 {:classes [:logseq.class/Code-block :logseq.class/Quote-block :logseq.class/Math-block]
+        :properties [:logseq.property.node/display-type :logseq.property.code/lang]}]
+   [38 {:fix add-tags-for-typed-display-blocks}]
+   [39 {:fix rename-card-view-to-gallery-view}]
+   [40 {:classes [:logseq.class/pdf-annotation]
+        :properties [:logseq.property/ls-type :logseq.property/hl-color :logseq.property/asset
+                     :logseq.property.pdf/hl-page :logseq.property.pdf/hl-value
+                     :logseq.property/hl-type :logseq.property.pdf/hl-image]
+        :fix add-pdf-annotation-class}]
+   [41 {:fix (rename-classes {:logseq.class/pdf-annotation :logseq.class/Pdf-annotation})}]
+   [42 {:properties [:kv/value :block/type :block/schema :block/parent
                      :block/order :block/collapsed? :block/page
                      :block/refs :block/path-refs :block/link
                      :block/title :block/closed-value-property
@@ -341,11 +376,9 @@
   (when (< db-schema/version max-schema-version)
     (js/console.warn (str "Current db schema-version is " db-schema/version ", max available schema-version is " max-schema-version))))
 
-(defn migrate-sqlite-db
-  [db]
-  (add-addresses-in-kvs-table db))
-
 (defn migrate
+  "Migrate 'frontend' datascript schema and data. To add a new migration,
+  add an entry to schema-version->updates and bump db-schema/version"
   [conn search-db]
   (let [db @conn
         version-in-db (or (:kv/value (d/entity db :logseq.kv/schema-version)) 0)]
@@ -393,5 +426,42 @@
               (ldb/transact! conn tx-data' {:db-migrate? true}))
             (println "DB schema migrated to " db-schema/version " from " version-in-db ".")))
         (catch :default e
-          (prn :error "DB migration failed:")
+          (prn :error (str "DB migration failed to migrate to " db-schema/version " from " version-in-db ":"))
           (js/console.error e))))))
+
+;; Backend migrations
+;; ==================
+
+(defn- add-addresses-in-kvs-table
+  [^Object sqlite-db]
+  (let [columns (->> (.exec sqlite-db #js {:sql "SELECT NAME FROM PRAGMA_TABLE_INFO('kvs')"
+                                           :rowMode "array"})
+                     bean/->clj
+                     (map first)
+                     set)]
+    (when-not (contains? columns "addresses")
+      (let [data (some->> (.exec sqlite-db #js {:sql "select addr, content from kvs"
+                                                :rowMode "array"})
+                          bean/->clj
+                          (map (fn [[addr content]]
+                                 (let [content' (sqlite-util/transit-read content)
+                                       [content' addresses] (if (map? content')
+                                                              [(dissoc content' :addresses)
+                                                               (when-let [addresses (:addresses content')]
+                                                                 (js/JSON.stringify (bean/->js addresses)))]
+                                                              [content' nil])
+                                       content' (sqlite-util/transit-write content')]
+                                   #js {:$addr addr
+                                        :$content content'
+                                        :$addresses addresses}))))]
+        (.exec sqlite-db #js {:sql "alter table kvs add column addresses JSON"})
+        (.transaction sqlite-db
+                      (fn [tx]
+                        (doseq [item data]
+                          (.exec tx #js {:sql "INSERT INTO kvs (addr, content, addresses) values ($addr, $content, $addresses) on conflict(addr) do update set content = $content, addresses = $addresses"
+                                         :bind item}))))))))
+
+(defn migrate-sqlite-db
+  "Migrate sqlite db schema"
+  [db]
+  (add-addresses-in-kvs-table db))

+ 82 - 43
src/main/frontend/worker/pipeline.cljs

@@ -69,6 +69,29 @@
   "tx-meta option, skip `d/store` on conn.
 default = false")
 
+(defn- add-missing-properties-to-typed-display-blocks
+  "Add missing properties for those two cases:
+  1. Add corresponding tag when invoking commands like /code block.
+  2. Add properties when tagging a block."
+  [db datoms]
+  (mapcat
+   (fn [d]
+     (cond
+       (and (= (:a d) :logseq.property.node/display-type) (keyword? (:v d)) (:added d))
+       (when-let [tag (ldb/get-class-ident-by-display-type (:v d))]
+         [[:db/add (:e d) :block/tags tag]])
+
+       (and (= (:a d) :block/tags)
+            (contains? ldb/node-display-classes (:db/ident (d/entity db (:v d))))
+            (:added d))
+       (when-let [display-type (ldb/get-display-type-by-class-ident (:db/ident (d/entity db (:v d))))]
+         [(cond->
+           {:db/id (:e d)
+            :logseq.property.node/display-type display-type}
+            (and (= display-type :code) (d/entity db :logseq.kv/latest-code-lang))
+            (assoc :logseq.property.code/lang (:kv/value (d/entity db :logseq.kv/latest-code-lang))))])))
+   datoms))
+
 (defn invoke-hooks
   [repo conn {:keys [tx-meta] :as tx-report} context]
   (when-not (:pipeline-replace? tx-meta)
@@ -93,49 +116,65 @@ default = false")
           {:tx-report final-tx-report})
 
         :else
-        (let [{:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)
-              _ (when (sqlite-util/local-file-based-graph? repo)
-                  (let [page-ids (distinct (map :db/id pages))]
-                    (doseq [page-id page-ids]
-                      (when (d/entity @conn page-id)
-                        (file/sync-to-file repo page-id tx-meta)))))
-              deleted-block-uuids (set (outliner-pipeline/filter-deleted-blocks (:tx-data tx-report)))
-              blocks' (remove (fn [b] (deleted-block-uuids (:block/uuid b))) blocks)
-              block-refs (when (seq blocks')
-                           (rebuild-block-refs repo tx-report blocks'))
-              refs-tx-report (when (seq block-refs)
-                               (ldb/transact! conn block-refs {:pipeline-replace? true}))
-              replace-tx (concat
+        (try
+          (let [display-blocks-tx-data (add-missing-properties-to-typed-display-blocks (:db-after tx-report) (:tx-data tx-report))
+                tx-report (if (seq display-blocks-tx-data)
+                            (let [result (ldb/transact! conn display-blocks-tx-data {:pipeline-replace? true})]
+                              (assoc tx-report
+                                     :tx-data (concat (:tx-data tx-report) (:tx-data result))
+                                     :db-after (:db-after result)))
+                            tx-report)
+                {:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)
+                _ (when (sqlite-util/local-file-based-graph? repo)
+                    (let [page-ids (distinct (map :db/id pages))]
+                      (doseq [page-id page-ids]
+                        (when (d/entity @conn page-id)
+                          (file/sync-to-file repo page-id tx-meta)))))
+                deleted-block-uuids (set (outliner-pipeline/filter-deleted-blocks (:tx-data tx-report)))
+                deleted-assets (keep (fn [id]
+                                       (let [e (d/entity (:db-before tx-report) [:block/uuid id])]
+                                         (when (ldb/asset? e)
+                                           {:block/uuid (:block/uuid e)
+                                            :ext (:logseq.property.asset/type e)}))) deleted-block-uuids)
+                blocks' (remove (fn [b] (deleted-block-uuids (:block/uuid b))) blocks)
+                block-refs (when (seq blocks')
+                             (rebuild-block-refs repo tx-report blocks'))
+                refs-tx-report (when (seq block-refs)
+                                 (ldb/transact! conn block-refs {:pipeline-replace? true}))
+                replace-tx (concat
                           ;; block path refs
-                          (when (seq blocks')
-                            (let [db-after (or (:db-after refs-tx-report) (:db-after tx-report))
-                                  blocks' (keep (fn [b] (d/entity db-after (:db/id b))) blocks')]
-                              (compute-block-path-refs-tx tx-report blocks')))
+                            (when (seq blocks')
+                              (let [db-after (or (:db-after refs-tx-report) (:db-after tx-report))
+                                    blocks' (keep (fn [b] (d/entity db-after (:db/id b))) blocks')]
+                                (compute-block-path-refs-tx tx-report blocks')))
 
                           ;; update block/tx-id
-                          (let [updated-blocks (remove (fn [b] (contains? (set deleted-block-uuids) (:block/uuid b)))
-                                                       (concat pages blocks))
-                                tx-id (get-in (or refs-tx-report tx-report) [:tempids :db/current-tx])]
-                            (keep (fn [b]
-                                    (when-let [db-id (:db/id b)]
-                                      {:db/id db-id
-                                       :block/tx-id tx-id})) updated-blocks)))
-              tx-report' (if (seq replace-tx)
-                           (ldb/transact! conn replace-tx {:pipeline-replace? true})
-                           (do
-                             (when-not (or (exists? js/process)
-                                           (:skip-store-conn tx-meta false))
-                               (d/store @conn))
-                             tx-report))
-              _ (validate-db! repo conn tx-report tx-meta context)
-              full-tx-data (concat (:tx-data tx-report)
-                                   (:tx-data refs-tx-report)
-                                   (:tx-data tx-report'))
-              final-tx-report (assoc tx-report' :tx-data full-tx-data)
-              affected-query-keys (when-not (:importing? context)
-                                    (worker-react/get-affected-queries-keys final-tx-report))]
-          {:tx-report final-tx-report
-           :affected-keys affected-query-keys
-           :deleted-block-uuids deleted-block-uuids
-           :pages pages
-           :blocks blocks})))))
+                            (let [updated-blocks (remove (fn [b] (contains? (set deleted-block-uuids) (:block/uuid b)))
+                                                         (concat pages blocks))
+                                  tx-id (get-in (or refs-tx-report tx-report) [:tempids :db/current-tx])]
+                              (keep (fn [b]
+                                      (when-let [db-id (:db/id b)]
+                                        {:db/id db-id
+                                         :block/tx-id tx-id})) updated-blocks)))
+                tx-report' (if (seq replace-tx)
+                             (ldb/transact! conn replace-tx {:pipeline-replace? true})
+                             (do
+                               (when-not (or (exists? js/process)
+                                             (:skip-store-conn tx-meta false))
+                                 (d/store @conn))
+                               tx-report))
+                _ (validate-db! repo conn tx-report tx-meta context)
+                full-tx-data (concat (:tx-data tx-report)
+                                     (:tx-data refs-tx-report)
+                                     (:tx-data tx-report'))
+                final-tx-report (assoc tx-report' :tx-data full-tx-data)
+                affected-query-keys (when-not (:importing? context)
+                                      (worker-react/get-affected-queries-keys final-tx-report))]
+            {:tx-report final-tx-report
+             :affected-keys affected-query-keys
+             :deleted-block-uuids deleted-block-uuids
+             :deleted-assets deleted-assets
+             :pages pages
+             :blocks blocks})
+          (catch :default e
+            (js/console.error e)))))))

+ 76 - 76
src/main/frontend/worker/rtc/asset.cljs

@@ -1,7 +1,7 @@
 (ns frontend.worker.rtc.asset
   "Fns to sync assets.
   some notes:
-  - block/type contains \"asset\"
+  - has :logseq.property.asset/type
   - block/content, store the asset name
   - an asset-block not having :file/path indicates need to download asset from server
   - an asset-block not having :logseq.property.asset/remote-metadata
@@ -25,7 +25,7 @@
           :in $
           :where
           [?asset :block/uuid]
-          [?asset :block/type "asset"]]
+          [?asset :logseq.property.asset/type]]
         db)
        (apply concat)))
 
@@ -51,66 +51,66 @@
   [get-ws-create-task conn graph-uuid asset-uuids]
   {:pre [(every? uuid? asset-uuids)]}
   (m/sp
-    (when (seq asset-uuids)
-      (let [asset-uuid->url (->> (m/? (ws-util/send&recv get-ws-create-task
-                                                         {:action "get-assets-upload-urls"
-                                                          :graph-uuid graph-uuid
-                                                          :asset-uuid->metadata
-                                                          (into {}
-                                                                (map (fn [asset-uuid] [asset-uuid {"checksum" "TEST-CHECKSUM"}]))
-                                                                asset-uuids)}))
-                                 :asset-uuid->url)]
-        (doseq [[asset-uuid put-url] asset-uuid->url]
-          (assert (uuid? asset-uuid) asset-uuid)
-          (let [{:keys [status] :as r}
-                (c.m/<? (http/put put-url {:headers {"x-amz-meta-checksum" "TEST-CHECKSUM"}
-                                           :body (js/JSON.stringify
-                                                  (clj->js {:TEST-ASSET true
-                                                            :asset-uuid (str asset-uuid)
-                                                            :graph-uuid (str graph-uuid)}))
-                                           :with-credentials? false}))]
-            (if (not= 200 status)
-              (prn :debug-failed-upload-asset {:resp r :asset-uuid asset-uuid :graph-uuid graph-uuid})
+   (when (seq asset-uuids)
+     (let [asset-uuid->url (->> (m/? (ws-util/send&recv get-ws-create-task
+                                                        {:action "get-assets-upload-urls"
+                                                         :graph-uuid graph-uuid
+                                                         :asset-uuid->metadata
+                                                         (into {}
+                                                               (map (fn [asset-uuid] [asset-uuid {"checksum" "TEST-CHECKSUM"}]))
+                                                               asset-uuids)}))
+                                :asset-uuid->url)]
+       (doseq [[asset-uuid put-url] asset-uuid->url]
+         (assert (uuid? asset-uuid) asset-uuid)
+         (let [{:keys [status] :as r}
+               (c.m/<? (http/put put-url {:headers {"x-amz-meta-checksum" "TEST-CHECKSUM"}
+                                          :body (js/JSON.stringify
+                                                 (clj->js {:TEST-ASSET true
+                                                           :asset-uuid (str asset-uuid)
+                                                           :graph-uuid (str graph-uuid)}))
+                                          :with-credentials? false}))]
+           (if (not= 200 status)
+             (prn :debug-failed-upload-asset {:resp r :asset-uuid asset-uuid :graph-uuid graph-uuid})
 
-              (when (some? (d/entity @conn [:block/uuid asset-uuid]))
-                (d/transact! conn [{:block/uuid asset-uuid
-                                    :logseq.property.asset/remote-metadata {:checksum "TEST"}}])))))))))
+             (when (some? (d/entity @conn [:block/uuid asset-uuid]))
+               (d/transact! conn [{:block/uuid asset-uuid
+                                   :logseq.property.asset/remote-metadata {:checksum "TEST"}}])))))))))
 
 (defn new-task--download-assets
   [get-ws-create-task conn graph-uuid asset-uuids]
   {:pre [(every? uuid? asset-uuids)]}
   (m/sp
-    (when (seq asset-uuids)
-      (let [asset-uuid->url
-            (->> (m/? (ws-util/send&recv get-ws-create-task {:action "get-assets-download-urls"
-                                                             :graph-uuid graph-uuid
-                                                             :asset-uuids asset-uuids}))
-                 :asset-uuid->url)]
-        (doseq [[asset-uuid get-url] asset-uuid->url]
-          (assert (uuid? asset-uuid) asset-uuid)
-          (let [{:keys [status _body] :as r} (c.m/<? (http/get get-url {:with-credentials? false}))]
-            (if (not= 200 status)
-              (prn :debug-failed-download-asset {:resp r :asset-uuid asset-uuid :graph-uuid graph-uuid})
-              (when (d/entity @conn [:block/uuid asset-uuid])
-                (d/transact! conn [{:block/uuid asset-uuid
-                                    :file/path "TEST-FILE-PATH"}])
-                (prn :debug-succ-download-asset asset-uuid)))))))))
+   (when (seq asset-uuids)
+     (let [asset-uuid->url
+           (->> (m/? (ws-util/send&recv get-ws-create-task {:action "get-assets-download-urls"
+                                                            :graph-uuid graph-uuid
+                                                            :asset-uuids asset-uuids}))
+                :asset-uuid->url)]
+       (doseq [[asset-uuid get-url] asset-uuid->url]
+         (assert (uuid? asset-uuid) asset-uuid)
+         (let [{:keys [status _body] :as r} (c.m/<? (http/get get-url {:with-credentials? false}))]
+           (if (not= 200 status)
+             (prn :debug-failed-download-asset {:resp r :asset-uuid asset-uuid :graph-uuid graph-uuid})
+             (when (d/entity @conn [:block/uuid asset-uuid])
+               (d/transact! conn [{:block/uuid asset-uuid
+                                   :file/path "TEST-FILE-PATH"}])
+               (prn :debug-succ-download-asset asset-uuid)))))))))
 
 (defonce ^:private *assets-sync-lock (atom nil))
 (defn- holding-assets-sync-lock
   "Use this to prevent multiple assets-sync loops at same time."
   [started-dfv task]
   (m/sp
-    (when-not (compare-and-set! *assets-sync-lock nil true)
-      (let [e (ex-info "Must not run multiple assets-sync loops"
-                       {:type :assets-sync.exception/lock-failed
-                        :missionary/retry true})]
-        (started-dfv e)
-        (throw e)))
-    (try
-      (m/? task)
-      (finally
-        (reset! *assets-sync-lock nil)))))
+   (when-not (compare-and-set! *assets-sync-lock nil true)
+     (let [e (ex-info "Must not run multiple assets-sync loops"
+                      {:type :assets-sync.exception/lock-failed
+                       :missionary/retry true})]
+       (started-dfv e)
+       (throw e)))
+   (try
+     (m/? task)
+     (finally
+       (reset! *assets-sync-lock nil)))))
 
 (def ^:private asset-change-event-schema
   [:map-of
@@ -140,38 +140,38 @@
      (holding-assets-sync-lock
       started-dfv
       (m/sp
-        (try
-          (started-dfv true)
-          (let [action->asset-blocks (get-action->asset-blocks @conn)]
-            (m/?
-             (m/join
-              (constantly nil)
-              (m/sp
+       (try
+         (started-dfv true)
+         (let [action->asset-blocks (get-action->asset-blocks @conn)]
+           (m/?
+            (m/join
+             (constantly nil)
+             (m/sp
                 ;; init phase:
                 ;; generate all asset-change-events from db
-                (when (or (seq (action->asset-blocks :download))
-                          (seq (action->asset-blocks :upload)))
-                  (prn "init phase: generate all asset-change-events from db" action->asset-blocks))
-                (m/? (new-task--download-assets
-                      get-ws-create-task conn graph-uuid (map :block/uuid (action->asset-blocks :download))))
-                (m/? (new-task--upload-assets
-                      get-ws-create-task conn graph-uuid (map :block/uuid (action->asset-blocks :upload)))))
-              (->>
-               (let [{asset-uuids-to-download :download
-                      asset-uuids-to-upload :upload} (m/?> asset-change-event-flow)]
-                 (m/? (new-task--download-assets get-ws-create-task conn graph-uuid asset-uuids-to-download))
-                 (m/? (new-task--upload-assets get-ws-create-task conn graph-uuid asset-uuids-to-upload)))
-               m/ap (m/reduce {} nil)))))
+              (when (or (seq (action->asset-blocks :download))
+                        (seq (action->asset-blocks :upload)))
+                (prn "init phase: generate all asset-change-events from db" action->asset-blocks))
+              (m/? (new-task--download-assets
+                    get-ws-create-task conn graph-uuid (map :block/uuid (action->asset-blocks :download))))
+              (m/? (new-task--upload-assets
+                    get-ws-create-task conn graph-uuid (map :block/uuid (action->asset-blocks :upload)))))
+             (->>
+              (let [{asset-uuids-to-download :download
+                     asset-uuids-to-upload :upload} (m/?> asset-change-event-flow)]
+                (m/? (new-task--download-assets get-ws-create-task conn graph-uuid asset-uuids-to-download))
+                (m/? (new-task--upload-assets get-ws-create-task conn graph-uuid asset-uuids-to-upload)))
+              m/ap (m/reduce {} nil)))))
 
-          (catch Cancelled e
-            (add-log-fn :rtc.asset.log/cancelled {})
-            (throw e)))))}))
+         (catch Cancelled e
+           (add-log-fn :rtc.asset.log/cancelled {})
+           (throw e)))))}))
 
 (comment
   (def x (atom 1))
   (def f (m/ap
-           (let [r (m/?> (m/buffer 10 (m/watch x)))]
-             (m/? (m/sleep 2000))
-             r)))
+          (let [r (m/?> (m/buffer 10 (m/watch x)))]
+            (m/? (m/sleep 2000))
+            r)))
 
   (def cancel ((m/reduce (fn [r e] (prn :e e)) f) prn prn)))

+ 4 - 4
src/main/frontend/worker/rtc/asset_db_listener.cljs

@@ -4,15 +4,15 @@
             [frontend.common.schema-register :as sr]
             [frontend.worker.db-listener :as db-listener]
             [frontend.worker.rtc.asset :as r.asset]
-            [frontend.worker.rtc.client-op :as client-op]))
+            [frontend.worker.rtc.client-op :as client-op]
+            [logseq.db :as ldb]))
 
 (defn entity-datoms=>action+asset-uuid
   [db-after entity-datoms]
   (when-let [e (ffirst entity-datoms)]
     (let [ent (d/entity db-after e)
-          block-uuid (:block/uuid ent)
-          block-type (:block/type ent)]
-      (when (and block-uuid (= block-type "asset"))
+          block-uuid (:block/uuid ent)]
+      (when (and block-uuid (ldb/asset? ent))
         (when-let [action (r.asset/asset-block->upload+download-action ent)]
           [action block-uuid])))))
 

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

@@ -129,6 +129,8 @@
  :right-side-bar/pane-more "More"
  :left-side-bar/switch "Switch to:"
  :left-side-bar/journals "Journals"
+ :left-side-bar/assets "Assets"
+ :left-side-bar/tasks "Tasks"
  :left-side-bar/nav-favorites "Favorites"
  :left-side-bar/nav-recent-pages "Recent"
  :page/something-went-wrong "Something went wrong"

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

@@ -4,7 +4,8 @@
             [frontend.db :as db]
             [frontend.test.helper :as test-helper]
             [datascript.core :as d]
-            [logseq.outliner.property :as outliner-property]))
+            [logseq.outliner.property :as outliner-property]
+            [logseq.db.frontend.class :as db-class]))
 
 (def repo test-helper/test-db-name-db-version)
 
@@ -24,7 +25,11 @@
   (let [opts {:redirect? false :create-first-block? false :class? true}
         _ (test-helper/create-page! "class1" opts)
         _ (test-helper/create-page! "class2" opts)]
-    (is (= ["Asset" "Card" "Cards" "Journal" "Query" "Root Tag" "Task" "class1" "class2"] (sort (map :block/title (model/get-all-classes repo)))))))
+    (is (= (set
+            (concat
+             (map :title (vals db-class/built-in-classes))
+             ["class1" "class2"]))
+           (set (map :block/title (model/get-all-classes repo)))))))
 
 (deftest ^:fix-me get-class-objects-test
   (let [opts {:redirect? false :create-first-block? false :class? true}

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