Browse Source

Merge branch 'feat/db' into feat/text-template

Tienson Qin 7 months ago
parent
commit
22743b4952

+ 1 - 1
.github/workflows/build.yml

@@ -175,7 +175,7 @@ jobs:
         run: cd deps/db && yarn install --frozen-lockfile
         run: cd deps/db && yarn install --frozen-lockfile
 
 
       - name: Validate created DB graphs
       - name: Validate created DB graphs
-        run: cd deps/db && yarn nbb-logseq script/validate_client_db.cljs ../../scripts/db-graph-with-props ../../scripts/db-graph-with-schema --closed-maps --group-errors
+        run: cd deps/db && yarn nbb-logseq script/validate_db.cljs ../../scripts/db-graph-with-props ../../scripts/db-graph-with-schema --closed-maps --group-errors
 
 
   e2e-test:
   e2e-test:
     # TODO: Re-enable when ready to enable tests for file graphs
     # TODO: Re-enable when ready to enable tests for file graphs

+ 2 - 2
bb.edn

@@ -64,7 +64,7 @@
   {:doc "Validate a DB graph's datascript schema"
   {:doc "Validate a DB graph's datascript schema"
    :requires ([babashka.fs :as fs])
    :requires ([babashka.fs :as fs])
    :task (apply shell {:dir "deps/db" :extra-env {"ORIGINAL_PWD" (fs/cwd)}}
    :task (apply shell {:dir "deps/db" :extra-env {"ORIGINAL_PWD" (fs/cwd)}}
-                "yarn -s nbb-logseq script/validate_client_db.cljs"
+                "yarn -s nbb-logseq script/validate_db.cljs"
                 *command-line-args*)}
                 *command-line-args*)}
 
 
   dev:db-query
   dev:db-query
@@ -81,7 +81,7 @@
   {:doc "Create a DB graph given a sqlite.build EDN file"
   {:doc "Create a DB graph given a sqlite.build EDN file"
    :requires ([babashka.fs :as fs])
    :requires ([babashka.fs :as fs])
    :task (apply shell {:dir "deps/db" :extra-env {"ORIGINAL_PWD" (fs/cwd)}}
    :task (apply shell {:dir "deps/db" :extra-env {"ORIGINAL_PWD" (fs/cwd)}}
-                "yarn -s nbb-logseq -cp src:../outliner/src script/create_graph.cljs" *command-line-args*)}
+                "yarn -s nbb-logseq -cp src:../outliner/src:script script/create_graph.cljs" *command-line-args*)}
 
 
   dev:db-import
   dev:db-import
   {:doc "Import a file graph to db graph"
   {:doc "Import a file graph to db graph"

+ 26 - 12
deps/db/script/create_graph.cljs

@@ -1,12 +1,15 @@
 (ns create-graph
 (ns create-graph
-  "An example script that creates a DB graph given a sqlite.build EDN file"
-  (:require [logseq.outliner.cli :as outliner-cli]
-            [clojure.string :as string]
+  "A script that creates a DB graph given a sqlite.build EDN file"
+  (:require ["fs" :as fs]
+            ["os" :as os]
+            ["path" :as node-path]
+            [babashka.cli :as cli]
             [clojure.edn :as edn]
             [clojure.edn :as edn]
+            [clojure.string :as string]
             [datascript.core :as d]
             [datascript.core :as d]
-            ["path" :as node-path]
-            ["os" :as os]
-            ["fs" :as fs]
+            #_:clj-kondo/ignore
+            [logseq.outliner.cli :as outliner-cli]
+            [validate-db]
             [nbb.classpath :as cp]
             [nbb.classpath :as cp]
             [nbb.core :as nbb]))
             [nbb.core :as nbb]))
 
 
@@ -17,11 +20,20 @@
     path
     path
     (node-path/join (or js/process.env.ORIGINAL_PWD ".") path)))
     (node-path/join (or js/process.env.ORIGINAL_PWD ".") path)))
 
 
+(def spec
+  "Options spec"
+  {:help {:alias :h
+          :desc "Print help"}
+   :validate {:alias :v
+              :desc "Validate db after creation"}})
+
 (defn -main [args]
 (defn -main [args]
-  (when (not= 2 (count args))
-    (println "Usage: $0 GRAPH-DIR EDN-PATH")
-    (js/process.exit 1))
-  (let [[graph-dir edn-path] args
+  (let [{options :opts args' :args} (cli/parse-args args {:spec spec})
+        [graph-dir edn-path] args'
+        _ (when (or (nil? graph-dir) (nil? edn-path) (:help options))
+            (println (str "Usage: $0 GRAPH-NAME EDN-PATH [OPTIONS]\nOptions:\n"
+                          (cli/format-opts {:spec spec})))
+            (js/process.exit 1))
         [dir db-name] (if (string/includes? graph-dir "/")
         [dir db-name] (if (string/includes? graph-dir "/")
                         ((juxt node-path/dirname node-path/basename) graph-dir)
                         ((juxt node-path/dirname node-path/basename) graph-dir)
                         [(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
                         [(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
@@ -34,7 +46,9 @@
     ;; (cljs.pprint/pprint _txs)
     ;; (cljs.pprint/pprint _txs)
     (d/transact! conn init-tx)
     (d/transact! conn init-tx)
     (d/transact! conn block-props-tx)
     (d/transact! conn block-props-tx)
-    (println "Created graph" (str db-name "!"))))
+    (println "Created graph" (str db-name "!"))
+    (when (:validate options)
+      (validate-db/validate-db @conn db-name {:group-errors true :closed-maps true :humanize true}))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 2 - 2
deps/db/script/dump_datoms.cljs

@@ -1,5 +1,5 @@
   (ns dump-datoms
   (ns dump-datoms
-    "An example script that dumps all eavt datoms to a specified edn file
+    "A script that dumps all eavt datoms to a specified edn file
 
 
      $ yarn -s nbb-logseq script/dump_datoms.cljs db-name datoms.edn"
      $ yarn -s nbb-logseq script/dump_datoms.cljs db-name datoms.edn"
     (:require [datascript.core :as d]
     (:require [datascript.core :as d]
@@ -28,5 +28,5 @@
     (println "Writing" (count datoms) "datoms to" file)
     (println "Writing" (count datoms) "datoms to" file)
     (fs/writeFileSync file (with-out-str (pprint/pprint datoms)))))
     (fs/writeFileSync file (with-out-str (pprint/pprint datoms)))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 5 - 5
deps/db/script/query.cljs

@@ -1,5 +1,5 @@
 (ns query
 (ns query
-  "An example script that queries any db graph from the commandline e.g.
+  "A script that queries any db graph from the commandline e.g.
 
 
   $ yarn -s nbb-logseq script/query.cljs db-name '[:find (pull ?b [:block/name :block/title]) :where [?b :block/created-at]]'"
   $ yarn -s nbb-logseq script/query.cljs db-name '[:find (pull ?b [:block/name :block/title]) :where [?b :block/created-at]]'"
   (:require [datascript.core :as d]
   (:require [datascript.core :as d]
@@ -44,8 +44,8 @@
             :desc "Lookup entities instead of query"}})
             :desc "Lookup entities instead of query"}})
 
 
 (defn -main [args]
 (defn -main [args]
-  (let [[graph-dir & args'] args
-        options (cli/parse-opts args' {:spec spec})
+  (let [{options :opts args' :args} (cli/parse-args args {:spec spec})
+        [graph-dir & args''] args'
         _ (when (or (nil? graph-dir) (:help options))
         _ (when (or (nil? graph-dir) (:help options))
             (println (str "Usage: $0 GRAPH-NAME [& ARGS] [OPTIONS]\nOptions:\n"
             (println (str "Usage: $0 GRAPH-NAME [& ARGS] [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
                           (cli/format-opts {:spec spec})))
@@ -60,7 +60,7 @@
                             (update :block/properties (fn [props] (map (fn [m] (into {} m)) props)))))
                             (update :block/properties (fn [props] (map (fn [m] (into {} m)) props)))))
                        (:entity options))
                        (:entity options))
                   ;; assumes no :in are in queries
                   ;; assumes no :in are in queries
-                  (let [query (into (edn/read-string (first args')) [:in '$ '%])
+                  (let [query (into (edn/read-string (first args'')) [:in '$ '%])
                         res (d/q query @conn (rules/extract-rules rules/db-query-dsl-rules))]
                         res (d/q query @conn (rules/extract-rules rules/db-query-dsl-rules))]
                     ;; Remove nesting for most queries which just have one :find binding
                     ;; Remove nesting for most queries which just have one :find binding
                     (if (= 1 (count (first res))) (mapv first res) res)))]
                     (if (= 1 (count (first res))) (mapv first res) res)))]
@@ -71,5 +71,5 @@
         (sh ["puget"] {:input (pr-str results) :stdio ["pipe" "inherit" "inherit"]})
         (sh ["puget"] {:input (pr-str results) :stdio ["pipe" "inherit" "inherit"]})
         (pprint/pprint results)))))
         (pprint/pprint results)))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 13 - 10
deps/db/script/validate_client_db.cljs → deps/db/script/validate_db.cljs

@@ -1,4 +1,4 @@
-(ns validate-client-db
+(ns validate-db
   "Script that validates the datascript db of a DB graph
   "Script that validates the datascript db of a DB graph
    NOTE: This script is also used in CI to confirm our db's schema is up to date"
    NOTE: This script is also used in CI to confirm our db's schema is up to date"
   (:require ["os" :as os]
   (:require ["os" :as os]
@@ -14,7 +14,7 @@
             [malli.error :as me]
             [malli.error :as me]
             [nbb.core :as nbb]))
             [nbb.core :as nbb]))
 
 
-(defn validate-client-db
+(defn validate-db*
   "Validate datascript db as a vec of entity maps"
   "Validate datascript db as a vec of entity maps"
   [db ent-maps* {:keys [verbose group-errors humanize closed-maps]}]
   [db ent-maps* {:keys [verbose group-errors humanize closed-maps]}]
   (let [ent-maps (db-malli-schema/update-properties-in-ents db ent-maps*)
   (let [ent-maps (db-malli-schema/update-properties-in-ents db ent-maps*)
@@ -66,6 +66,14 @@
                   :default true
                   :default true
                   :desc "Groups errors by their entity id"}})
                   :desc "Groups errors by their entity id"}})
 
 
+(defn validate-db [db db-name options]
+  (let [datoms (d/datoms db :eavt)
+        ent-maps (db-malli-schema/datoms->entities datoms)]
+    (println "Read graph" (str db-name " with counts: "
+                               (pr-str (assoc (db-validate/graph-counts db ent-maps)
+                                              :datoms (count datoms)))))
+    (validate-db* db ent-maps options)))
+
 (defn- validate-graph [graph-dir options]
 (defn- validate-graph [graph-dir options]
   (let [[dir db-name] (if (string/includes? graph-dir "/")
   (let [[dir db-name] (if (string/includes? graph-dir "/")
                         (let [graph-dir'
                         (let [graph-dir'
@@ -75,13 +83,8 @@
         conn (try (sqlite-cli/open-db! dir db-name)
         conn (try (sqlite-cli/open-db! dir db-name)
                   (catch :default e
                   (catch :default e
                     (println "Error: For graph" (str (pr-str graph-dir) ":") (str e))
                     (println "Error: For graph" (str (pr-str graph-dir) ":") (str e))
-                    (js/process.exit 1)))
-        datoms (d/datoms @conn :eavt)
-        ent-maps (db-malli-schema/datoms->entities datoms)]
-    (println "Read graph" (str db-name " with counts: "
-                               (pr-str (assoc (db-validate/graph-counts @conn ent-maps)
-                                              :datoms (count datoms)))))
-    (validate-client-db @conn ent-maps options)))
+                    (js/process.exit 1)))]
+    (validate-db @conn db-name options)))
 
 
 (defn -main [argv]
 (defn -main [argv]
   (let [{:keys [args opts]} (cli/parse-args argv {:spec spec})
   (let [{:keys [args opts]} (cli/parse-args argv {:spec spec})
@@ -92,5 +95,5 @@
     (doseq [graph-dir args]
     (doseq [graph-dir args]
       (validate-graph graph-dir opts))))
       (validate-graph graph-dir opts))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 12 - 4
deps/db/src/logseq/db/frontend/property.cljs

@@ -153,7 +153,9 @@
                               :schema {:type :node
                               :schema {:type :node
                                        :public? true
                                        :public? true
                                        :view-context :page}
                                        :view-context :page}
-                              :queryable? true}
+                              :queryable? true
+                              :properties
+                              {:logseq.property/description "Provides parent-child relationships between nodes. For tags this enables inheritance and for pages this enables namespaces."}}
      :logseq.property/default-value {:title "Default value"
      :logseq.property/default-value {:title "Default value"
                                      :schema {:type :entity
                                      :schema {:type :entity
                                               :public? false
                                               :public? false
@@ -172,7 +174,9 @@
      :logseq.property/hide-empty-value {:title "Hide empty value"
      :logseq.property/hide-empty-value {:title "Hide empty value"
                                         :schema {:type :checkbox
                                         :schema {:type :checkbox
                                                  :public? true
                                                  :public? true
-                                                 :view-context :property}}
+                                                 :view-context :property}
+                                        :properties
+                                        {:logseq.property/description "Hides a property's value on any node when empty e.g. when a property appears on a node through a tag."}}
      :logseq.property.class/hide-from-node {:title "Hide from Node"
      :logseq.property.class/hide-from-node {:title "Hide from Node"
                                             :schema {:type :checkbox
                                             :schema {:type :checkbox
                                                      :public? true
                                                      :public? true
@@ -186,7 +190,9 @@
                                  :schema {:type :page
                                  :schema {:type :page
                                           :public? true
                                           :public? true
                                           :view-context :page
                                           :view-context :page
-                                          :cardinality :many}}
+                                          :cardinality :many}
+                                 :properties
+                                 {:logseq.property/description "Provides a way for a page to associate to another page i.e. backward compatible tagging."}}
      :logseq.property/background-color {:title "Background color"
      :logseq.property/background-color {:title "Background color"
                                         :schema {:type :default :hide? true}}
                                         :schema {:type :default :hide? true}}
      :logseq.property/background-image {:title "Background image"
      :logseq.property/background-image {:title "Background image"
@@ -548,7 +554,9 @@
      :logseq.property/enable-history? {:title "Enable property history"
      :logseq.property/enable-history? {:title "Enable property history"
                                        :schema {:type :checkbox
                                        :schema {:type :checkbox
                                                 :public? true
                                                 :public? true
-                                                :view-context :property}}
+                                                :view-context :property}
+                                       :properties
+                                       {:logseq.property/description "Records history anytime a property's value changes on a node."}}
      :logseq.property.history/block {:title "History block"
      :logseq.property.history/block {:title "History block"
                                      :schema {:type :entity
                                      :schema {:type :entity
                                               :hide? true}}
                                               :hide? true}}

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

@@ -183,14 +183,14 @@
              {:property-attributes
              {:property-attributes
               (merge {:db/id (or (property-db-ids prop-name)
               (merge {:db/id (or (property-db-ids prop-name)
                                  (throw (ex-info "No :db/id for property" {:property prop-name})))}
                                  (throw (ex-info "No :db/id for property" {:property prop-name})))}
-                     (select-keys prop-m [:build/properties-ref-types]))}))
+                     (select-keys prop-m [:build/properties-ref-types :block/created-at :block/updated-at]))}))
           [(merge (sqlite-util/build-new-property (get-ident all-idents prop-name)
           [(merge (sqlite-util/build-new-property (get-ident all-idents prop-name)
                                                   (db-property/get-property-schema prop-m)
                                                   (db-property/get-property-schema prop-m)
                                                   {:block-uuid (:block/uuid prop-m)
                                                   {:block-uuid (:block/uuid prop-m)
                                                    :title (:block/title prop-m)})
                                                    :title (:block/title prop-m)})
                   {:db/id (or (property-db-ids prop-name)
                   {:db/id (or (property-db-ids prop-name)
                               (throw (ex-info "No :db/id for property" {:property prop-name})))}
                               (throw (ex-info "No :db/id for property" {:property prop-name})))}
-                  (select-keys prop-m [:build/properties-ref-types]))])
+                  (select-keys prop-m [:build/properties-ref-types :block/created-at :block/updated-at]))])
         pvalue-tx-m
         pvalue-tx-m
         (->property-value-tx-m new-block (:build/properties prop-m) properties all-idents)]
         (->property-value-tx-m new-block (:build/properties prop-m) properties all-idents)]
     (cond-> []
     (cond-> []
@@ -415,8 +415,8 @@
   (vec
   (vec
    (mapcat
    (mapcat
     (fn [{:keys [page blocks]}]
     (fn [{:keys [page blocks]}]
-      (let [ignore-page-tx? (and build-existing-tx? (not (::new-page? (meta page))) (not (:build/keep-uuid? page)))
-            page' (if ignore-page-tx?
+      (let [build-existing-tx?' (and build-existing-tx? (not (::new-page? (meta page))) (not (:build/keep-uuid? page)))
+            page' (if build-existing-tx?'
                     page
                     page
                     (merge
                     (merge
                      ;; TODO: Use sqlite-util/build-new-page
                      ;; TODO: Use sqlite-util/build-new-page
@@ -430,8 +430,8 @@
                           page-id-fn)]
                           page-id-fn)]
         (into
         (into
          ;; page tx
          ;; page tx
-         (if ignore-page-tx?
-           []
+         (if build-existing-tx?'
+           [(select-keys page [:block/uuid :block/created-at :block/updated-at])]
            (build-page-tx page' all-idents page-uuids properties))
            (build-page-tx page' all-idents page-uuids properties))
          ;; blocks tx
          ;; blocks tx
          (reduce (fn [acc m]
          (reduce (fn [acc m]
@@ -707,8 +707,8 @@
     See auto-create-ontology for more details
     See auto-create-ontology for more details
   * :build-existing-tx? - When set to true, blocks, pages, properties and classes with :block/uuid are treated as
   * :build-existing-tx? - When set to true, blocks, pages, properties and classes with :block/uuid are treated as
      existing in DB and are skipped for creation. This is useful for building tx on existing DBs e.g. for importing.
      existing in DB and are skipped for creation. This is useful for building tx on existing DBs e.g. for importing.
-     Blocks are updated with any attributes passed to it while all other node types are ignored for update unless
-     :build/keep-uuid? is set.
+     Blocks and pages are updated with any attributes passed to it while all other node types are ignored for update
+     unless :build/keep-uuid? is set.
   * :page-id-fn - custom fn that returns ent lookup id for page refs e.g. `[:block/uuid X]`
   * :page-id-fn - custom fn that returns ent lookup id for page refs e.g. `[:block/uuid X]`
     Default is :db/id
     Default is :db/id
 
 

+ 3 - 1
deps/db/src/logseq/db/sqlite/create_graph.cljs

@@ -105,7 +105,9 @@
   as built-in?. Returns their tx data as well as data needed for subsequent build steps"
   as built-in?. Returns their tx data as well as data needed for subsequent build steps"
   []
   []
   ;; bootstrap-idents must either be necessary to define a property or be used on every property
   ;; bootstrap-idents must either be necessary to define a property or be used on every property
-  (let [bootstrap-idents #{:logseq.property/type :logseq.property/hide? :logseq.property/built-in?}
+  (let [bootstrap-idents #{:logseq.property/type :logseq.property/hide? :logseq.property/built-in?
+                           ;; Required to define :properties on a property
+                           :logseq.property/created-from-property}
         bootstrap-properties (map build-bootstrap-property bootstrap-idents)
         bootstrap-properties (map build-bootstrap-property bootstrap-idents)
         ;; First tx bootstrap properties so they can take affect. Then tx the bootstrap properties on themselves
         ;; First tx bootstrap properties so they can take affect. Then tx the bootstrap properties on themselves
         bootstrap-properties-tx (into (mapv #(apply dissoc % bootstrap-idents) bootstrap-properties)
         bootstrap-properties-tx (into (mapv #(apply dissoc % bootstrap-idents) bootstrap-properties)

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

@@ -492,11 +492,12 @@
                      (mapcat #(sqlite-build/extract-from-blocks (:blocks %) (fn [m] (some-> m :block/uuid vector)))
                      (mapcat #(sqlite-build/extract-from-blocks (:blocks %) (fn [m] (some-> m :block/uuid vector)))
                              pages-and-blocks))
                              pages-and-blocks))
              set)
              set)
+        ;; only looks one-level deep in properties e.g. not inside :build/page
         ref-uuids
         ref-uuids
-        (->> (concat (mapcat (comp get-pvalue-uuids :build/properties) (vals classes))
-                     (mapcat (comp get-pvalue-uuids :build/properties) (vals properties))
-                     (mapcat (comp get-pvalue-uuids :build/properties :page) pages-and-blocks)
-                     (mapcat #(sqlite-build/extract-from-blocks (:blocks %) (comp get-pvalue-uuids :build/properties)) pages-and-blocks))
+        (->> (concat (mapcat get-pvalue-uuids (vals classes))
+                     (mapcat get-pvalue-uuids (vals properties))
+                     (mapcat (comp get-pvalue-uuids :page) pages-and-blocks)
+                     (mapcat #(sqlite-build/extract-from-blocks (:blocks %) get-pvalue-uuids) pages-and-blocks))
              set)]
              set)]
     (set/difference ref-uuids known-uuids)))
     (set/difference ref-uuids known-uuids)))
 
 

+ 1 - 1
deps/graph-parser/script/db_import.cljs

@@ -189,5 +189,5 @@
       (when (:verbose options') (println "Transacted" (count (d/datoms @conn :eavt)) "datoms"))
       (when (:verbose options') (println "Transacted" (count (d/datoms @conn :eavt)) "datoms"))
       (println "Created graph" (str db-name "!")))))
       (println "Created graph" (str db-name "!")))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 1 - 1
deps/outliner/script/transact.cljs

@@ -37,5 +37,5 @@
         (d/transact! conn update-tx)
         (d/transact! conn update-tx)
         (println "Updated" (count update-tx) "block(s) for graph" (str db-name "!"))))))
         (println "Updated" (count update-tx) "block(s) for graph" (str db-name "!"))))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 1 - 1
deps/publishing/script/publishing.cljs

@@ -60,5 +60,5 @@
       (publish-db-graph static-dir graph-dir output-path options)
       (publish-db-graph static-dir graph-dir output-path options)
       (publish-file-graph static-dir graph-dir output-path options))))
       (publish-file-graph static-dir graph-dir output-path options))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 13 - 24
deps/shui/src/logseq/shui/popup/core.cljs

@@ -122,14 +122,13 @@
   ([] (when-let [id (some-> (get-popups) (last) :id)] (hide! id 0)))
   ([] (when-let [id (some-> (get-popups) (last) :id)] (hide! id 0)))
   ([id] (hide! id 0 {}))
   ([id] (hide! id 0 {}))
   ([id delay] (hide! id delay {}))
   ([id delay] (hide! id delay {}))
-  ([id delay {:keys [all?]}]
+  ([id delay {:keys [_all?]}]
    (when-let [popup (get-popup id)]
    (when-let [popup (get-popup id)]
      (let [config (last popup)
      (let [config (last popup)
            target (:target config)
            target (:target config)
-           f #(if all?
-                (reset! *popups [])
-                (do (detach-popup! id)
-                  (some-> (:on-after-hide config) (apply []))))]
+           f (fn []
+               (detach-popup! id)
+               (some-> (:on-after-hide config) (apply [])))]
        (some-> (:on-before-hide config) (apply []))
        (some-> (:on-before-hide config) (apply []))
        (some-> target (d/remove-attr! "data-popup-active"))
        (some-> target (d/remove-attr! "data-popup-active"))
        (if (and (number? delay) (> delay 0))
        (if (and (number? delay) (> delay 0))
@@ -146,20 +145,6 @@
            auto-side? _auto-focus? _target root-props content-props
            auto-side? _auto-focus? _target root-props content-props
            _on-before-hide _on-after-hide]
            _on-before-hide _on-after-hide]
     :as _props}]
     :as _props}]
-  ;; disableOutsidePointerEvents
-  ;(rum/use-effect!
-  ;  (fn []
-  ;    (when-not as-dropdown?
-  ;      (let [^js style js/document.body.style
-  ;            set-pointer-event! #(set! (. style -pointerEvents) %)
-  ;            try-unset! #(when (nil? (seq @*popups))
-  ;                          (set-pointer-event! nil))]
-  ;        (if open?
-  ;          (set-pointer-event! "none")
-  ;          (try-unset!))
-  ;        #(try-unset!))))
-  ;  [open?])
-
   (when-let [[x y _ height] position]
   (when-let [[x y _ height] position]
     (let [popup-root (if (not force-popover?) dropdown-menu popover)
     (let [popup-root (if (not force-popover?) dropdown-menu popover)
           popup-trigger (if (not force-popover?) dropdown-menu-trigger popover-trigger)
           popup-trigger (if (not force-popover?) dropdown-menu-trigger popover-trigger)
@@ -174,7 +159,12 @@
           auto-side? (if (boolean? auto-side?) auto-side? true)
           auto-side? (if (boolean? auto-side?) auto-side? true)
           content-props (cond-> content-props
           content-props (cond-> content-props
                           auto-side? (assoc :side (auto-side-fn)))
                           auto-side? (assoc :side (auto-side-fn)))
-          hide (fn [] (hide! id 1))]
+          handle-key-escape! (fn [^js e]
+                               (when-not (false? (some-> content-props (:onEscapeKeyDown) (apply [e])))
+                                 (hide! id 1)))
+          handle-pointer-outside! (fn [^js e]
+                                    (when-not (false? (some-> content-props (:onPointerDownOutside) (apply [e])))
+                                      (hide! id 1)))]
       (popup-root
       (popup-root
         (merge root-props {:open open?})
         (merge root-props {:open open?})
         (popup-trigger
         (popup-trigger
@@ -186,10 +176,9 @@
                            :width 1
                            :width 1
                            :top y
                            :top y
                            :left x}} ""))
                            :left x}} ""))
-        (let [content-props (cond-> (merge {:onEscapeKeyDown hide
-                                            :disableOutsideScroll false
-                                            :onPointerDownOutside hide}
-                                      content-props)
+        (let [content-props (cond-> (merge content-props {:onEscapeKeyDown handle-key-escape!
+                                                          :disableOutsideScroll false
+                                                          :onPointerDownOutside handle-pointer-outside!})
                               (and (not force-popover?)
                               (and (not force-popover?)
                                 (not as-dropdown?))
                                 (not as-dropdown?))
                               (assoc :on-key-down (fn [^js e]
                               (assoc :on-key-down (fn [^js e]

+ 2 - 2
resources/css/shui.css

@@ -284,11 +284,11 @@ div[data-radix-popper-content-wrapper] {
   @apply overflow-y-auto;
   @apply overflow-y-auto;
 
 
   &[data-side=top] {
   &[data-side=top] {
-    max-height: calc(var(--radix-dropdown-menu-content-available-height) - 40px);
+    max-height: calc(var(--radix-dropdown-menu-content-available-height) - 20px);
   }
   }
 
 
   &[data-side=bottom] {
   &[data-side=bottom] {
-    max-height: calc(var(--radix-dropdown-menu-content-available-height) - 20px);
+    max-height: calc(var(--radix-dropdown-menu-content-available-height) - 10px);
   }
   }
 
 
   &.text-popover-foreground {
   &.text-popover-foreground {

+ 1 - 1
scripts/src/logseq/tasks/db_graph/create_graph_with_large_sizes.cljs

@@ -83,5 +83,5 @@
     #_(d/transact! conn blocks-tx)
     #_(d/transact! conn blocks-tx)
     (println "Created graph" (str db-name " with " (count (d/datoms @conn :eavt)) " datoms!"))))
     (println "Created graph" (str db-name " with " (count (d/datoms @conn :eavt)) " datoms!"))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 1 - 1
scripts/src/logseq/tasks/db_graph/create_graph_with_properties.cljs

@@ -222,5 +222,5 @@
     (d/transact! conn block-props-tx)
     (d/transact! conn block-props-tx)
     (println "Created graph" (str db-name " with " (count (d/datoms @conn :eavt)) " datoms!"))))
     (println "Created graph" (str db-name " with " (count (d/datoms @conn :eavt)) " datoms!"))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 1 - 1
scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs

@@ -423,5 +423,5 @@
     (when (:debug options) (write-debug-file @conn))
     (when (:debug options) (write-debug-file @conn))
     (println "Created graph" (str db-name "!"))))
     (println "Created graph" (str db-name "!"))))
 
 
-(when (= nbb/*file* (:file (meta #'-main)))
+(when (= nbb/*file* (nbb/invoked-file))
   (-main *command-line-args*))
   (-main *command-line-args*))

+ 5 - 1
src/main/frontend/components/block.css

@@ -245,7 +245,11 @@
   @apply w-full inline;
   @apply w-full inline;
 
 
   > .ui__checkbox {
   > .ui__checkbox {
-    @apply relative top-[2px];
+    @apply relative top-0.5;
+
+    &.checked {
+      @apply top-1;
+    }
 
 
     > span {
     > span {
       @apply h-full;
       @apply h-full;

+ 55 - 53
src/main/frontend/components/editor.cljs

@@ -661,61 +661,63 @@
 (rum/defc shui-editor-popups
 (rum/defc shui-editor-popups
   [id format action _data]
   [id format action _data]
   (hooks/use-effect!
   (hooks/use-effect!
-   (fn []
-     (let [pid (case action
-                 :commands
-                 (open-editor-popup! :commands
-                                     (commands id format)
-                                     {:content-props {:withoutAnimation false}})
-
-                 (:block-search :page-search :page-search-hashtag)
-                 (open-editor-popup! action
-                                     (if (= :block-search action)
-                                       (block-search id format)
-                                       (page-search id format))
-                                     {:root-props {:onOpenChange
-                                                   #(when-not %
-                                                      (when (contains?
-                                                             #{:block-search :page-search :page-search-hashtag}
-                                                             (state/get-editor-action))
-                                                        (state/clear-editor-action!)))}})
-
-                 :datepicker
-                 (open-editor-popup! :datepicker
-                                     (datetime-comp/date-picker id format nil) {})
-
-                 :input
-                 (open-editor-popup! :input
-                                     (editor-input id
-                                                   (fn [command m]
-                                                     (editor-handler/handle-command-input command id format m))
-                                                   (fn []
-                                                     (editor-handler/handle-command-input-close id)))
-                                     {:content-props {:onOpenAutoFocus #()}})
-
-                 :select-code-block-mode
-                 (open-editor-popup! :code-block-mode-picker
-                                     (code-block-mode-picker id format) {})
-
-                 :template-search
-                 (open-editor-popup! :template-search
-                                     (template-search id format) {})
-
-                 (:property-search :property-value-search)
-                 (open-editor-popup! action
-                                     (if (= :property-search action)
-                                       (property-search id) (property-value-search id))
-                                     {})
-
-                 :zotero
-                 (open-editor-popup! :zotero
-                                     (zotero/zotero-search id) {})
+    (fn []
+      (let [pid (case action
+                  :commands
+                  (open-editor-popup! :commands
+                    (commands id format)
+                    {:content-props {:withoutAnimation false}})
+
+                  (:block-search :page-search :page-search-hashtag)
+                  (open-editor-popup! action
+                    (if (= :block-search action)
+                      (block-search id format)
+                      (page-search id format))
+                    {:root-props {:onOpenChange
+                                  #(when-not %
+                                     (when (contains?
+                                             #{:block-search :page-search :page-search-hashtag}
+                                             (state/get-editor-action))
+                                       (state/clear-editor-action!)))}})
+
+                  :datepicker
+                  (open-editor-popup! :datepicker
+                    (datetime-comp/date-picker id format nil) {})
+
+                  :input
+                  (open-editor-popup! :input
+                    (editor-input id
+                      ;; on-submit
+                      (fn [command m]
+                        (editor-handler/handle-command-input command id format m))
+                      ;; on-cancel
+                      (fn []
+                        (editor-handler/handle-command-input-close id)))
+                    {:content-props {:onOpenAutoFocus #()}})
+
+                  :select-code-block-mode
+                  (open-editor-popup! :code-block-mode-picker
+                    (code-block-mode-picker id format) {})
+
+                  :template-search
+                  (open-editor-popup! :template-search
+                    (template-search id format) {})
+
+                  (:property-search :property-value-search)
+                  (open-editor-popup! action
+                    (if (= :property-search action)
+                      (property-search id) (property-value-search id))
+                    {})
+
+                  :zotero
+                  (open-editor-popup! :zotero
+                    (zotero/zotero-search id) {})
 
 
                   ;; TODO: try remove local model state
                   ;; TODO: try remove local model state
-                 false)]
-       #(when pid
-          (shui/popup-hide! pid))))
-   [action])
+                  false)]
+        #(when pid
+           (shui/popup-hide! pid))))
+    [action])
   [:<>])
   [:<>])
 
 
 (rum/defc command-popups <
 (rum/defc command-popups <

+ 17 - 11
src/main/frontend/components/property/value.cljs

@@ -79,7 +79,9 @@
                        (db-property-handler/remove-block-property!
                        (db-property-handler/remove-block-property!
                         (:db/id block)
                         (:db/id block)
                         :logseq.property/icon))
                         :logseq.property/icon))
-                     (clear-overlay!))]
+                     (clear-overlay!)
+                     (when editing?
+                       (editor-handler/restore-last-saved-cursor!)))]
 
 
     (hooks/use-effect!
     (hooks/use-effect!
      (fn []
      (fn []
@@ -92,13 +94,16 @@
             (fn []
             (fn []
               (when-let [^js target (some-> (.querySelector container (str "#ls-block-" (str (:block/uuid block))))
               (when-let [^js target (some-> (.querySelector container (str "#ls-block-" (str (:block/uuid block))))
                                             (.querySelector ".block-main-container"))]
                                             (.querySelector ".block-main-container"))]
+                (state/set-editor-action! :property-icon-picker)
                 (shui/popup-show! target
                 (shui/popup-show! target
-                                  #(icon-component/icon-search
-                                    {:on-chosen on-chosen!
-                                     :icon-value icon
-                                     :del-btn? (some? icon)})
-                                  {:id :ls-icon-picker
-                                   :align :start})))))))
+                  #(icon-component/icon-search
+                     {:on-chosen on-chosen!
+                      :icon-value icon
+                      :del-btn? (some? icon)})
+                  {:id :ls-icon-picker
+                   :on-after-hide #(state/set-editor-action! nil)
+                   :content-props {:onEscapeKeyDown #(when editing? (editor-handler/restore-last-saved-cursor!))}
+                   :align :start})))))))
      [editing?])
      [editing?])
 
 
     [:div.col-span-3.flex.flex-row.items-center.gap-2
     [:div.col-span-3.flex.flex-row.items-center.gap-2
@@ -1271,10 +1276,11 @@
                                    (or (= (:db/id v) (:db/id block))
                                    (or (= (:db/id v) (:db/id block))
                                        ;; property value self embedded
                                        ;; property value self embedded
                                        (= (:db/id (:block/link v)) (:db/id block))))]
                                        (= (:db/id (:block/link v)) (:db/id block))))]
-     (if (or (and (de/entity? v) (self-value-or-embedded? v))
-             (and (coll? v) (every? de/entity? v)
-                  (some self-value-or-embedded? v))
-             (and (= p-block (:db/id block)) (= p-property (:db/id property))))
+     (if (and (or (and (de/entity? v) (self-value-or-embedded? v))
+                  (and (coll? v) (every? de/entity? v)
+                       (some self-value-or-embedded? v))
+                  (and (= p-block (:db/id block)) (= p-property (:db/id property))))
+              (not= :logseq.class/Tag (:db/ident block)))
        [:div.flex.flex-row.items-center.gap-1
        [:div.flex.flex-row.items-center.gap-1
         [:div.warning "Self reference"]
         [:div.warning "Self reference"]
         (shui/button {:variant :outline
         (shui/button {:variant :outline

+ 6 - 0
src/main/frontend/handler/editor.cljs

@@ -1895,6 +1895,12 @@
 
 
   (handle-command-input-close id))
   (handle-command-input-close id))
 
 
+(defn restore-last-saved-cursor!
+  ([] (restore-last-saved-cursor! (state/get-input)))
+  ([input]
+   (when-let [saved-cursor (and input (state/get-editor-last-pos))]
+     (cursor/move-cursor-to input saved-cursor true))))
+
 (defn- close-autocomplete-if-outside
 (defn- close-autocomplete-if-outside
   [input]
   [input]
   (when (and input
   (when (and input