Browse Source

Merge branch 'feat/db' into feat/capacitor-new

Tienson Qin 5 months ago
parent
commit
0b9b327630

+ 9 - 0
README.md

@@ -54,6 +54,7 @@
 
 ## Table of Contents
 
+  * [<g-emoji class="g-emoji" alias="database" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png">🚀</g-emoji> Database Version](#-database-version)
   * [<g-emoji class="g-emoji" alias="thinking" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f914.png">🤔</g-emoji> Why Logseq?](#-why-logseq)
   * [<g-emoji class="g-emoji" alias="eyes" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f440.png">👀</g-emoji> How can I use it?](#-how-can-i-use-it)
   * [<g-emoji class="g-emoji" alias="books" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4da.png">📚</g-emoji> Learn more](#-learn-more)
@@ -65,6 +66,14 @@
   * [<g-emoji class="g-emoji" alias="sparkles" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/2728.png">✨</g-emoji> Inspiration](#-inspiration)
 * [<g-emoji class="g-emoji" alias="pray" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f64f.png">🙏</g-emoji> Thank You](#-thank-you)
 
+## 🚀 Database Version
+
+The Database version (DB version) of Logseq introduces DB graphs while maintaining support for file graphs. [See this page](https://github.com/logseq/docs/blob/feat/db/db-version.md) to get an overview of the main features for DB graphs. If you are an existing user, [see this page](https://github.com/logseq/docs/blob/feat/db/db-version-changes.md) to get an overview of changes with the DB version.
+
+The DB version of Logseq is alpha software. When using DB graphs, we recommend you create a dedicated test graph and choose one project/workflow (that’s not crucial for you) to test with it. **Data loss is possible**, which is why we recommend [automated backups](https://github.com/logseq/docs/blob/feat/db/db-version.md#automated-backup) or making [regular SQLite DB backups](https://github.com/logseq/docs/blob/feat/db/db-version.md#graph-export). When using file graphs, **data corruption is possible** as some file content can be duplicated. We only recommend using file graphs if you are making regular backups with git.
+
+To try the latest web version, go to https://test.logseq.com/.
+
 ## 🤔 Why Logseq?
 
 [Logseq](https://logseq.com) is a **knowledge management** and **collaboration** platform. It focuses on **privacy**, **longevity**, and [**user control**](https://www.gnu.org/philosophy/free-sw.en.html). Logseq offers a range of **powerful tools** for **knowledge management**, **collaboration**, **PDF annotation**, and **task management** with support for multiple file formats, including **Markdown** and **Org-mode**, and **various features** for organizing and structuring your notes.

+ 7 - 17
deps/db/script/create_graph.cljs

@@ -2,17 +2,16 @@
   "A script that creates or updates a DB graph given a sqlite.build EDN file.
    If the given graph already exists, the EDN file updates the graph."
   (:require ["fs" :as fs]
-            ["os" :as os]
             ["path" :as node-path]
             [babashka.cli :as cli]
             [clojure.edn :as edn]
-            [clojure.string :as string]
             [datascript.core :as d]
             [logseq.db.sqlite.export :as sqlite-export]
             [logseq.outliner.cli :as outliner-cli]
             [nbb.classpath :as cp]
             [nbb.core :as nbb]
-            [validate-db]))
+            [validate-db]
+            [logseq.db.common.sqlite-cli :as sqlite-cli]))
 
 (defn- resolve-path
   "If relative path, resolve with $ORIGINAL_PWD"
@@ -21,17 +20,6 @@
     path
     (node-path/join (or js/process.env.ORIGINAL_PWD ".") path)))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (def spec
   "Options spec"
   {:help {:alias :h
@@ -48,11 +36,13 @@
             (println (str "Usage: $0 GRAPH-NAME EDN-PATH [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
             (js/process.exit 1))
-        [dir db-name] (get-dir-and-db-name graph-dir)
+        init-conn-args (conj (sqlite-cli/->open-db-args graph-dir))
         sqlite-build-edn (merge (if (:import options) {} {:auto-create-ontology? true})
                                 (-> (resolve-path edn-path) fs/readFileSync str edn/read-string))
-        graph-exists? (fs/existsSync (node-path/join dir db-name))
-        conn (outliner-cli/init-conn dir db-name {:classpath (cp/get-classpath) :import-type :cli/create-graph})
+        graph-exists? (fs/existsSync (apply node-path/join init-conn-args))
+        db-name (if (= 1 (count init-conn-args)) (first init-conn-args) (second init-conn-args))
+        conn (apply outliner-cli/init-conn
+                    (conj init-conn-args {:classpath (cp/get-classpath) :import-type :cli/create-graph}))
         {:keys [init-tx block-props-tx misc-tx] :as _txs}
         (if (:import options)
           (sqlite-export/build-import sqlite-build-edn @conn {})

+ 3 - 17
deps/db/script/diff_graphs.cljs

@@ -1,27 +1,13 @@
 (ns diff-graphs
   "A script that diffs two DB graphs through their sqlite.build EDN"
-  (:require ["os" :as os]
-            ["path" :as node-path]
-            [babashka.cli :as cli]
+  (:require [babashka.cli :as cli]
             [clojure.data :as data]
             [clojure.pprint :as pprint]
-            [clojure.string :as string]
             [logseq.common.config :as common-config]
             [logseq.db.common.sqlite-cli :as sqlite-cli]
             [logseq.db.sqlite.export :as sqlite-export]
             [nbb.core :as nbb]))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (def spec
   "Options spec"
   {:help {:alias :h
@@ -43,8 +29,8 @@
             (println (str "Usage: $0 GRAPH-NAME GRAPH-NAME2 [& ARGS] [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
             (js/process.exit 1))
-        conn (apply sqlite-cli/open-db! (get-dir-and-db-name graph-dir))
-        conn2 (apply sqlite-cli/open-db! (get-dir-and-db-name graph-dir2))
+        conn (apply sqlite-cli/open-db! (sqlite-cli/->open-db-args graph-dir))
+        conn2 (apply sqlite-cli/open-db! (sqlite-cli/->open-db-args graph-dir2))
         export-options (select-keys options [:include-timestamps? :exclude-namespaces :exclude-built-in-pages?])
         export-map (sqlite-export/build-export @conn {:export-type :graph :graph-options export-options})
         export-map2 (sqlite-export/build-export @conn2 {:export-type :graph :graph-options export-options})

+ 3 - 10
deps/db/script/dump_datoms.cljs

@@ -3,28 +3,21 @@
 
      $ yarn -s nbb-logseq script/dump_datoms.cljs db-name datoms.edn"
     (:require ["fs" :as fs]
-              ["os" :as os]
-              ["path" :as path]
+              ["path" :as node-path]
               [clojure.pprint :as pprint]
               [datascript.core :as d]
               [logseq.db.common.sqlite-cli :as sqlite-cli]
               [nbb.core :as nbb]))
 
-(defn read-graph
-  "The db graph bare version of gp-cli/parse-graph"
-  [graph-name]
-  (let [graphs-dir (path/join (os/homedir) "logseq/graphs")]
-    (sqlite-cli/open-db! graphs-dir graph-name)))
-
 (defn -main [args]
   (when (< (count args) 2)
     (println "Usage: $0 GRAPH FILE")
     (js/process.exit 1))
   (let [[graph-name file*] args
-        conn (read-graph graph-name)
+        conn (apply sqlite-cli/open-db! (sqlite-cli/->open-db-args graph-name))
         datoms (mapv #(vec %) (d/datoms @conn :eavt))
         parent-dir (or js/process.env.ORIGINAL_PWD ".")
-        file (path/join parent-dir file*)]
+        file (node-path/join parent-dir file*)]
     (println "Writing" (count datoms) "datoms to" file)
     (fs/writeFileSync file (with-out-str (pprint/pprint datoms)))))
 

+ 1 - 16
deps/db/script/export_graph.cljs

@@ -1,12 +1,9 @@
 (ns export-graph
   "A script that exports a graph to 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.pprint :as pprint]
-            [clojure.string :as string]
             [logseq.db.common.sqlite-cli :as sqlite-cli]
             [logseq.db.sqlite.export :as sqlite-export]
             [nbb.core :as nbb]))
@@ -18,17 +15,6 @@
     path
     (node-path/join (or js/process.env.ORIGINAL_PWD ".") path)))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (def spec
   "Options spec"
   {:help {:alias :h
@@ -54,8 +40,7 @@
             (println (str "Usage: $0 GRAPH-NAME [& ARGS] [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
             (js/process.exit 1))
-        [dir db-name] (get-dir-and-db-name graph-dir)
-        conn (sqlite-cli/open-db! dir db-name)
+        conn (apply sqlite-cli/open-db! (sqlite-cli/->open-db-args graph-dir))
         export-options (dissoc options :file)
         export-map (sqlite-export/build-export @conn {:export-type :graph :graph-options export-options})]
     (if (:file options)

+ 3 - 18
deps/db/script/query.cljs

@@ -3,15 +3,12 @@
 
   $ yarn -s nbb-logseq script/query.cljs db-name '[:find (pull ?b [:block/name :block/title]) :where [?b :block/created-at]]'"
   (:require ["child_process" :as child-process]
-            ["os" :as os]
-            ["path" :as node-path]
             [babashka.cli :as cli]
             [clojure.edn :as edn]
             [clojure.pprint :as pprint]
-            [clojure.string :as string]
             [datascript.core :as d]
-            [logseq.db.frontend.rules :as rules]
             [logseq.db.common.sqlite-cli :as sqlite-cli]
+            [logseq.db.frontend.rules :as rules]
             [nbb.core :as nbb]))
 
 (defn- sh
@@ -22,17 +19,6 @@
                            (clj->js (rest cmd))
                            (clj->js (merge {:stdio "inherit"} opts))))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (def spec
   "Options spec"
   {:help {:alias :h
@@ -48,9 +34,8 @@
             :coerce []
             :desc "Lookup entities instead of query"}})
 
-(defn query-graph [graph-dir args'' options]
-  (let [[dir db-name] (get-dir-and-db-name graph-dir)
-        conn (sqlite-cli/open-db! dir db-name)
+(defn query-graph [graph args'' options]
+  (let [conn (apply sqlite-cli/open-db! (sqlite-cli/->open-db-args graph))
         results (if (:entity options)
                   (map #(when-let [ent (d/entity @conn
                                                  (if (string? %) (edn/read-string %) %))]

+ 4 - 17
deps/db/script/validate_db.cljs

@@ -1,11 +1,8 @@
 (ns validate-db
   "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"
-  (:require ["os" :as os]
-            ["path" :as node-path]
-            [babashka.cli :as cli]
+  (:require [babashka.cli :as cli]
             [cljs.pprint :as pprint]
-            [clojure.string :as string]
             [datascript.core :as d]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.validate :as db-validate]
@@ -50,17 +47,6 @@
         (js/process.exit 1))
       (println "Valid!"))))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (def spec
   "Options spec"
   {:help {:alias :h
@@ -86,8 +72,9 @@
     (validate-db* db ent-maps options)))
 
 (defn- validate-graph [graph-dir options]
-  (let [[dir db-name] (get-dir-and-db-name graph-dir)
-        conn (try (sqlite-cli/open-db! dir db-name)
+  (let [open-db-args (sqlite-cli/->open-db-args graph-dir)
+        db-name (if (= 1 (count open-db-args)) (first open-db-args) (second open-db-args))
+        conn (try (apply sqlite-cli/open-db! open-db-args)
                   (catch :default e
                     (println "Error: For graph" (str (pr-str graph-dir) ":") (str e))
                     (js/process.exit 1)))]

+ 31 - 10
deps/db/src/logseq/db/common/sqlite_cli.cljs

@@ -2,8 +2,10 @@
   "Primary ns to interact with DB files for DB and file graphs with node.js based CLIs"
   (:require ["better-sqlite3" :as sqlite3]
             ["fs" :as fs]
+            ["os" :as os]
             ["path" :as node-path]
             [cljs-bean.core :as bean]
+            [clojure.string :as string]
             ;; FIXME: datascript.core has to come before datascript.storage or else nbb fails
             [datascript.core]
             [datascript.storage :refer [IStorage]]
@@ -86,14 +88,33 @@
   "For a given database name, opens a sqlite db connection for it, creates
   needed sqlite tables if not created and returns a datascript connection that's
   connected to the sqlite db"
-  [graphs-dir db-name]
-  (let [[_db-sanitized-name db-full-path] (common-sqlite/get-db-full-path graphs-dir db-name)
-        db (new sqlite db-full-path nil)
+  ([db-full-path]
+   (open-db! nil db-full-path))
+  ([graphs-dir db-name]
+   (let [[base-name db-full-path]
+         (if (nil? graphs-dir)
+           [(node-path/basename db-name) db-name]
+           [db-name (second (common-sqlite/get-db-full-path graphs-dir db-name))])
+         db (new sqlite db-full-path nil)
         ;; For both desktop and CLI, only file graphs have db-name that indicate their db type
-        schema (if (common-sqlite/local-file-based-graph? db-name)
-                 file-schema/schema
-                 db-schema/schema)]
-    (common-sqlite/create-kvs-table! db)
-    (let [storage (new-sqlite-storage db)
-          conn (common-sqlite/get-storage-conn storage schema)]
-      conn)))
+         schema (if (common-sqlite/local-file-based-graph? base-name)
+                  file-schema/schema
+                  db-schema/schema)]
+     (common-sqlite/create-kvs-table! db)
+     (let [storage (new-sqlite-storage db)
+           conn (common-sqlite/get-storage-conn storage schema)]
+       conn))))
+
+(defn ->open-db-args
+  "Creates args for open-db from a graph arg. Works for relative and absolute paths and
+   defaults to ~/logseq/graphs/ when no '/' present in name"
+  [graph-dir-or-path]
+  ;; Pass full path directly to allow for paths that don't have standard graph naming convention
+  (if (node-path/isAbsolute graph-dir-or-path)
+    [graph-dir-or-path]
+    (if (string/includes? graph-dir-or-path "/")
+      (let [resolve-path' #(if (node-path/isAbsolute %) %
+                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
+                               (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
+        ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir-or-path)))
+      [(node-path/join (os/homedir) "logseq" "graphs") graph-dir-or-path])))

+ 8 - 19
deps/graph-parser/script/db_import.cljs

@@ -4,9 +4,7 @@
    the import process"
   (:require ["fs" :as fs]
             ["fs/promises" :as fsp]
-            ["os" :as os]
             ["path" :as node-path]
-            #_:clj-kondo/ignore
             [babashka.cli :as cli]
             [cljs.pprint :as pprint]
             [clojure.set :as set]
@@ -18,7 +16,8 @@
             [logseq.outliner.pipeline :as outliner-pipeline]
             [nbb.classpath :as cp]
             [nbb.core :as nbb]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [logseq.db.common.sqlite-cli :as sqlite-cli]))
 
 (def tx-queue (atom cljs.core/PersistentQueue.EMPTY))
 (def original-transact! d/transact!)
@@ -125,17 +124,6 @@
       (p/let [_ (gp-exporter/export-doc-files conn files' <read-file doc-options)]
         {:import-state (:import-state doc-options)}))))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (def spec
   "Options spec"
   {:help {:alias :h
@@ -171,9 +159,11 @@
             (println (str "Usage: $0 FILE-GRAPH DB-GRAPH [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
             (js/process.exit 1))
-        [dir db-name] (get-dir-and-db-name db-graph-dir)
+        init-conn-args (sqlite-cli/->open-db-args db-graph-dir)
+        db-name (if (= 1 (count init-conn-args)) (first init-conn-args) (second init-conn-args))
+        db-dir (if (= 1 (count init-conn-args)) (node-path/dirname (first init-conn-args)) (second init-conn-args))
         file-graph' (resolve-path file-graph)
-        conn (outliner-cli/init-conn dir db-name {:classpath (cp/get-classpath)})
+        conn (apply outliner-cli/init-conn (conj init-conn-args {:classpath (cp/get-classpath)}))
         directory? (.isDirectory (fs/statSync file-graph'))
         user-options (cond-> (merge {:all-tags false} (dissoc options :verbose :files :help :continue))
                        ;; coerce option collection into strings
@@ -182,12 +172,11 @@
                        true
                        (set/rename-keys {:all-tags :convert-all-tags? :remove-inline-tags :remove-inline-tags?}))
         _ (when (:verbose options) (prn :options user-options))
-        options' (merge {:user-options user-options
-                         :graph-name db-name}
+        options' (merge {:user-options user-options}
                         (select-keys options [:files :verbose :continue :debug]))]
     (p/let [{:keys [import-state]}
             (if directory?
-              (import-file-graph-to-db file-graph' (node-path/join dir db-name) conn options')
+              (import-file-graph-to-db file-graph' db-dir conn options')
               (import-files-to-db file-graph' conn options'))]
 
       (when-let [ignored-props (seq @(:ignored-properties import-state))]

+ 4 - 17
deps/outliner/script/transact.cljs

@@ -1,34 +1,21 @@
 (ns transact
   "This script generically runs transactions against the queried blocks"
-  (:require ["os" :as os]
-            ["path" :as node-path]
-            [clojure.edn :as edn]
-            [clojure.string :as string]
+  (:require [clojure.edn :as edn]
             [datascript.core :as d]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.common.sqlite-cli :as sqlite-cli]
             [logseq.outliner.db-pipeline :as db-pipeline]
             [nbb.core :as nbb]))
 
-(defn- get-dir-and-db-name
-  "Gets dir and db name for use with open-db! Works for relative and absolute paths and
-   defaults to ~/logseq/graphs/ when no '/' present in name"
-  [graph-dir]
-  (if (string/includes? graph-dir "/")
-    (let [resolve-path' #(if (node-path/isAbsolute %) %
-                             ;; $ORIGINAL_PWD used by bb tasks to correct current dir
-                             (node-path/join (or js/process.env.ORIGINAL_PWD ".") %))]
-      ((juxt node-path/dirname node-path/basename) (resolve-path' graph-dir)))
-    [(node-path/join (os/homedir) "logseq" "graphs") graph-dir]))
-
 (defn -main [args]
   (when (< (count args) 3)
     (println "Usage: $0 GRAPH-DIR QUERY TRANSACT-FN")
     (js/process.exit 1))
   (let [[graph-dir query* transact-fn*] args
         dry-run? (contains? (set args) "-n")
-        [dir db-name] (get-dir-and-db-name graph-dir)
-        conn (sqlite-cli/open-db! dir db-name)
+        open-db-args (sqlite-cli/->open-db-args graph-dir)
+        db-name (if (= 1 (count open-db-args)) (first open-db-args) (second open-db-args))
+        conn (apply sqlite-cli/open-db! open-db-args)
         ;; find blocks to update
         query (into (edn/read-string query*) [:in '$ '%]) ;; assumes no :in are in queries
         transact-fn (edn/read-string transact-fn*)

+ 9 - 4
deps/outliner/src/logseq/outliner/cli.cljs

@@ -46,10 +46,15 @@
    * :additional-config - Additional config map to merge into repo config.edn
    * :classpath - A java classpath string i.e. paths delimited by ':'. Used to find default config.edn
      that comes with Logseq"
-  [dir db-name & [opts]]
-  (fs/mkdirSync (node-path/join dir db-name) #js {:recursive true})
-  ;; Same order as frontend.db.conn/start!
-  (let [conn (sqlite-cli/open-db! dir db-name)]
+  [& args*]
+  (let [[args opts] (if (map? (last args*))
+                      [(butlast args*) (last args*)]
+                      [args* {}])
+        ;; Only mkdir when a dir and db-name are passed
+        _ (when (= 2 (count args))
+            (fs/mkdirSync (apply node-path/join args) #js {:recursive true}))
+        ;; Same order as frontend.db.conn/start!
+        conn (apply sqlite-cli/open-db! args)]
     (db-pipeline/add-listener conn)
     (setup-init-data conn opts)
     conn))

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

@@ -4,7 +4,6 @@
    NOTE: This script is also used in CI to confirm graph creation works"
   (:require ["fs" :as fs]
             ["fs-extra$default" :as fse]
-            ["os" :as os]
             ["path" :as node-path]
             [babashka.cli :as cli]
             [cljs.pprint :as pprint]
@@ -18,7 +17,8 @@
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.outliner.cli :as outliner-cli]
             [nbb.classpath :as cp]
-            [nbb.core :as nbb]))
+            [nbb.core :as nbb]
+            [logseq.db.common.sqlite-cli :as sqlite-cli]))
 
 (defn- date-journal-title [date]
   (date-time-util/int->journal-title (date-time-util/date->int date) "MMM do, yyyy"))
@@ -210,14 +210,15 @@
             (println (str "Usage: $0 GRAPH-NAME [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
             (js/process.exit 1))
-        [dir db-name] (if (string/includes? graph-dir "/")
-                        ((juxt node-path/dirname node-path/basename) graph-dir)
-                        [(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
-        db-path (node-path/join dir db-name "db.sqlite")
-        _ (when (fs/existsSync db-path)
+        init-conn-args (sqlite-cli/->open-db-args graph-dir)
+        db-name (if (= 1 (count init-conn-args)) (first init-conn-args) (second init-conn-args))
+        db-path (apply node-path/join init-conn-args)
+        ;; Only remove the directory if the directory is being overwritten
+        _ (when (and (= 2 (count init-conn-args)) (fs/existsSync db-path))
             (fse/removeSync db-path))
-        conn (outliner-cli/init-conn dir db-name {:additional-config (:config options)
-                                                  :classpath (cp/get-classpath)})
+        conn (apply outliner-cli/init-conn
+                    (conj init-conn-args {:additional-config (:config options)
+                                          :classpath (cp/get-classpath)}))
         init-data (create-init-data)
         _ (when (:file options) (fs/writeFileSync (:file options) (with-out-str (pprint/pprint init-data))))
         {:keys [init-tx block-props-tx]} (outliner-cli/build-blocks-tx init-data)
@@ -226,7 +227,6 @@
     (when (seq conflicting-names)
       (println "Error: Following names conflict -" (string/join "," conflicting-names))
       (js/process.exit 1))
-    (println "DB dir: " (node-path/join dir db-name))
     (println "Generating" (count (filter :block/name init-tx)) "pages and"
              (count (filter :block/title init-tx)) "blocks ...")
     (d/transact! conn init-tx)

+ 7 - 8
scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs

@@ -11,8 +11,6 @@
        type logseq doesnt' support yet
      * schema.org assumes no cardinality. For now, only :node properties are given a :cardinality :many"
   (:require ["fs" :as fs]
-            ["os" :as os]
-            ["path" :as node-path]
             [babashka.cli :as cli]
             [clojure.edn :as edn]
             [clojure.set :as set]
@@ -23,7 +21,8 @@
             [logseq.db.frontend.property :as db-property]
             [logseq.outliner.cli :as outliner-cli]
             [nbb.classpath :as cp]
-            [nbb.core :as nbb]))
+            [nbb.core :as nbb]
+            [logseq.db.common.sqlite-cli :as sqlite-cli]))
 
 (defn- get-comment-string
   [rdfs-comment renamed-pages]
@@ -406,11 +405,11 @@
             (println (str "Usage: $0 GRAPH-NAME [OPTIONS]\nOptions:\n"
                           (cli/format-opts {:spec spec})))
             (js/process.exit 1))
-        [dir db-name] (if (string/includes? graph-dir "/")
-                        ((juxt node-path/dirname node-path/basename) graph-dir)
-                        [(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
-        conn (outliner-cli/init-conn dir db-name {:additional-config (:config options)
-                                                  :classpath (cp/get-classpath)})
+        init-conn-args (sqlite-cli/->open-db-args graph-dir)
+        db-name (if (= 1 (count init-conn-args)) (first init-conn-args) (second init-conn-args))
+        conn (apply outliner-cli/init-conn
+                    (conj init-conn-args {:additional-config (:config options)
+                                          :classpath (cp/get-classpath)}))
         init-data (create-init-data (d/q '[:find [?name ...] :where [?b :block/name ?name]] @conn)
                                     options)
         {:keys [init-tx block-props-tx]} (outliner-cli/build-blocks-tx init-data)]

+ 58 - 40
src/main/frontend/components/block.cljs

@@ -3049,6 +3049,24 @@
                                                                                            :container-id (:container-id config)}))}})])
      (block-content config block edit-input-id block-id *show-query?))))
 
+(rum/defc block-content-wrapper
+  [block editor-box edit? type-block-editor? editor-cp content-cp]
+  (let [[editing? set-editing!] (hooks/use-state edit?)
+        editing-block (state/get-edit-block)
+        next-edit-block-id (:db/id editing-block)]
+    (hooks/use-effect!
+     (fn []
+       (if (and editing? (false? edit?) next-edit-block-id
+                (not= next-edit-block-id (:db/id block)))
+         ;; editing another block, need to wait another block's editor to be ready
+         (util/schedule #(set-editing! false))
+         (set-editing! edit?))
+       (fn []))
+     [edit?])
+    (if (and editor-box editing? (not type-block-editor?))
+      editor-cp
+      content-cp)))
+
 (rum/defcs ^:large-vars/cleanup-todo block-content-or-editor < rum/reactive
   [state config {:block/keys [uuid] :as block} {:keys [edit-input-id block-id edit? hide-block-refs-count? refs-count *hide-block-refs? *show-query?]}]
   (let [format (if (config/db-based-graph? (state/get-current-repo))
@@ -3074,46 +3092,46 @@
      (when (and db-based? (not table?)) (block-positioned-properties config block :block-left))
      [:div.block-content-or-editor-inner
       [:div.block-row.flex.flex-1.flex-row.gap-1.items-center
-       (if (and editor-box edit? (not type-block-editor?))
-         [:div.editor-wrapper.flex.flex-1.w-full
-          {:id editor-id
-           :class (util/classnames [{:opacity-50 (boolean (or (ldb/built-in? block) (ldb/journal? block)))}])}
-          (ui/catch-error
-           (ui/block-error "Something wrong in the editor" {})
-           (editor-box {:block block
-                        :block-id uuid
-                        :block-parent-id block-id
-                        :format format}
-                       edit-input-id
-                       config))]
-         [:div.flex.flex-1.w-full.block-content-wrapper
-          {:style {:display "flex"}}
-          (when-let [actions-cp (:page-title-actions-cp config)]
-            (actions-cp block))
-          (block-content-with-error config block edit-input-id block-id *show-query? editor-box)
-
-          (when (and (not hide-block-refs-count?)
-                     (not named?)
-                     (not (:table-block-title? config)))
-            [:div.flex.flex-row.items-center
-             (when (and (:embed? config)
-                        (:embed-parent config))
-               [:a.opacity-70.hover:opacity-100.svg-small.inline
-                {:on-pointer-down (fn [e]
-                                    (util/stop e)
-                                    (when-let [block (:embed-parent config)]
-                                      (editor-handler/edit-block! block :max)))}
-                svg/edit])
-
-             (when block-reference-only?
-               [:a.opacity-70.hover:opacity-100.svg-small.inline
-                {:on-pointer-down (fn [e]
-                                    (util/stop e)
-                                    (editor-handler/edit-block! block :max))}
-                svg/edit])])
-
-          (when-not (or (:table? config) (:property? config) (:page-title? config))
-            (block-refs-count block refs-count *hide-block-refs?))])
+       (let [content-cp [:div.flex.flex-1.w-full.block-content-wrapper
+                         {:style {:display "flex"}}
+                         (when-let [actions-cp (:page-title-actions-cp config)]
+                           (actions-cp block))
+                         (block-content-with-error config block edit-input-id block-id *show-query? editor-box)
+
+                         (when (and (not hide-block-refs-count?)
+                                    (not named?)
+                                    (not (:table-block-title? config)))
+                           [:div.flex.flex-row.items-center
+                            (when (and (:embed? config)
+                                       (:embed-parent config))
+                              [:a.opacity-70.hover:opacity-100.svg-small.inline
+                               {:on-pointer-down (fn [e]
+                                                   (util/stop e)
+                                                   (when-let [block (:embed-parent config)]
+                                                     (editor-handler/edit-block! block :max)))}
+                               svg/edit])
+
+                            (when block-reference-only?
+                              [:a.opacity-70.hover:opacity-100.svg-small.inline
+                               {:on-pointer-down (fn [e]
+                                                   (util/stop e)
+                                                   (editor-handler/edit-block! block :max))}
+                               svg/edit])])
+
+                         (when-not (or (:table? config) (:property? config) (:page-title? config))
+                           (block-refs-count block refs-count *hide-block-refs?))]
+             editor-cp [:div.editor-wrapper.flex.flex-1.w-full
+                        {:id editor-id
+                         :class (util/classnames [{:opacity-50 (boolean (or (ldb/built-in? block) (ldb/journal? block)))}])}
+                        (ui/catch-error
+                         (ui/block-error "Something wrong in the editor" {})
+                         (editor-box {:block block
+                                      :block-id uuid
+                                      :block-parent-id block-id
+                                      :format format}
+                                     edit-input-id
+                                     config))]]
+         (block-content-wrapper block editor-box edit? type-block-editor? editor-cp content-cp))
 
        (when-not (:table-block-title? config)
          [:div.ls-block-right.flex.flex-row.items-center.self-start.gap-1

+ 2 - 0
src/main/frontend/components/editor.cljs

@@ -800,6 +800,8 @@
                                       (when (= (util/ekey e) "Escape")
                                         (editor-on-hide state :esc e))))
                :auto-focus true
+               :autocapitalize "off"
+               :autocorrect "off"
                :class heading-class}
                (some? parent-block)
                (assoc :parentblockid (str (:block/uuid parent-block)))

+ 1 - 1
src/main/frontend/state.cljs

@@ -1991,11 +1991,11 @@ Similar to re-frame subscriptions"
         (assert (and container-id (:block/uuid block))
                 "container-id or block uuid is missing")
         (set-state! :editor/block-refs #{})
+        (set-state! :editor/block block)
         (if property-block
           (set-editing-block-id! [container-id (:block/uuid property-block) (:block/uuid block)])
           (set-editing-block-id! [container-id (:block/uuid block)]))
         (set-state! :editor/container-id container-id)
-        (set-state! :editor/block block)
         (set-state! :editor/content content :path-in-sub-atom (:block/uuid block))
         (set-state! :editor/last-key-code nil)
         (set-state! :editor/set-timestamp-block nil)