Merge branch 'master' into enhance/mobile-silk

This commit is contained in:
charlie
2025-07-12 21:11:21 +08:00
20 changed files with 158 additions and 87 deletions

View File

@@ -68,11 +68,16 @@
## 🚀 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/master/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/master/db-version-changes.md) to get an overview of changes with the DB 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/master/db-version.md) to get an overview of the main features for DB graphs. If you are an existing user, [see changes with the DB version](https://github.com/logseq/docs/blob/master/db-version-changes.md). The DB version has its own new mobile app! To participate in the mobile app alpha, [please complete this brief form](https://forms.gle/nfefJv51jUuULbFB9). The DB version also has a new sync approach, RTC (Real Time Collaboration)! You can use it to sync graphs between multiple devices or collaborate with others. To participate in the RTC alpha, [please fill out this form](https://forms.gle/YSyF4WfKPSDuwyjH6).
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 thats not crucial for you. **Data loss is possible**, which is why we recommend [automated backups](https://github.com/logseq/docs/blob/master/db-version.md#automated-backup) or making [regular SQLite DB backups](https://github.com/logseq/docs/blob/master/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.
The DB version is in beta status while the new mobile app and RTC is in alpha. This means that **data loss is possible** so we recommend [automated backups](https://github.com/logseq/docs/blob/master/db-version.md#automated-backup) or [regular SQLite DB backups](https://github.com/logseq/docs/blob/master/db-version.md#graph-export). When using DB graphs, we recommend you create a dedicated test graph and choose one project thats not crucial for you. When using file graphs, **data corruption is possible** as some file content can be duplicated. We only recommend using it with file graphs if you make regular backups with git.
To try the latest web version, go to https://test.logseq.com/. For DB version issues, please report them to https://github.com/logseq/db-test/issues. To try the latest desktop version, go to https://github.com/logseq/logseq/actions/workflows/build-desktop-release.yml and click on the latest release. Scroll to the bottom and under the `Artifacts` section download the artifact for your operating system.
To get started with the DB version:
* To try the latest web version, go to https://test.logseq.com/.
* To try the latest desktop version, login to Github and go to https://github.com/logseq/logseq/actions/workflows/build-desktop-release.yml and click on the latest release. Scroll to the bottom and under the `Artifacts` section download the artifact for your operating system.
* To report bugs, please file them at https://github.com/logseq/db-test/issues.
* For feature or enhancement requests, please file them on Discord on the `#db-feedback` channel.
* For discussion, see the `#db-chat` channel in Discord.
## 🤔 Why Logseq?

View File

@@ -110,7 +110,8 @@
;; Use file-entity-util and entity-util when in a single graph context
"ldb/whiteboard\\?" "ldb/journal\\?" "ldb/page\\?"]
res (grep-many multi-graph-fns (into file-graph-paths db-graph-paths))]
(when-not (and (= 1 (:exit res)) (= "" (:out res)))
(when-not (or (and (= 1 (:exit res)) (= "" (:out res)))
(and (zero? (:exit res)) (string/starts-with? (:out res) "src/main/mobile/components/app.cljs:")))
(println "The following files should not have fns meant to be used in multi-graph contexts:")
(println (:out res))
(System/exit 1))))

View File

@@ -1157,23 +1157,24 @@
(excalidraw uuid-or-title (:block/uuid config))]
:else
[:span.page-reference
{:data-ref (str uuid-or-title)}
(when brackets?
[:span.text-gray-500.bracket page-ref/left-brackets])
(when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
[:div.inline-block
{:style {:margin-right 1
:margin-top -2
:vertical-align "middle"}
:on-pointer-down (fn [e]
(util/stop e))}
(block-positioned-properties config block :block-left)])
(page-cp config' (if (uuid? uuid-or-title)
{:block/uuid uuid-or-title}
{:block/name uuid-or-title}))
(when brackets?
[:span.text-gray-500.bracket page-ref/right-brackets])])))))))
(let [blank-title? (string/blank? (:block/title block))]
[:span.page-reference
{:data-ref (str uuid-or-title)}
(when (and brackets? (not blank-title?))
[:span.text-gray-500.bracket page-ref/left-brackets])
(when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
[:div.inline-block
{:style {:margin-right 1
:margin-top -2
:vertical-align "middle"}
:on-pointer-down (fn [e]
(util/stop e))}
(block-positioned-properties config block :block-left)])
(page-cp config' (if (uuid? uuid-or-title)
{:block/uuid uuid-or-title}
{:block/name uuid-or-title}))
(when (and brackets? (not blank-title?))
[:span.text-gray-500.bracket page-ref/right-brackets])]))))))))
(defn- latex-environment-content
[name option content]
@@ -2844,10 +2845,7 @@
(:block/tags block)
(remove (fn [t]
(or (ldb/inline-tag? (:block/raw-title block) t)
(if (contains? t :logseq.property.class/hide-from-node)
(:logseq.property.class/hide-from-node t)
;; Mobile app hides by default while everything else doesn't
(if (util/capacitor-new?) true false))
(:logseq.property.class/hide-from-node t)
(contains? hidden-internal-tags (:db/ident t))
(and (util/mobile?) (= (:db/ident t) :logseq.class/Task))))))
popup-opts {:align :end

View File

@@ -493,7 +493,7 @@
[:div.ls-block.property-value-container.flex.flex-row.gap-1.items-start
(when-not (or block? (and property-desc (:class-schema? opts)))
[:div.flex.h-6.items-center
[:div.flex.items-center {:style {:height 28}}
[:div {:class "pl-1.5 -mr-[3px] opacity-60"}
[:span.bullet-container [:span.bullet]]]])
[:div.flex.flex-1

View File

@@ -19,14 +19,18 @@
:will-unmount (fn [state]
(state/clear-edit!)
(state/clear-selection!)
state)}
state)
:did-mount (fn [state]
(when-not (util/mobile?)
(editor-handler/quick-add-open-last-block!))
state)}
[]
(when (model/get-journal-page (date/today))
(when-let [add-page (ldb/get-built-in-page (db/get-db) common-config/quick-add-page-name)]
(let [mobile? (util/mobile?)
add-button [:div
(shui/button
{:variant (if mobile? :default :outline)
{:variant :default
:size :sm
:on-click (fn [_e]
(editor-handler/quick-add-blocks!))}
@@ -35,13 +39,11 @@
[:div.ls-quick-add.flex.flex-1.flex-col.w-full.gap-4
[:div.border-b.pb-4.flex.flex-row.justify-between.gap-4.items-center
[:div.font-medium
{:class (when-not mobile? "text-xs")}
"Quick add"]
(when mobile? add-button)]
[:div.content
{:class (if mobile?
"flex flex-1 flex-col w-full"
"block -ml-6")}
(page/page-blocks-cp add-page {})
(when-not mobile?
add-button)]]))))
(page/page-blocks-cp add-page {})]
(when-not mobile? add-button)]))))

View File

@@ -126,7 +126,9 @@
(repo-handler/remove-repo! repo)
(state/pub-event! [:graph/unlinked repo (state/get-current-repo)]))))))}
"Delete local graph"))
(when (and db-based? root (not remote?))
(when (and db-based? root
(not remote?)
(= url (state/get-current-repo)))
(shui/dropdown-menu-item
{:key "logseq-sync"
:class "use-logseq-sync-menu-item"
@@ -141,11 +143,14 @@
(shui/popup-show! nil
(fn []
(rtc-indicator/uploading-logs))
{:id :rtc-graph-upload-log})
(rtc-indicator/on-upload-finished-task
(fn []
(shui/popup-hide! :rtc-graph-upload-log)
(rtc-flows/trigger-rtc-start repo)))))))}
{:id :rtc-graph-upload-log}))
(rtc-indicator/on-upload-finished-task
(fn []
(when (util/mobile?) (shui/popup-hide! :rtc-graph-upload-log))
(p/do!
(rtc-flows/trigger-rtc-start repo)
(rtc-handler/<get-remote-graphs)))))))}
"Use Logseq sync (Beta testing)"))
(when (and remote? (or (and db-based? manager?) (not db-based?)))
(shui/dropdown-menu-item
@@ -165,9 +170,12 @@
(fn [graph-uuid _graph-schema-version]
(async-util/c->p (file-sync/<delete-graph graph-uuid))))]
(state/set-state! [:file-sync/remote-graphs :loading] true)
(when (= (state/get-current-repo) repo)
(state/<invoke-db-worker :thread-api/rtc-stop))
(p/do! (<delete-graph GraphUUID GraphSchemaVersion)
(state/delete-remote-graph! repo)
(state/set-state! [:file-sync/remote-graphs :loading] false)))))))))}
(state/set-state! [:file-sync/remote-graphs :loading] false)
(rtc-handler/<get-remote-graphs)))))))))}
"Delete from server")))))]]]))
(rum/defc repos-cp < rum/reactive
@@ -228,9 +236,7 @@
:on-click (fn []
(when-not (util/capacitor-new?)
(file-sync/load-session-graphs))
(p/do!
(rtc-handler/<get-remote-graphs)
(repo-handler/refresh-repos!))))]]
(rtc-handler/<get-remote-graphs)))]]
(repos-inner remote-graphs)])]]))
(defn- repos-dropdown-links [repos current-repo downloading-graph-id & {:as opts}]
@@ -464,13 +470,13 @@
(p/do
(state/set-state! :rtc/uploading? true)
(rtc-handler/<rtc-create-graph! repo)
(state/set-state! :rtc/uploading? false)
(rtc-flows/trigger-rtc-start repo))
(rtc-flows/trigger-rtc-start repo)
(rtc-handler/<get-remote-graphs))
(p/catch (fn [error]
(reset! *creating-db? false)
(state/set-state! :rtc/uploading? false)
(log/error :create-db-failed error)))))
(reset! *creating-db? false)
(log/error :create-db-failed error)))
(p/finally (fn []
(state/set-state! :rtc/uploading? false)
(reset! *creating-db? false)))))
(shui/dialog-close!))))))
submit! (fn [^js e click?]
(when-let [value (and (or click? (= (gobj/get e "key") "Enter"))

View File

@@ -5,6 +5,7 @@
[frontend.config :as config]
[frontend.db :as db]
[frontend.flows :as flows]
[frontend.handler.db-based.rtc :as rtc-handler]
[frontend.handler.db-based.rtc-flows :as rtc-flows]
[frontend.state :as state]
[frontend.ui :as ui]
@@ -138,7 +139,15 @@
remote-tx (assoc :remote-tx remote-tx)
rtc-state (assoc :rtc-state rtc-state))
pprint/pprint
with-out-str)]])]))
with-out-str)]])
(when-not (= rtc-state :open)
[:div.mt-4
(shui/button {:variant :default
:size :sm
:on-click (fn []
(rtc-handler/<rtc-start! (state/get-current-repo)
{:stop-before-start? true}))}
"Start sync")])]))
(rum/defc indicator
[]

View File

@@ -5,6 +5,7 @@
[frontend.db :as db]
[frontend.handler.db-based.rtc-flows :as rtc-flows]
[frontend.handler.notification :as notification]
[frontend.handler.repo :as repo-handler]
[frontend.handler.user :as user-handler]
[frontend.state :as state]
[frontend.util :as util]
@@ -145,7 +146,8 @@
:GraphUUID (:graph-uuid graph)
:rtc-graph? true})
(dissoc graph :graph-uuid :graph-name)))))]
(state/set-state! :rtc/graphs result)))
(state/set-state! :rtc/graphs result)
(repo-handler/refresh-repos!)))
(defn <rtc-get-users-info
[]

View File

@@ -1475,7 +1475,10 @@
(for [[_index ^js file] (map-indexed vector files)]
;; WARN file name maybe fully qualified path when paste file
(p/let [file-name (node-path/basename (.-name file))
file-name-without-ext (db-asset/asset-name->title file-name)
file-name-without-ext* (db-asset/asset-name->title file-name)
file-name-without-ext (if (= file-name-without-ext* "image")
(date/get-date-time-string-2)
file-name-without-ext*)
checksum (assets-handler/get-file-checksum file)
existing-asset (db-async/<get-asset-with-checksum repo checksum)]
(if existing-asset
@@ -3966,3 +3969,10 @@
(if (shui-dialog/get-modal :ls-dialog-quick-add)
(quick-add-blocks!)
(show-quick-add)))
(defn quick-add-open-last-block!
[]
(when-let [add-page (ldb/get-built-in-page (db/get-db) common-config/quick-add-page-name)]
(when (:block/_parent add-page)
(let [block (last (ldb/sort-by-order (:block/_parent add-page)))]
(edit-block! block :max {:container-id :unknown-container})))))

View File

@@ -376,6 +376,11 @@
(defmethod handle :rtc/log [[_ data]]
(state/set-state! :rtc/log data))
(defmethod handle :rtc/remote-graph-gone [_]
(p/do!
(notification/show! "This graph has been removed from Logseq Sync." :warning false)
(rtc-handler/<get-remote-graphs)))
(defmethod handle :rtc/download-remote-graph [[_ graph-name graph-uuid graph-schema-version]]
(assert (= (:major (db-schema/parse-schema-version db-schema/version))
(:major (db-schema/parse-schema-version graph-schema-version)))
@@ -392,6 +397,7 @@
(indicator/downloading-logs)])
{:id :download-rtc-graph}))
(rtc-handler/<rtc-download-graph! graph-name graph-uuid graph-schema-version 60000)
(rtc-handler/<get-remote-graphs)
(when (util/mobile?)
(shui/popup-hide! :download-rtc-graph)))
(p/catch (fn [e]

View File

@@ -203,7 +203,8 @@
{:id :page-histories :label "modal-page-histories"}))
(defmethod events/handle :file-sync/maybe-onboarding-show [[_ type]]
(file-sync/maybe-onboarding-show type))
(when-not util/web-platform?
(file-sync/maybe-onboarding-show type)))
(defmethod events/handle :file-sync/storage-exceed-limit [[_]]
(notification/show! "file sync storage exceed limit" :warning false)

View File

@@ -284,7 +284,7 @@
(defn rtc-group?
[]
(set/intersection (state/user-groups) #{"team" "rtc_2025_07_10"}))
(boolean (seq (set/intersection (state/user-groups) #{"team" "rtc_2025_07_10"}))))
(defn alpha-user?
[]

View File

@@ -55,6 +55,9 @@
(defmethod handle :notify-existing-file [_ _worker data]
(state/pub-event! [:graph/notify-existing-file data]))
(defmethod handle :remote-graph-gone []
(state/pub-event! [:rtc/remote-graph-gone]))
(defmethod handle :default [_ _worker data]
(prn :debug "Worker data not handled: " data))

View File

@@ -92,7 +92,6 @@
[repo graph-uuid]
{:pre [(some? graph-uuid)]}
(when-let [conn (worker-state/get-client-ops-conn repo)]
(assert (nil? (first (d/datoms @conn :avet :graph-uuid))))
(d/transact! conn [[:db/add "e" :graph-uuid graph-uuid]])))
(defn get-graph-uuid
@@ -470,13 +469,3 @@
(m/ap
(let [_ (m/?> (c.m/throttle 100 db-updated-flow))]
(datom-count-fn @conn))))))
(defn reset-client-op-conn
[repo]
(when-let [conn (worker-state/get-client-ops-conn repo)]
(let [tx-data (->> (concat (d/datoms @conn :avet :graph-uuid)
(d/datoms @conn :avet :local-tx)
(d/datoms @conn :avet :aes-key-jwk)
(d/datoms @conn :avet :block/uuid))
(map (fn [datom] [:db/retractEntity (:e datom)])))]
(d/transact! conn tx-data))))

View File

@@ -9,6 +9,7 @@
[frontend.worker.rtc.branch-graph :as r.branch-graph]
[frontend.worker.rtc.client :as r.client]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.db :as rtc-db]
[frontend.worker.rtc.exception :as r.ex]
[frontend.worker.rtc.full-upload-download-graph :as r.upload-download]
[frontend.worker.rtc.log-and-state :as rtc-log-and-state]
@@ -370,7 +371,9 @@
(log/info :rtc-loop-task e)))
start-ex (m/? onstarted-task)]
(if (instance? ExceptionInfo start-ex)
start-ex
(do
(canceler)
start-ex)
(do (reset! *rtc-loop-metadata {:repo repo
:graph-uuid graph-uuid
:local-graph-schema-version schema-version
@@ -442,7 +445,14 @@
{:action "delete-graph"
:graph-uuid graph-uuid
:schema-version (str schema-version)}))]
(when ex-data (log/info ::delete-graph-failed {:graph-uuid graph-uuid :ex-data ex-data}))
(if ex-data
(log/info ::delete-graph-failed {:graph-uuid graph-uuid :ex-data ex-data})
;; Clean up rtc data in existing dbs so that the graph can be uploaded again
(when-let [repo (worker-state/get-current-repo)]
(when-let [conn (worker-state/get-datascript-conn repo)]
(let [graph-id (ldb/get-graph-rtc-uuid @conn)]
(when (= (str graph-id) (str graph-uuid))
(rtc-db/remove-rtc-data-in-conn! repo))))))
(boolean (nil? ex-data))))))
(defn new-task--get-users-info

View File

@@ -0,0 +1,26 @@
(ns frontend.worker.rtc.db
"rtc db ops"
(:require [datascript.core :as d]
[frontend.worker.state :as worker-state]))
(defn remove-rtc-data-from-local-db!
[repo]
(when-let [conn (worker-state/get-datascript-conn repo)]
(d/transact! conn [[:db/retractEntity :logseq.kv/graph-uuid]
[:db/retractEntity :logseq.kv/graph-local-tx]
[:db/retractEntity :logseq.kv/remote-schema-version]])))
(defn reset-client-op-conn
[repo]
(when-let [conn (worker-state/get-client-ops-conn repo)]
(let [tx-data (->> (concat (d/datoms @conn :avet :graph-uuid)
(d/datoms @conn :avet :local-tx)
(d/datoms @conn :avet :aes-key-jwk)
(d/datoms @conn :avet :block/uuid))
(map (fn [datom] [:db/retractEntity (:e datom)])))]
(d/transact! conn tx-data))))
(defn remove-rtc-data-in-conn!
[repo]
(remove-rtc-data-from-local-db! repo)
(reset-client-op-conn repo))

View File

@@ -11,6 +11,7 @@
[frontend.worker.db-metadata :as worker-db-metadata]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.const :as rtc-const]
[frontend.worker.rtc.db :as rtc-db]
[frontend.worker.rtc.log-and-state :as rtc-log-and-state]
[frontend.worker.rtc.ws-util :as ws-util]
[frontend.worker.shared-service :as shared-service]
@@ -120,14 +121,6 @@
(:db/ident block) (update :db/ident ldb/read-transit-str)
(:block/order block) (update :block/order ldb/read-transit-str)))))))
(defn- remove-rtc-data-in-conn!
[repo]
(client-op/reset-client-op-conn repo)
(when-let [conn (worker-state/get-datascript-conn repo)]
(d/transact! conn [[:db/retractEntity :logseq.kv/graph-uuid]
[:db/retractEntity :logseq.kv/graph-local-tx]
[:db/retractEntity :logseq.kv/remote-schema-version]])))
(defn new-task--upload-graph
[get-ws-create-task repo conn remote-graph-name major-schema-version]
(m/sp
@@ -477,7 +470,7 @@
(m/sp
(rtc-log-and-state/rtc-log :rtc.log/branch-graph {:sub-type :fetching-presigned-put-url
:message "fetching presigned put-url"})
(remove-rtc-data-in-conn! repo)
(rtc-db/remove-rtc-data-in-conn! repo)
(let [[{:keys [url key]} all-blocks-str]
(m/?
(m/join

View File

@@ -1,16 +1,21 @@
(ns frontend.worker.rtc.ws-util
"Add RTC related logic to the function based on ws."
(:require [cljs-http-missionary.client :as http]
[frontend.worker.rtc.db :as rtc-db]
[frontend.worker.rtc.exception :as r.ex]
[frontend.worker.rtc.malli-schema :as rtc-schema]
[frontend.worker.rtc.ws :as ws]
[frontend.worker.state :as worker-state]
[frontend.worker.util :as worker-util]
[goog.string :as gstring]
[logseq.graph-parser.utf8 :as utf8]
[missionary.core :as m]))
(defn- handle-remote-ex
[resp]
(when (= :graph-not-exist (:type (:ex-data resp)))
(rtc-db/remove-rtc-data-in-conn! (worker-state/get-current-repo))
(worker-util/post-message :remote-graph-gone []))
(if-let [e ({:graph-not-exist r.ex/ex-remote-graph-not-exist
:graph-not-ready r.ex/ex-remote-graph-not-ready
:bad-request-body r.ex/ex-bad-request-body

View File

@@ -4,11 +4,13 @@
[clojure.string :as string]
[frontend.components.journal :as journal]
[frontend.components.rtc.indicator :as rtc-indicator]
[frontend.config :as config]
[frontend.date :as date]
[frontend.db :as db]
[frontend.db.conn :as db-conn]
[frontend.handler.editor :as editor-handler]
[frontend.handler.page :as page-handler]
[frontend.handler.repo :as repo-handler]
[frontend.handler.user :as user-handler]
[frontend.mobile.util :as mobile-util]
[frontend.rum :as frum]
@@ -35,10 +37,17 @@
[promesa.core :as p]
[rum.core :as rum]))
(rum/defc app-graphs-select
(rum/defc app-graphs-select < rum/reactive
[]
(let [current-repo (state/get-current-repo)
graphs (state/get-repos)
graphs (->> (state/sub [:me :repos])
(util/distinct-by :url))
remote-graphs (state/sub :rtc/graphs)
graphs (->>
(if (seq remote-graphs)
(repo-handler/combine-local-&-remote-graphs graphs remote-graphs)
graphs)
(filter (fn [item] (config/db-based-graph? (:url item)))))
short-repo-name (if current-repo
(db-conn/get-short-repo-name current-repo)
"Select a Graph")]
@@ -49,9 +58,11 @@
:class "border-none w-full rounded-lg"
:on-click (fn []
(let [buttons (concat
(for [repo graphs]
{:text (some-> (:url repo) (string/replace #"^logseq_db_" ""))
:role (:url repo)})
(->>
(for [repo graphs]
{:text (some-> (:url repo) (string/replace #"^logseq_db_" ""))
:role (:url repo)})
(remove (fn [{:keys [text]}] (string/blank? text))))
[{:text "Add new graph"
:role "add-new-graph"}])]
(ui-component/open-modal! "Switch graph"

View File

@@ -1,11 +1,8 @@
(ns mobile.components.popup
"Mobile popup"
(:require [dommy.core :as dom]
[frontend.db :as db]
[frontend.handler.editor :as editor-handler]
[frontend.state :as state]
[logseq.common.config :as common-config]
[logseq.db :as ldb]
[logseq.shui.popup.core :as shui-popup]
[logseq.shui.ui :as shui]
[mobile.components.ui :as mobile-ui]
@@ -85,10 +82,7 @@
:initialBreakpoint initial-breakpoint
:onDidPresent (fn []
(when (= :ls-quick-add (:id opts))
(when-let [add-page (ldb/get-built-in-page (db/get-db) common-config/quick-add-page-name)]
(when (:block/_parent add-page)
(let [block (last (ldb/sort-by-order (:block/_parent add-page)))]
(editor-handler/edit-block! block :max {:container-id :unknown-container}))))))
(editor-handler/quick-add-open-last-block!)))
:breakpoints breakpoints
:onDidDismiss (fn []
(mobile-state/set-popup! nil)