瀏覽代碼

Merge branch 'master' into feat/capacitor-new

Tienson Qin 6 月之前
父節點
當前提交
c74abbb7a3
共有 39 個文件被更改,包括 326 次插入334 次删除
  1. 1 0
      .gitignore
  2. 1 1
      README.md
  3. 1 1
      deps.edn
  4. 1 1
      deps/db/deps.edn
  5. 8 7
      deps/db/src/logseq/db/sqlite/export.cljs
  6. 4 4
      deps/db/test/logseq/db/sqlite/export_test.cljs
  7. 1 1
      deps/outliner/deps.edn
  8. 4 2
      deps/publishing/src/logseq/publishing/html.cljs
  9. 0 1
      packages/tldraw/apps/tldraw-logseq/src/lib/logseq-context.ts
  10. 2 0
      public/index.html
  11. 44 30
      src/main/frontend/components/block.cljs
  12. 33 34
      src/main/frontend/components/cmdk/core.cljs
  13. 10 6
      src/main/frontend/components/editor.cljs
  14. 38 37
      src/main/frontend/components/page.cljs
  15. 1 0
      src/main/frontend/components/property.css
  16. 7 3
      src/main/frontend/components/property/value.cljs
  17. 1 1
      src/main/frontend/components/views.cljs
  18. 0 14
      src/main/frontend/db/model.cljs
  19. 0 1
      src/main/frontend/extensions/code.cljs
  20. 9 15
      src/main/frontend/extensions/pdf/assets.cljs
  21. 10 12
      src/main/frontend/extensions/pdf/core.cljs
  22. 0 3
      src/main/frontend/extensions/tldraw.cljs
  23. 4 6
      src/main/frontend/handler.cljs
  24. 5 1
      src/main/frontend/handler/assets.cljs
  25. 13 3
      src/main/frontend/handler/common/page.cljs
  26. 4 2
      src/main/frontend/handler/db_based/export.cljs
  27. 70 84
      src/main/frontend/handler/editor.cljs
  28. 1 4
      src/main/frontend/handler/editor/lifecycle.cljs
  29. 2 1
      src/main/frontend/handler/events/ui.cljs
  30. 1 3
      src/main/frontend/handler/jump.cljs
  31. 3 7
      src/main/frontend/handler/page.cljs
  32. 1 1
      src/main/frontend/mixins.cljs
  33. 16 16
      src/main/frontend/mobile/deeplink.cljs
  34. 2 0
      src/main/frontend/modules/instrumentation/sentry.cljs
  35. 10 12
      src/main/frontend/state.cljs
  36. 10 14
      src/main/frontend/util.cljc
  37. 2 3
      src/main/frontend/worker/db/migrate.cljs
  38. 5 2
      src/main/frontend/worker/db_worker.cljs
  39. 1 1
      src/main/logseq/api.cljs

+ 1 - 0
.gitignore

@@ -49,6 +49,7 @@ charlie/
 docker
 android/app/src/main/assets/capacitor.plugin.json
 ios/App/App/capacitor.config.json
+ios/App/App/public
 
 startup.png
 

+ 1 - 1
README.md

@@ -72,7 +72,7 @@ The Database version (DB version) of Logseq introduces DB graphs while maintaini
 
 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. **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.
 
-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 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.
 
 ## 🤔 Why Logseq?
 

+ 1 - 1
deps.edn

@@ -5,7 +5,7 @@
                                          :sha     "5d672bf84ed944414b9f61eeb83808ead7be9127"}
 
   datascript/datascript                 {:git/url "https://github.com/logseq/datascript" ;; fork
-                                         :sha     "4b1f15f05a6b4a718a62c247956206480e361ea6"}
+                                         :sha     "b28f6574b9447bba9ccaa5d2b0cfd79308acf0e3"}
 
   datascript-transit/datascript-transit {:mvn/version "0.3.0"}
   borkdude/rewrite-edn                  {:mvn/version "0.4.9"}

+ 1 - 1
deps/db/deps.edn

@@ -1,7 +1,7 @@
 {:deps
  ;; These nbb-logseq deps are kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
  {datascript/datascript {:git/url "https://github.com/logseq/datascript" ;; fork
-                         :sha     "4b1f15f05a6b4a718a62c247956206480e361ea6"}
+                         :sha     "b28f6574b9447bba9ccaa5d2b0cfd79308acf0e3"}
   datascript-transit/datascript-transit {:mvn/version "0.3.0"
                                          :exclusions [datascript/datascript]}
   cljs-bean/cljs-bean         {:mvn/version "1.5.0"}

+ 8 - 7
deps/db/src/logseq/db/sqlite/export.cljs

@@ -7,16 +7,16 @@
             [datascript.core :as d]
             [datascript.impl.entity :as de]
             [logseq.db :as ldb]
+            [logseq.db.common.entity-plus :as entity-plus]
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.db :as db-db]
-            [logseq.db.common.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.property :as db-property]
-            [logseq.db.sqlite.build :as sqlite-build]
-            [medley.core :as medley]
             [logseq.db.frontend.property.type :as db-property-type]
-            [logseq.db.frontend.schema :as db-schema]))
+            [logseq.db.frontend.schema :as db-schema]
+            [logseq.db.sqlite.build :as sqlite-build]
+            [medley.core :as medley]))
 
 ;; Export fns
 ;; ==========
@@ -527,8 +527,9 @@
 
 (defn- build-view-nodes-export
   "Exports given nodes from a view. Nodes are a random mix of blocks and pages"
-  [db eids]
-  (let [nodes (map #(d/entity db %) eids)
+  [db rows {:keys [group-by?]}]
+  (let [eids (if group-by? (mapcat second rows) rows)
+        nodes (map #(d/entity db %) eids)
         property-value-ents (mapcat #(->> (apply dissoc (db-property/properties %) db-property/public-db-attribute-properties)
                                           vals
                                           (filter de/entity?))
@@ -859,7 +860,7 @@
           :page
           (build-page-export db (:page-id options))
           :view-nodes
-          (build-view-nodes-export db (:node-ids options))
+          (build-view-nodes-export db (:rows options) (select-keys options [:group-by?]))
           :selected-nodes
           (build-selected-nodes-export db (:node-ids options))
           :graph-ontology

+ 4 - 4
deps/db/test/logseq/db/sqlite/export_test.cljs

@@ -524,14 +524,14 @@
                             (mapv #(vector :block/uuid (:block/uuid %)))))
         conn2 (db-test/create-conn)
         {:keys [init-tx block-props-tx] :as _txs}
-        (-> (sqlite-export/build-export @conn {:export-type :view-nodes :node-ids (get-node-ids @conn)})
+        (-> (sqlite-export/build-export @conn {:export-type :view-nodes :rows (get-node-ids @conn)})
             (sqlite-export/build-import @conn2 {}))
         ;; _ (cljs.pprint/pprint _txs)
         _ (d/transact! conn2 init-tx)
         _ (d/transact! conn2 block-props-tx)
         _ (validate-db @conn2)
         imported-nodes (sqlite-export/build-export @conn2 {:export-type :view-nodes
-                                                           :node-ids (get-node-ids @conn2)})]
+                                                           :rows (get-node-ids @conn2)})]
 
     (is (= (sort-pages-and-blocks (:pages-and-blocks original-data)) (:pages-and-blocks imported-nodes)))
     (is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
@@ -882,8 +882,8 @@
 (deftest build-import-can-import-existing-page-with-different-uuid
   (testing "By default any properties passed to an existing page are upserted"
     (test-import-existing-page {}
-                              {:logseq.property/description "second description"
-                               :logseq.property/exclude-from-graph-view true}))
+                               {:logseq.property/description "second description"
+                                :logseq.property/exclude-from-graph-view true}))
   (testing "With ::existing-pages-keep-properties?, existing properties on existing pages are not overwritten by imported data"
     (test-import-existing-page {:existing-pages-keep-properties? true}
                                {:logseq.property/description "first description"

+ 1 - 1
deps/outliner/deps.edn

@@ -1,7 +1,7 @@
 {:deps
  ;; These nbb-logseq deps are kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
  {datascript/datascript {:git/url "https://github.com/logseq/datascript" ;; fork
-                         :sha     "4b1f15f05a6b4a718a62c247956206480e361ea6"}
+                         :sha     "b28f6574b9447bba9ccaa5d2b0cfd79308acf0e3"}
   com.cognitect/transit-cljs {:mvn/version "0.8.280"}
 
   ;; Any other deps should be added here and to nbb.edn

+ 4 - 2
deps/publishing/src/logseq/publishing/html.cljs

@@ -2,10 +2,10 @@
   "This frontend only ns builds the publishing html including doing all the
 necessary db filtering"
   (:require [clojure.string :as string]
+            [datascript.core :as d]
+            [datascript.transit :as dt]
             [goog.string :as gstring]
             [goog.string.format]
-            [datascript.transit :as dt]
-            [datascript.core :as d]
             [logseq.publishing.db :as db]))
 
 ;; Copied from hiccup but tweaked for publish usage
@@ -129,6 +129,8 @@ necessary db filtering"
             [:script {:src "static/js/interact.min.js"}]
             [:script {:src "static/js/highlight.min.js"}]
             [:script {:src "static/js/katex.min.js"}]
+            [:script {:defer true :type "module" :src "static/js/pdfjs/pdf.mjs"}]
+            [:script {:defer true :type "module" :src "static/js/pdf_viewer3.mjs"}]
             [:script {:src "static/js/html2canvas.min.js"}]
             [:script {:src "static/js/code-editor.js"}]
             [:script {:src "static/js/custom.js"}]])))))

+ 0 - 1
packages/tldraw/apps/tldraw-logseq/src/lib/logseq-context.ts

@@ -55,7 +55,6 @@ export interface LogseqContextValue {
     queryBlockByUUID: (uuid: string) => any
     getBlockPageName: (uuid: string) => string
     getRedirectPageName: (uuidOrPageName: string) => string
-    insertFirstPageBlock: (pageName: string) => string
     isWhiteboardPage: (pageName: string) => boolean
     isMobile: () => boolean
     saveAsset: (file: File) => Promise<string>

+ 2 - 0
public/index.html

@@ -50,6 +50,8 @@
 <script defer src="/static/js/highlight.min.js"></script>
 <script defer src="/static/js/interact.min.js"></script>
 <script defer src="/static/js/marked.min.js"></script>
+<script defer type="module" src="/static/js/pdfjs/pdf.mjs"></script>
+<script defer type="module" src="/static/js/pdf_viewer3.mjs"></script>
 <script defer src="/static/js/eventemitter3.umd.min.js"></script>
 <script defer src="/static/js/html2canvas.min.js"></script>
 <script defer src="/static/js/lsplugin.core.js"></script>

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

@@ -916,6 +916,12 @@
      (let [inline-list (gp-mldoc/inline->edn v (mldoc/get-default-config format))]
        [:div.inline.mr-1 (map-inline config inline-list)]))))
 
+(defn- <get-block
+  [block-id]
+  (db-async/<get-block (state/get-current-repo) block-id
+                       {:children? false
+                        :skip-refresh? true}))
+
 (rum/defcs page-cp-inner < db-mixins/query rum/reactive
   {:init (fn [state]
            (let [args (:rum/args state)
@@ -932,8 +938,7 @@
                (or (:skip-async-load? config) (:table-view? config))
                (reset! *result page)
                :else
-               (p/let [result (db-async/<get-block (state/get-current-repo) page-id-or-name {:children? false
-                                                                                             :skip-refresh? true})]
+               (p/let [result (<get-block page-id-or-name)]
                  (reset! *result result)))
 
              (assoc state :*entity *result)))}
@@ -1032,7 +1037,10 @@
                  (let [block (last (:rum/args state))
                        asset-type (:logseq.property.asset/type block)
                        path (path/path-join common-config/local-assets-dir (str (:block/uuid block) "." asset-type))]
-                   (p/let [result (fs/file-exists? (config/get-repo-dir (state/get-current-repo)) path)]
+                   (p/let [result (if config/publishing?
+                                    ;; publishing doesn't have window.pfs defined
+                                    true
+                                    (fs/file-exists? (config/get-repo-dir (state/get-current-repo)) path))]
                      (reset! (::file-exists? state) result))
                    state))}
   [state config block]
@@ -1043,9 +1051,10 @@
         {:keys [direction loaded total]} (state/sub :rtc/asset-upload-download-progress
                                                     {:path-in-sub-atom [repo (str (:block/uuid block))]})
         downloading? (and (= direction :download) (not= loaded total))
-        download-finished? (and (= direction :download) (= loaded total))]
+        asset-file-write-finished? (state/sub :assets/asset-file-write-finish
+                                              {:path-in-sub-atom [repo (str (:block/uuid block))]})]
     (cond
-      (or file-exists? download-finished?)
+      (or file-exists? asset-file-write-finished?)
       (asset-link (assoc config :asset-block block)
                   (:block/title block)
                   (path/path-join (str "../" common-config/local-assets-dir) file)
@@ -1066,6 +1075,7 @@
         (contains? config/video-formats asset-type))))
 
 (declare block-positioned-properties)
+
 (rum/defc page-reference < rum/reactive db-mixins/query
   "Component for page reference"
   [{:keys [html-export? nested-link? show-brackets? id] :as config*} uuid-or-title* label]
@@ -1096,7 +1106,7 @@
               brackets? (and (or show-brackets? nested-link?)
                              (not html-export?)
                              (not contents-page?))]
-          (when-not (= (:db/id block) (:db/id (:block config)))
+          (when-not (and (:db/id block) (= (:db/id block) (:db/id (:block config))))
             (cond
               (and asset? (img-audio-video? block))
               (asset-cp config block)
@@ -2580,19 +2590,20 @@
    (dom/closest target ".query-table")))
 
 (defn- block-content-on-pointer-down
-  [e block block-id content edit-input-id config]
-  (let [selection-blocks (state/get-selection-blocks)
+  [e block block-id edit-input-id content config]
+  (let [target (.-target e)
+        selection-blocks (state/get-selection-blocks)
         starting-block (state/get-selection-start-block-or-first)
-        mobile-selection? (and (util/capacitor-new?) (seq selection-blocks))]
+        mobile-selection? (and (util/capacitor-new?) (seq selection-blocks))
+        block-dom-element (util/rec-get-node target "ls-block")]
     (if mobile-selection?
-      (let [ids (set (state/get-selection-block-ids))
-            block-node (gdom/getElement block-id)]
+      (let [ids (set (state/get-selection-block-ids))]
         (if (contains? ids (:block/uuid block))
           (do
-            (state/drop-selection-block! block-node)
+            (state/drop-selection-block! block-dom-element)
             (when (= 1 (count ids))
               (state/set-state! :mobile/show-action-bar? false)))
-          (state/conj-selection-block! block-node)))
+          (state/conj-selection-block! block-dom-element)))
       (do
         (util/mobile-keep-keyboard-open false)
         (when-not (or
@@ -2608,29 +2619,28 @@
                 (and meta? shift?)
                 (when-not (empty? selection-blocks)
                   (util/stop e)
-                  (editor-handler/highlight-selection-area! block-id {:append? true}))
+                  (editor-handler/highlight-selection-area! block-id block-dom-element {:append? true}))
 
                 meta?
                 (do
                   (util/stop e)
-                  (let [block-dom-element (gdom/getElement block-id)]
-                    (if (some #(= block-dom-element %) selection-blocks)
-                      (state/drop-selection-block! block-dom-element)
-                      (state/conj-selection-block! block-dom-element :down)))
+                  (if (some #(= block-dom-element %) selection-blocks)
+                    (state/drop-selection-block! block-dom-element)
+                    (state/conj-selection-block! block-dom-element :down))
                   (if (empty? (state/get-selection-blocks))
                     (state/clear-selection!)
-                    (state/set-selection-start-block! block-id)))
+                    (state/set-selection-start-block! block-dom-element)))
 
                 (and shift? starting-block)
                 (do
                   (util/stop e)
                   (util/clear-selection!)
-                  (editor-handler/highlight-selection-area! block-id))
+                  (editor-handler/highlight-selection-area! block-id block-dom-element))
 
                 shift?
                 (do
                   (util/clear-selection!)
-                  (state/set-selection-start-block! block-id))
+                  (state/set-selection-start-block! block-dom-element))
 
                 :else
                 (let [block (or (db/entity [:block/uuid (:block/uuid block)]) block)]
@@ -2639,7 +2649,7 @@
                   (let [f #(p/do!
                             (when-not (:block.temp/fully-loaded? (db/entity (:db/id block)))
                               (db-async/<get-block (state/get-current-repo) (:db/id block) {:children? false}))
-                            (let [cursor-range (some-> (gdom/getElement block-id)
+                            (let [cursor-range (some-> block-dom-element
                                                        (dom/by-class "block-content-inner")
                                                        first
                                                        util/caret-range)
@@ -2659,12 +2669,12 @@
                                {:db (db/get-db)
                                 :move-cursor? false
                                 :container-id (:container-id config)})))]
-                ;; wait a while for the value of the caret range
+                    ;; wait a while for the value of the caret range
                     (p/do!
                      (state/pub-event! [:editor/save-code-editor])
                      (f))
 
-                    (state/set-selection-start-block! block-id)))))))))))
+                    (state/set-selection-start-block! block-dom-element)))))))))))
 
 (rum/defc dnd-separator-wrapper < rum/reactive
   [block block-id top?]
@@ -2955,7 +2965,7 @@
                                             (let [f (:on-block-content-pointer-down config)]
                                               (if (fn? f)
                                                 (f e)
-                                                (block-content-on-pointer-down e block block-id content edit-input-id config))))))))]
+                                                (block-content-on-pointer-down e block block-id edit-input-id content config))))))))]
     [:div.block-content.inline
      (cond-> {:id (str "block-content-" uuid)
               :key (str "block-content-" uuid)}
@@ -3385,7 +3395,8 @@
 
 (defn- block-mouse-over
   [^js e block *control-show? block-id doc-mode?]
-  (let [mouse-moving? (not= (some-> @*block-last-mouse-event (.-clientY)) (.-clientY e))]
+  (let [mouse-moving? (not= (some-> @*block-last-mouse-event (.-clientY)) (.-clientY e))
+        block-dom-node (util/rec-get-node (.-target e) "ls-block")]
     (when (and mouse-moving?
                (not @*dragging?)
                (not= (:block/uuid block) (:block/uuid (state/get-edit-block))))
@@ -3398,7 +3409,7 @@
       (when (non-dragging? e)
         (when-let [container (gdom/getElement "app-container-wrapper")]
           (dom/add-class! container "blocks-selection-mode"))
-        (editor-handler/highlight-selection-area! block-id {:append? true})))))
+        (editor-handler/highlight-selection-area! block-id block-dom-node {:append? true})))))
 
 (defn- block-mouse-leave
   [*control-show? block-id doc-mode?]
@@ -4319,9 +4330,12 @@
                                      (when (not (:block-children? config))
                                        {:top? top?
                                         :bottom? bottom?}))
-        (str (:block/uuid item)
-             (when linked-block
-               (str "-" (:block/uuid original-block))))))))
+        (str
+         (:container-id config)
+         "-"
+         (:block/uuid item)
+         (when linked-block
+           (str "-" (:block/uuid original-block))))))))
 
 (rum/defc block-list
   [config blocks]

+ 33 - 34
src/main/frontend/components/cmdk/core.cljs

@@ -131,10 +131,16 @@
                                       (= input (util/page-name-sanity-lc (:block/title block))))) blocks-result))))
         include-slash? (or (string/includes? input "/")
                            (string/starts-with? input "/"))
+        start-with-slash? (string/starts-with? input "/")
         order* (cond
                  (= search-mode :graph)
                  []
 
+                 start-with-slash?
+                 [["Filters" :filters (visible-items :filters)]
+                  ["Current page"   :current-page   (visible-items :current-page)]
+                  ["Nodes"          :nodes         (visible-items :nodes)]]
+
                  include-slash?
                  [(when-not node-exists?
                     ["Create"         :create         (create-items input)])
@@ -558,8 +564,11 @@
 
 (defmethod handle-action :filter [_ state _event]
   (let [item (some-> state state->highlighted-item)
-        !input (::input state)]
-    (reset! !input (get-filter-user-input @!input))
+        !input (::input state)
+        input-ref @(::input-ref state)]
+    (let [value (get-filter-user-input @!input)]
+      (reset! !input value)
+      (set! (.-value input-ref) value))
     (let [!filter (::filter state)
           group (get-in item [:filter :group])]
       (swap! !filter assoc :group group)
@@ -779,13 +788,14 @@
                                       (handle-action :default state e)
                                       (util/stop-propagation e))
       esc? (let [filter' @(::filter state)]
-             (when-not (string/blank? input)
-               (util/stop e)
-               (handle-input-change state nil ""))
-             (when (and filter' (string/blank? input))
-               (util/stop e)
-               (reset! (::filter state) nil)
-               (load-results :default state)))
+             (if filter'
+               (do
+                 (util/stop e)
+                 (reset! (::filter state) nil)
+                 (load-results :default state))
+               (when-not (string/blank? input)
+                 (util/stop e)
+                 (handle-input-change state nil ""))))
       (and meta? (= keyname "c")) (do
                                     (copy-block-ref state)
                                     (util/stop-propagation e))
@@ -841,6 +851,7 @@
       {:class "text-xl bg-transparent border-none w-full outline-none px-3 py-3"
        :auto-focus true
        :autoComplete "off"
+       :autoCapitalize false
        :placeholder (input-placeholder false)
        :ref #(when-not @input-ref (reset! input-ref %))
        :on-change debounced-on-change
@@ -848,23 +859,12 @@
                   (when-let [on-blur (:on-input-blur opts)]
                     (on-blur input)))
        :on-composition-end (gfun/debounce (fn [e] (handle-input-change state e)) 100)
-       :on-key-down (gfun/debounce
-                     (fn [e]
-                       (p/let [value (.-value @input-ref)
-                               last-char (last value)
-                               backspace? (= (util/ekey e) "Backspace")
-                               filter-group (:group @(::filter state))
-                               slash? (= (util/ekey e) "/")
-                               namespace-pages (when (and slash? (contains? #{:whiteboards} filter-group))
-                                                 (search/block-search (state/get-current-repo) (str value "/") {}))
-                               namespace-page-matched? (some #(string/includes? % "/") namespace-pages)]
-                         (when (and filter-group
-                                    (or (and slash? (not namespace-page-matched?))
-                                        (and backspace? (= last-char "/"))
-                                        (and backspace? (= input ""))))
-                           (reset! (::filter state) nil)
-                           (load-results :default state))))
-                     100)
+       :on-key-down (fn [e]
+                      (case (util/ekey e)
+                        "Esc"
+                        (when-not @(::filter state)
+                          (shui/dialog-close!))
+                        nil))
        :default-value input}]]))
 
 (defn rand-tip
@@ -1044,14 +1044,13 @@
                                       (and (contains? #{:create} group-filter)
                                            (= group-key :create))))))
                    results-ordered)]
-        (when-not (= ["Filters"] (map first items))
-          (if (seq items)
-            (for [[group-name group-key _group-count group-items] items]
-              (let [title (string/capitalize group-name)]
-                (result-group state title group-key group-items first-item sidebar?)))
-            [:div.flex.flex-col.p-4.opacity-50
-             (when-not (string/blank? @*input)
-               "No matched results")])))]
+        (if (seq items)
+          (for [[group-name group-key _group-count group-items] items]
+            (let [title (string/capitalize group-name)]
+              (result-group state title group-key group-items first-item sidebar?)))
+          [:div.flex.flex-col.p-4.opacity-50
+           (when-not (string/blank? @*input)
+             "No matched results")]))]
      (when-not sidebar? (hints state))]))
 
 (rum/defc cmdk-modal [props]

+ 10 - 6
src/main/frontend/components/editor.cljs

@@ -155,8 +155,9 @@
                    (when-not (string/blank? q)
                      (p/let [result (if db-tag?
                                       (editor-handler/get-matched-classes q)
-                                      (editor-handler/<get-matched-blocks q {:nlp-pages? true
-                                                                             :page-only? (not db-based?)}))]
+                                      (p/let [result (editor-handler/<get-matched-blocks q {:nlp-pages? true
+                                                                                            :page-only? (not db-based?)})]
+                                        (reverse (sort-by (fn [result] (:page? result)) result))))]
                        (set-matched-pages! result))))]
     (hooks/use-effect! search-f [(hooks/use-debounced-value q 150)])
 
@@ -206,10 +207,10 @@
                                  (ui/icon "letter-p" {:size 14})
 
                                  (db-model/whiteboard-page? block')
-                                 (ui/icon "writing")
+                                 (ui/icon "writing" {:size 14})
 
-                                 (:page? block')
-                                 (ui/icon "file")
+                                 (or (ldb/page? block') (:page? block'))
+                                 (ui/icon "file" {:size 14})
 
                                  (or (string/starts-with? (str (:block/title block')) (t :new-tag))
                                      (string/starts-with? (str (:block/title block')) (t :new-page)))
@@ -225,7 +226,10 @@
                                               (str title " -> alias: " (:block/title target))
                                               title))
                                           (block-handler/block-unique-title block'))]
-                              (search-handler/highlight-exact-query title q))]]))
+                              (if (or (string/starts-with? title (t :new-tag))
+                                      (string/starts-with? title (t :new-page)))
+                                title
+                                (search-handler/highlight-exact-query title q)))]]))
          :empty-placeholder [:div.text-gray-500.text-sm.px-4.py-2 (if db-tag?
                                                                     "Search for a tag"
                                                                     "Search for a node")]

+ 38 - 37
src/main/frontend/components/page.cljs

@@ -102,42 +102,43 @@
 (rum/defc add-button
   [block container-id]
   (let [*ref (rum/use-ref nil)
-        has-children? (:block/_parent block)]
-    [:div.ls-block.block-add-button.flex-1.flex-col.rounded-sm.cursor-text.transition-opacity.ease-in.duration-100.!py-0
-     {:class (if has-children?
-               "opacity-0"
-               "opacity-50")
-      :data-blockId (:db/id block)
-      :ref *ref
-      :on-click (fn [e]
-                  (util/stop e)
-                  (state/set-state! :editor/container-id container-id)
-                  (editor-handler/api-insert-new-block! ""
-                                                        {:block-uuid (:block/uuid block)}))
-      :on-mouse-over (fn []
-                       (let [ref (rum/deref *ref)
-                             prev-block (util/get-prev-block-non-collapsed (rum/deref *ref) {:up-down? true})]
-                         (cond
-                           (and prev-block (dom/has-class? prev-block "is-blank"))
-                           (dom/add-class! ref "opacity-0")
-                           (and prev-block has-children?)
-                           (dom/add-class! ref "opacity-50")
-                           :else
-                           (dom/add-class! ref "opacity-100"))))
-      :on-mouse-leave #(do
-                         (dom/remove-class! (rum/deref *ref) "opacity-50")
-                         (dom/remove-class! (rum/deref *ref) "opacity-100"))
-      :on-key-down (fn [e]
-                     (util/stop e)
-                     (when (= "Enter" (util/ekey e))
-                       (state/set-state! :editor/container-id container-id)
-                       (editor-handler/api-insert-new-block! "" block)))
-      :tab-index 0}
-     [:div.flex.flex-row
-      [:div.flex.items-center {:style {:height 28
-                                       :margin-left (if (util/mobile?) 0 22)}}
-       [:span.bullet-container.cursor.opacity-0.transition-opacity.ease-in.duration-100
-        [:span.bullet]]]]]))
+        has-children? (:block/_parent block)
+        page? (ldb/page? block)
+        opacity-class (if has-children? "opacity-0" "opacity-50")]
+    (when page?
+      [:div.ls-block.block-add-button.flex-1.flex-col.rounded-sm.cursor-text.transition-opacity.ease-in.duration-100.!py-0
+       {:class opacity-class
+        :data-blockId (:db/id block)
+        :ref *ref
+        :on-click (fn [e]
+                    (util/stop e)
+                    (state/set-state! :editor/container-id container-id)
+                    (editor-handler/api-insert-new-block! ""
+                                                          {:block-uuid (:block/uuid block)}))
+        :on-mouse-over (fn []
+                         (let [ref (rum/deref *ref)
+                               prev-block (util/get-prev-block-non-collapsed (rum/deref *ref) {:up-down? true})]
+                           (cond
+                             (and prev-block (dom/has-class? prev-block "is-blank"))
+                             (dom/add-class! ref "opacity-0")
+                             (and prev-block has-children?)
+                             (dom/add-class! ref "opacity-50")
+                             :else
+                             (dom/add-class! ref "opacity-100"))))
+        :on-mouse-leave #(do
+                           (dom/remove-class! (rum/deref *ref) "opacity-50")
+                           (dom/remove-class! (rum/deref *ref) "opacity-100"))
+        :on-key-down (fn [e]
+                       (util/stop e)
+                       (when (= "Enter" (util/ekey e))
+                         (state/set-state! :editor/container-id container-id)
+                         (editor-handler/api-insert-new-block! "" block)))
+        :tab-index 0}
+       [:div.flex.flex-row
+        [:div.flex.items-center {:style {:height 28
+                                         :margin-left (if (util/mobile?) 0 22)}}
+         [:span.bullet-container
+          [:span.bullet]]]]])))
 
 (rum/defcs page-blocks-cp < rum/reactive db-mixins/query
   {:will-mount (fn [state]
@@ -652,7 +653,7 @@
                                                    :whiteboard? whiteboard?}))])])
 
          (when (and (not preview?) (or (not show-tabs?) tabs-rendered?))
-           [:div.ml-1.flex.flex-col.gap-4
+           [:div.ml-1.flex.flex-col.gap-8.mt-4
             (when today?
               (today-queries repo today? sidebar?))
 

+ 1 - 0
src/main/frontend/components/property.css

@@ -293,6 +293,7 @@ a.control-link {
   min-height: 28px;
   padding-top: 0;
   padding-bottom: 0;
+  container-type: normal;
 }
 
 .ls-page-properties .property-key, .ls-properties-area .property-key {

+ 7 - 3
src/main/frontend/components/property/value.cljs

@@ -631,17 +631,17 @@
         clear-value-label [:div.flex.flex-row.items-center.gap-1.text-sm
                            (ui/icon "x" {:size 14})
                            [:div clear-value]]
-        [items _] (hooks/use-state (sort-select-items property selected-choices items))
+        [sorted-items set-items!] (hooks/use-state (sort-select-items property selected-choices items))
         items' (->>
                 (if (and (seq selected-choices)
                          (not multiple-choices?)
                          (not (and (ldb/class? block) (= (:db/ident property) :logseq.property/parent)))
                          (not= (:db/ident property) :logseq.property.view/type))
-                  (concat items
+                  (concat sorted-items
                           [{:value clear-value
                             :label clear-value-label
                             :clear? true}])
-                  items)
+                  sorted-items)
                 (remove #(= :logseq.property/empty-placeholder (:value %))))
         k :on-chosen
         f (get opts k)
@@ -658,6 +658,10 @@
                 (when-not (false? (:exit-edit? opts))
                   (shui/popup-hide!)))
                (f chosen selected?)))]
+    (hooks/use-effect!
+     (fn []
+       (set-items! (sort-select-items property selected-choices items)))
+     [items])
     (select/select (assoc opts
                           :selected-choices selected-choices
                           :items items'

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

@@ -483,7 +483,7 @@
               (:name column))))))
        (shui/dropdown-menu-item
         {:key "export-edn"
-         :on-click #(db-export-handler/export-view-nodes-data rows)}
+         :on-click #(db-export-handler/export-view-nodes-data rows {:group-by? (some? group-by-property-ident)})}
         "Export EDN"))))))
 
 (defn- get-column-size

+ 0 - 14
src/main/frontend/db/model.cljs

@@ -338,20 +338,6 @@ independent of format as format specific heading characters are stripped"
   (when page-name-or-uuid
     (ldb/get-case-page (conn/get-db) page-name-or-uuid)))
 
-(defn get-journal-page
-  [page-title]
-  (when-let [journal-day (date/journal-title->int page-title)]
-    (when-let [db (conn/get-db)]
-      (->
-       (d/q
-        '[:find [?page ...]
-          :in $ ?day
-          :where
-          [?page :block/journal-day ?day]]
-        db
-        journal-day)
-       first))))
-
 (defn get-redirect-page-name
   "Given any readable page-name, return the exact page-name in db. If page
    doesn't exists yet, will return the passed `page-name`. Accepts both

+ 0 - 1
src/main/frontend/extensions/code.cljs

@@ -481,7 +481,6 @@
                                      (contains? #{:code} (:logseq.property.node/display-type code-block))
                                      (not= (:block/uuid edit-block) (:block/uuid (state/get-edit-block))))
                                 (editor-handler/edit-block! (or code-block edit-block) :max {:container-id (:container-id config)}))
-                              (state/set-editing-block-dom-id! (:block-parent-id config))
                               (state/set-block-component-editing-mode! true)
                               (state/set-state! :editor/code-block-context
                                                 {:editor editor

+ 9 - 15
src/main/frontend/extensions/pdf/assets.cljs

@@ -118,7 +118,7 @@
             page))))))
 
 (defn file-based-ensure-ref-block!
-  [pdf-current {:keys [id content page properties] :as hl} insert-opts]
+  [pdf-current {:keys [id content page properties] :as _hl} insert-opts]
   (p/let [ref-page (when pdf-current (file-based-ensure-ref-page! pdf-current))]
     (when ref-page
       (let [ref-block (db-model/query-block-by-uuid id)]
@@ -126,29 +126,23 @@
           (do
             (println "[existed ref block]" ref-block)
             ref-block)
-          (let [text       (:text content)
+          (let [text (:text content)
+                area? (not (nil? (:image content)))
                 wrap-props #(if-let [stamp (:image content)]
                               (assoc %
                                      :hl-type :area
                                      :hl-stamp stamp)
                               %)
-                db-base? (config/db-based-graph? (state/get-current-repo))
-                props (cond->
-                       {(pu/get-pid :logseq.property/ls-type)  :annotation
-                        (pu/get-pid :logseq.property.pdf/hl-page)  page
-                        (pu/get-pid :logseq.property.pdf/hl-color) (:color properties)}
-
-                        db-base?
-                        (assoc (pu/get-pid :logseq.property.pdf/hl-value) hl)
-
-                        (not db-base?)
-                         ;; force custom uuid
-                        (assoc :id (if (string? id) (uuid id) id)))
+                props {:id (if (string? id) (uuid id) id)
+                       (pu/get-pid :logseq.property/ls-type) :annotation
+                       (pu/get-pid :logseq.property.pdf/hl-page) page
+                       (pu/get-pid :logseq.property.pdf/hl-color) (:color properties)}
                 properties (wrap-props props)]
             (when (string? text)
               (editor-handler/api-insert-new-block!
-               text (merge {:page        (:block/name ref-page)
+               text (merge {:page (:block/name ref-page)
                             :custom-uuid id
+                            :edit-block? (not area?)
                             :properties properties}
                            insert-opts)))))))))
 

+ 10 - 12
src/main/frontend/extensions/pdf/core.cljs

@@ -176,13 +176,13 @@
                              (if-not id
                                ;; add highlight
                                (let [highlight (merge highlight
-                                                      {:id         (pdf-utils/gen-uuid)
+                                                      {:id (pdf-utils/gen-uuid)
                                                        :properties properties})]
                                  (p/let [highlight' (add-hl! highlight)]
                                    (pdf-utils/clear-all-selection owner-win)
                                    (pdf-assets/copy-hl-ref! highlight' viewer)))
 
-;; update highlight
+                               ;; update highlight
                                (upd-hl! (assoc highlight :properties properties)))
 
                              (reset! *highlight-last-color (keyword action)))))
@@ -570,16 +570,14 @@
 
                       (if-let [vw-pos (and (pdf-assets/area-highlight? hl)
                                            (pdf-utils/scaled-to-vw-pos viewer (:position hl)))]
-                        ;; exceptions
-                        (->
-                         (p/let [result (pdf-assets/persist-hl-area-image$ viewer (:pdf/current @state/state)
-                                                                           hl nil (:bounding vw-pos))]
-                           (if (de/entity? result)
-                             (let [hl' (assoc-in hl [:content :image] (:db/id result))]
-                               (set-highlights! (map (fn [hl] (if (= (:id hl) (:id hl')) hl' hl)) highlights'))
-                               hl')
-                             (throw (js/Error. (str "[pdf] unexpected persist asset image return:" result)))))
-                         (p/catch (fn [e] (js/console.error e))))
+                        (-> (p/let [result (pdf-assets/persist-hl-area-image$ viewer (:pdf/current @state/state)
+                                                                              hl nil (:bounding vw-pos))]
+                              (if (de/entity? result)
+                                (let [hl' (assoc-in hl [:content :image] (:db/id result))]
+                                  (set-highlights! (map (fn [hl] (if (= (:id hl) (:id hl')) hl' hl)) highlights'))
+                                  hl')
+                                hl))
+                            (p/catch (fn [e] (js/console.error e))))
                         hl))))
         upd-hl! (fn [hl]
                   (let [highlights (pdf-utils/fix-nested-js highlights)]

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

@@ -14,7 +14,6 @@
             [frontend.db.model :as model]
             [frontend.extensions.pdf.assets :as pdf-assets]
             [frontend.handler.assets :as assets-handler]
-            [frontend.handler.editor :as editor-handler]
             [frontend.handler.file-based.editor :as file-editor-handler]
             [frontend.handler.history :as history]
             [frontend.handler.notification :as notification]
@@ -142,8 +141,6 @@
    :setCurrentPdf (fn [src] (state/set-current-pdf! (if src (pdf-assets/inflate-asset src) nil)))
    :copyToClipboard (fn [text, html] (util/copy-to-clipboard! text :html html))
    :getRedirectPageName (fn [page-name-or-uuid] (model/get-redirect-page-name page-name-or-uuid))
-   :insertFirstPageBlock (fn [page-name]
-                           (editor-handler/insert-first-page-block-if-not-exists! page-name))
    :addNewPage (fn [page-name]
                  (p/let [result (page-handler/<create! page-name {:redirect? false})]
                    (str (:block/uuid result))))

+ 4 - 6
src/main/frontend/handler.cljs

@@ -44,12 +44,10 @@
 
 (defn- set-global-error-notification!
   []
-  (when-not config/dev?
-    (set! js/window.onerror
-          (fn [message, _source, _lineno, _colno, error]
-            (when-not (error/ignored? message)
-              (js/console.error message)
-              (log/error :exception error))))))
+  (set! js/window.onerror
+        (fn [message, _source, _lineno, _colno, error]
+          (when-not (error/ignored? message)
+            (log/error :exception error)))))
 
 (defn- watch-for-date!
   []

+ 5 - 1
src/main/frontend/handler/assets.cljs

@@ -249,7 +249,11 @@
         repo-dir (config/get-repo-dir repo)
         file-path (path/path-join common-config/local-assets-dir
                                   (str asset-block-id-str "." asset-type))]
-    (fs/write-plain-text-file! repo repo-dir file-path data {})))
+    (p/do!
+     (fs/write-plain-text-file! repo repo-dir file-path data {})
+     (state/update-state!
+      :assets/asset-file-write-finish
+      (fn [m] (assoc-in m [repo asset-block-id-str] (common-util/time-ms)))))))
 
 (defn <unlink-asset
   [repo asset-block-id asset-type]

+ 13 - 3
src/main/frontend/handler/common/page.cljs

@@ -2,7 +2,8 @@
   "Common fns for file and db based page handlers, including create!, delete!
   and favorite fns. This ns should be agnostic of file or db concerns but there
   is still some file-specific tech debt to remove from create!"
-  (:require [clojure.string :as string]
+  (:require [clojure.set :as set]
+            [clojure.string :as string]
             [datascript.core :as d]
             [dommy.core :as dom]
             [frontend.config :as config]
@@ -55,8 +56,17 @@
                                (common-util/split-first (str "#" page-ref/left-brackets) (:block/title parsed-result)))
                               string/trim)
                       title)]
-       (if (and has-tags? (nil? title'))
-         (notification/show! "Page name can't include \"#\"." :warning)
+       (cond
+         (and has-tags? (nil? title'))
+         (notification/show! "Page name can't include \"#\"." :error)
+         (and has-tags?
+              (seq (set/intersection ldb/private-tags (set (map :db/ident (:block/tags parsed-result))))))
+         (notification/show! (str "New page can't set built-in tags: "
+                                  (string/join ", "
+                                               (keep #(when (ldb/private-tags (:db/ident %)) (pr-str (:block/title %)))
+                                                     (:block/tags parsed-result))))
+                             :error)
+         :else
          (when-not (string/blank? title')
            (p/let [options' (if db-based?
                               (cond-> (update options :tags concat (:block/tags parsed-result))

+ 4 - 2
src/main/frontend/handler/db_based/export.cljs

@@ -23,10 +23,12 @@
         (notification/show! "Copied block's data!" :success)))
     (notification/show! "No block found" :warning)))
 
-(defn export-view-nodes-data [node-ids]
+(defn export-view-nodes-data [rows {:keys [group-by?]}]
   (p/let [result (state/<invoke-db-worker :thread-api/export-edn
                                           (state/get-current-repo)
-                                          {:export-type :view-nodes :node-ids node-ids})
+                                          {:export-type :view-nodes
+                                           :rows rows
+                                           :group-by? group-by?})
           pull-data (with-out-str (pprint/pprint result))]
     (when-not (= :export-edn-error result)
       (.writeText js/navigator.clipboard pull-data)

+ 70 - 84
src/main/frontend/handler/editor.cljs

@@ -603,20 +603,6 @@
              (when-let [id (:block/uuid new-block)]
                (db/entity [:block/uuid id])))))))))
 
-(defn insert-first-page-block-if-not-exists!
-  [page-uuid-or-title]
-  (let [page-title (str page-uuid-or-title)]
-    (when-not (string/blank? page-title)
-      (when-let [page (db/get-page page-title)]
-        (let [class-or-property? (or (ldb/class? page) (ldb/property? page))]
-          (when (or class-or-property? (db/page-empty? (state/get-current-repo) (:db/id page)))
-            (let [new-block (cond-> {:block/title ""}
-                              (not (config/db-based-graph? (state/get-current-repo)))
-                              (assoc :block/format (get page :block/format :markdown)))]
-              (ui-outliner-tx/transact!
-               {:outliner-op :insert-blocks}
-               (outliner-op/insert-blocks! [new-block] page {:sibling? false})))))))))
-
 (defn check
   [{:block/keys [marker title repeated? uuid] :as block}]
   (let [new-content (string/replace-first title marker "DONE")
@@ -782,11 +768,10 @@
 (declare expand-block!)
 
 (defn delete-block-inner!
-  [repo {:keys [block-id value format config]}]
+  [repo {:keys [block-id value format config block-container]}]
   (when block-id
     (when-let [block-e (db/entity [:block/uuid block-id])]
-      (let [prev-block (db-model/get-prev (db/get-db) (:db/id block-e))
-            block-parent-id (str "ls-block-" block-id)]
+      (let [prev-block (db-model/get-prev (db/get-db) (:db/id block-e))]
         (cond
           (and (nil? prev-block)
                (nil? (:block/parent block-e)))
@@ -801,56 +786,55 @@
                                           (let [block (db/entity [:block/uuid block-id])]
                                             (seq (:block/_parent block)))))]
             (when-not (and has-children? left-has-children?)
-              (when block-parent-id
-                (let [block-parent (gdom/getElement block-parent-id)
-                      sibling-or-parent-block
-                      (if (:embed? config)
-                        (util/get-prev-block-non-collapsed
-                         block-parent
-                         {:container (util/rec-get-blocks-container block-parent)})
-                        (util/get-prev-block-non-collapsed-non-embed block-parent))
-                      {:keys [prev-block new-content edit-block-f]}
-                      (move-to-prev-block repo sibling-or-parent-block format value)
-                      concat-prev-block? (boolean (and prev-block new-content))
-                      transact-opts {:outliner-op :delete-blocks}]
-                  (cond
-                    (and prev-block (:block/name prev-block)
-                         (not= (:db/id prev-block) (:db/id (:block/parent block)))
-                         (db-model/hidden-page? (:block/page block))) ; embed page
-                    nil
-
-                    concat-prev-block?
-                    (let [children (:block/_parent (db/entity (:db/id block)))
-                          db-based? (config/db-based-graph? repo)
-                          prev-block-is-not-parent? (empty? (:block/_parent prev-block))
-                          delete-prev-block? (and db-based?
-                                                  prev-block-is-not-parent?
-                                                  (empty? (:block/tags block))
-                                                  (not (:logseq.property.node/display-type block))
-                                                  (seq (:block/properties block))
-                                                  (empty? (:block/properties prev-block))
-                                                  (not (:logseq.property/created-from-property block)))]
-                      (if delete-prev-block?
-                        (p/do!
-                         (state/set-state! :editor/edit-block-fn
-                                           #(edit-block! (assoc block :block/title new-content) (count (:block/title prev-block))))
-                         (ui-outliner-tx/transact!
-                          transact-opts
-                          (delete-block-aux! prev-block)
-                          (save-block! repo block new-content {})))
-                        (p/do!
-                         (state/set-state! :editor/edit-block-fn edit-block-f)
-                         (ui-outliner-tx/transact!
-                          transact-opts
-                          (when (seq children)
-                            (outliner-op/move-blocks! children prev-block false))
-                          (delete-block-aux! block)
-                          (save-block! repo prev-block new-content {})))))
-
-                    :else
-                    (p/do!
-                     (state/set-state! :editor/edit-block-fn edit-block-f)
-                     (delete-block-aux! block))))))))))))
+              (let [block-parent block-container
+                    sibling-or-parent-block
+                    (if (:embed? config)
+                      (util/get-prev-block-non-collapsed
+                       block-parent
+                       {:container (util/rec-get-blocks-container block-parent)})
+                      (util/get-prev-block-non-collapsed-non-embed block-parent))
+                    {:keys [prev-block new-content edit-block-f]}
+                    (move-to-prev-block repo sibling-or-parent-block format value)
+                    concat-prev-block? (boolean (and prev-block new-content))
+                    transact-opts {:outliner-op :delete-blocks}]
+                (cond
+                  (and prev-block (:block/name prev-block)
+                       (not= (:db/id prev-block) (:db/id (:block/parent block)))
+                       (db-model/hidden-page? (:block/page block))) ; embed page
+                  nil
+
+                  concat-prev-block?
+                  (let [children (:block/_parent (db/entity (:db/id block)))
+                        db-based? (config/db-based-graph? repo)
+                        prev-block-is-not-parent? (empty? (:block/_parent prev-block))
+                        delete-prev-block? (and db-based?
+                                                prev-block-is-not-parent?
+                                                (empty? (:block/tags block))
+                                                (not (:logseq.property.node/display-type block))
+                                                (seq (:block/properties block))
+                                                (empty? (:block/properties prev-block))
+                                                (not (:logseq.property/created-from-property block)))]
+                    (if delete-prev-block?
+                      (p/do!
+                       (state/set-state! :editor/edit-block-fn
+                                         #(edit-block! (assoc block :block/title new-content) (count (:block/title prev-block))))
+                       (ui-outliner-tx/transact!
+                        transact-opts
+                        (delete-block-aux! prev-block)
+                        (save-block! repo block new-content {})))
+                      (p/do!
+                       (state/set-state! :editor/edit-block-fn edit-block-f)
+                       (ui-outliner-tx/transact!
+                        transact-opts
+                        (when (seq children)
+                          (outliner-op/move-blocks! children prev-block false))
+                        (delete-block-aux! block)
+                        (save-block! repo prev-block new-content {})))))
+
+                  :else
+                  (p/do!
+                   (state/set-state! :editor/edit-block-fn edit-block-f)
+                   (delete-block-aux! block)))))))))))
 
 (defn delete-block!
   [repo]
@@ -1145,9 +1129,9 @@
        (save-current-block!)
        (if (re-find url-regex page)
          (js/window.open page)
-         (let [page-name (db-model/get-redirect-page-name page)]
+         (do
            (state/clear-edit!)
-           (insert-first-page-block-if-not-exists! page-name)))))))
+           (route-handler/redirect-to-page! page)))))))
 
 (defn open-link-in-sidebar!
   []
@@ -1217,26 +1201,24 @@
       (delete-block-aux! block))))
 
 (defn highlight-selection-area!
-  [end-block-id & {:keys [append?]}]
-  (when-let [start-block (state/get-selection-start-block-or-first)]
-    (let [end-block-node (gdom/getElement end-block-id)
-          start-node (gdom/getElement start-block)
+  [end-block-id block-dom-element & {:keys [append?]}]
+  (when-let [start-node (state/get-selection-start-block-or-first)]
+    (let [end-block-node block-dom-element
           select-direction (state/get-selection-direction)
           selected-blocks (state/get-unsorted-selection-blocks)
-          last-node (when-let [node (last selected-blocks)]
-                      (gdom/getElement (.-id ^js node)))
+          last-node (last selected-blocks)
           latest-visible-block (or last-node start-node)
           latest-block-id (when latest-visible-block (.-id latest-visible-block))]
       (if (and start-node end-block-node)
-        (let [blocks (util/get-nodes-between-two-nodes start-block end-block-id "ls-block")
-              direction (util/get-direction-between-two-nodes start-block end-block-id "ls-block")
+        (let [blocks (util/get-nodes-between-two-nodes start-node end-block-node "ls-block")
+              direction (util/get-direction-between-two-nodes start-node end-block-node "ls-block")
               blocks (if (= direction :up) (reverse blocks) blocks)]
           (state/exit-editing-and-set-selected-blocks! blocks direction))
         (when latest-visible-block
-          (let [blocks (util/get-nodes-between-two-nodes latest-block-id end-block-id "ls-block")
+          (let [blocks (util/get-nodes-between-two-nodes latest-visible-block end-block-node "ls-block")
                 direction (if (= latest-block-id end-block-id)
                             select-direction
-                            (util/get-direction-between-two-nodes latest-block-id end-block-id "ls-block"))
+                            (util/get-direction-between-two-nodes latest-visible-block end-block-node "ls-block"))
                 blocks (if (= direction :up) (reverse (util/sort-by-height blocks)) (util/sort-by-height blocks))]
             (if append?
               (do (state/clear-edit!)
@@ -1269,7 +1251,7 @@
   (cond
     ;; when editing, quit editing and select current block
     (state/editing?)
-    (let [element (gdom/getElement (state/get-editing-block-dom-id))]
+    (when-let [element (state/get-editor-block-container)]
       (when element
         (p/do!
          (save-current-block!)
@@ -2414,7 +2396,9 @@
               "page-ref" (when-not (string/blank? (:link thing-at-point))
                            (let [page (:link thing-at-point)
                                  page-name (db-model/get-redirect-page-name page)]
-                             (insert-first-page-block-if-not-exists! page-name)))
+                             (p/do!
+                              (save-current-block!)
+                              (route-handler/redirect-to-page! page-name))))
               "list-item" (dwim-in-list)
               "properties-drawer" (dwim-in-properties state))
 
@@ -2535,7 +2519,8 @@
                (save-block! repo uuid value))
 
              (cond
-               (dom/has-class? sibling-block "block-add-button")
+               (and (dom/has-class? sibling-block "block-add-button")
+                    (util/rec-get-node current-block "ls-page-title"))
                (.click sibling-block)
 
                property-value-container?
@@ -2588,7 +2573,7 @@
         {:block/keys [format uuid] :as block} (or block (state/get-edit-block))
         format (or format :markdown)
         repo (state/get-current-repo)
-        editing-block (gdom/getElement (state/get-editing-block-dom-id))
+        editing-block (state/get-editor-block-container)
         f (if up? util/get-prev-block-non-collapsed util/get-next-block-non-collapsed)
         sibling-block (f editing-block)
         sibling-block (or (when (and sibling-block (property-value-node? sibling-block))
@@ -2611,7 +2596,8 @@
           (property-value-node? sibling-block)
           (focus-trigger editing-block sibling-block)
 
-          (dom/has-class? sibling-block "block-add-button")
+          (and (dom/has-class? sibling-block "block-add-button")
+               (util/rec-get-node editing-block "ls-page-title"))
           (.click sibling-block)
 
           :else

+ 1 - 4
src/main/frontend/handler/editor/lifecycle.cljs

@@ -9,7 +9,7 @@
 
 (defn did-mount!
   [state]
-  (let [[{:keys [block-parent-id]} id] (:rum/args state)
+  (let [[_ id] (:rum/args state)
         content (state/get-edit-content)
         input (state/get-input)
         node (util/rec-get-node input "ls-block")
@@ -20,9 +20,6 @@
     (when container-id
       (state/set-state! :editor/container-id container-id))
 
-    (when block-parent-id
-      (state/set-editing-block-dom-id! block-parent-id))
-
     (when content
       (editor-handler/restore-cursor-pos! id content))
 

+ 2 - 1
src/main/frontend/handler/events/ui.cljs

@@ -47,7 +47,8 @@
    {:id :ls-dialog-cmdk
     :align :top
     :content-props {:class "ls-dialog-cmdk"}
-    :close-btn? false}))
+    :close-btn? false
+    :onEscapeKeyDown (fn [e] (.preventDefault e))}))
 
 (defmethod events/handle :command/run [_]
   (when (util/electron?)

+ 1 - 3
src/main/frontend/handler/jump.cljs

@@ -99,9 +99,7 @@
         (editor-handler/expand-block! current-block-id))
       (let [f #(let [selected-block-or-editing-block (or (first (state/get-selection-blocks))
                                                          ;; current editing block
-                                                         (some-> (state/get-editing-block-dom-id)
-                                                                 js/document.getElementById
-                                                                 (util/rec-get-node ".ls-block")))
+                                                         (state/get-editor-block-container))
                      triggers (->> (if selected-block-or-editing-block
                                      (d/sel selected-block-or-editing-block ".jtrigger")
                                      (d/sel ".jtrigger"))

+ 3 - 7
src/main/frontend/handler/page.cljs

@@ -11,7 +11,6 @@
             [frontend.db :as db]
             [frontend.db.async :as db-async]
             [frontend.db.conn :as conn]
-            [frontend.db.model :as model]
             [frontend.fs :as fs]
             [frontend.handler.common.page :as page-common-handler]
             [frontend.handler.db-based.page :as db-page-handler]
@@ -25,7 +24,6 @@
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.mobile.util :as mobile-util]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state :as state]
@@ -324,8 +322,7 @@
                (not config/publishing?))
       (state/set-today! (date/today))
       (when (or (config/db-based-graph? repo)
-                (config/local-file-based-graph? repo)
-                (and (= config/demo-repo repo) (not (mobile-util/native-platform?))))
+                (config/local-file-based-graph? repo))
         (let [title (date/today)
               today-page (util/page-name-sanity-lc title)
               format (state/get-preferred-format repo)
@@ -338,10 +335,9 @@
                           (when-not db-based? (state/pub-event! [:journal/insert-template today-page]))
                           (ui-handler/re-render-root!)
                           (plugin-handler/hook-plugin-app :today-journal-created {:title today-page})))]
-          (when (db/page-empty? repo today-page)
+          (when-not (db/get-page today-page)
             (if db-based?
-              (when-not (model/get-journal-page title)
-                (create-f))
+              (create-f)
               (p/let [file-name (date/journal-title->default title)
                       file-rpath (str (config/get-journals-directory) "/" file-name "."
                                       (config/get-file-extension format))

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

@@ -133,7 +133,7 @@
   "Notice: the first parameter needs to be a `config` with `id`, optional `sidebar?`, `whiteboard?`"
   {:init (fn [state]
            (let [config (first (:rum/args state))
-                 key (select-keys config [:id :sidebar? :whiteboard? :embed? :custom-query? :query :current-block :table?])
+                 key (select-keys config [:id :sidebar? :whiteboard? :embed? :custom-query? :query :current-block :table? :block? :db/id :page-name])
                  container-id (or (:container-id config) (state/get-container-id key))]
              (assoc state :container-id container-id)))})
 

+ 16 - 16
src/main/frontend/mobile/deeplink.cljs

@@ -1,18 +1,15 @@
 (ns frontend.mobile.deeplink
-  (:require
-   [clojure.string :as string]
-   [frontend.config :as config]
-   [frontend.db.async :as db-async]
-   [frontend.db.model :as db-model]
-   [frontend.handler.editor :as editor-handler]
-   [frontend.handler.notification :as notification]
-   [frontend.handler.route :as route-handler]
-   [frontend.mobile.intent :as intent]
-   [frontend.state :as state]
-   [frontend.util.text :as text-util]
-   [goog :refer [Uri]]
-   [logseq.common.util :as common-util]
-   [promesa.core :as p]))
+  (:require [clojure.string :as string]
+            [frontend.config :as config]
+            [frontend.db.async :as db-async]
+            [frontend.handler.notification :as notification]
+            [frontend.handler.route :as route-handler]
+            [frontend.mobile.intent :as intent]
+            [frontend.state :as state]
+            [frontend.util.text :as text-util]
+            [goog :refer [Uri]]
+            [logseq.common.util :as common-util]
+            [promesa.core :as p]))
 
 (def *link-to-another-graph (atom false))
 
@@ -55,8 +52,11 @@
              (fn []
                (cond
                  page-name
-                 (let [db-page-name (db-model/get-redirect-page-name page-name)]
-                   (editor-handler/insert-first-page-block-if-not-exists! db-page-name))
+                 (p/let [block (db-async/<get-block (state/get-current-repo) page-name {:children? false})]
+                   (if block
+                     (route-handler/redirect-to-page! block-uuid)
+                     (notification/show! (str "Open link failed. Page `" page-name "` doesn't exist in the graph."
+                                              :result block) :error false)))
 
                  block-uuid
                  (p/let [block (db-async/<get-block (state/get-current-repo) block-uuid {:children? false})]

+ 2 - 0
src/main/frontend/modules/instrumentation/sentry.cljs

@@ -26,6 +26,8 @@
                     (assoc :revision config/revision))}
    ;; :integrations [(new posthog/SentryIntegration posthog "logseq" 5311485)
    ;;                (new BrowserTracing)]
+   :ignoreErrors ["ResizeObserver loop limit exceeded"
+                  "ResizeObserver loop completed with undelivered notifications"]
    :debug config/dev?
    :tracesSampleRate 1.0
    :beforeSend (fn [^js event]

+ 10 - 12
src/main/frontend/state.cljs

@@ -160,7 +160,6 @@
       :editor/in-composition?                false
       :editor/content                        (atom {})
       :editor/block                          (atom nil)
-      :editor/block-dom-id                   (atom nil)
       :editor/set-timestamp-block            (atom nil) ;; click rendered block timestamp-cp to set timestamp
       :editor/last-input-time                (atom {})
       :editor/document-mode?                 document-mode?
@@ -217,6 +216,7 @@
       ;; assets
       :assets/alias-enabled?                 (or (storage/get :assets/alias-enabled?) false)
       :assets/alias-dirs                     (or (storage/get :assets/alias-dirs) [])
+      :assets/asset-file-write-finish        (atom {})
 
       ;; mobile
       :mobile/container-urls                 nil
@@ -1076,9 +1076,9 @@ Similar to re-frame subscriptions"
   []
   (or @(get @state :selection/start-block)
       (when-let [edit-block (get-edit-block)]
-        (let [id (str "ls-block-" (:block/uuid edit-block))]
-          (set-selection-start-block! id)
-          id))))
+        (let [node (util/rec-get-node edit-block "ls-block")]
+          (set-selection-start-block! node)
+          node))))
 
 (defn get-cursor-range
   []
@@ -1469,14 +1469,6 @@ Similar to re-frame subscriptions"
         (util/set-theme-light)
         (util/set-theme-dark)))))
 
-(defn set-editing-block-dom-id!
-  [block-dom-id]
-  (set-state! :editor/block-dom-id block-dom-id))
-
-(defn get-editing-block-dom-id
-  []
-  @(:editor/block-dom-id @state))
-
 (defn set-root-component!
   [component]
   (set-state! :ui/root-component component))
@@ -1936,6 +1928,12 @@ Similar to re-frame subscriptions"
   []
   @(:editor/args @state))
 
+(defn get-editor-block-container
+  []
+  (some-> (get-edit-input-id)
+          (gdom/getElement)
+          (util/rec-get-node "ls-block")))
+
 (defn set-page-blocks-cp!
   [value]
   (set-state! [:view/components :page-blocks] value))

+ 10 - 14
src/main/frontend/util.cljc

@@ -244,17 +244,17 @@
    (defn set-theme-light
      []
      (p/do!
-       (.setStyle StatusBar (clj->js {:style (.-Light Style)}))
-       (when (mobile-util/native-android?)
-         (.setBackgroundColor StatusBar (clj->js {:color "#ffffff"}))))))
+      (.setStyle StatusBar (clj->js {:style (.-Light Style)}))
+      (when (mobile-util/native-android?)
+        (.setBackgroundColor StatusBar (clj->js {:color "#ffffff"}))))))
 
 #?(:cljs
    (defn set-theme-dark
      []
      (p/do!
-       (.setStyle StatusBar (clj->js {:style (.-Dark Style)}))
-       (when (mobile-util/native-android?)
-         (.setBackgroundColor StatusBar (clj->js {:color "#000000"}))))))
+      (.setStyle StatusBar (clj->js {:style (.-Dark Style)}))
+      (when (mobile-util/native-android?)
+        (.setBackgroundColor StatusBar (clj->js {:color "#000000"}))))))
 
 (defn find-first
   [pred coll]
@@ -742,11 +742,9 @@
 
 #?(:cljs
    (defn get-nodes-between-two-nodes
-     [id1 id2 class]
+     [node-1 node-2 class]
      (when-let [nodes (array-seq (js/document.getElementsByClassName class))]
-       (let [node-1 (gdom/getElement id1)
-             node-2 (gdom/getElement id2)
-             idx-1 (.indexOf nodes node-1)
+       (let [idx-1 (.indexOf nodes node-1)
              idx-2 (.indexOf nodes node-2)
              start (min idx-1 idx-2)
              end (inc (max idx-1 idx-2))]
@@ -754,11 +752,9 @@
 
 #?(:cljs
    (defn get-direction-between-two-nodes
-     [id1 id2 class]
+     [node-1 node-2 class]
      (when-let [nodes (array-seq (js/document.getElementsByClassName class))]
-       (let [node-1 (gdom/getElement id1)
-             node-2 (gdom/getElement id2)
-             idx-1 (.indexOf nodes node-1)
+       (let [idx-1 (.indexOf nodes node-1)
              idx-2 (.indexOf nodes node-2)]
          (if (>= idx-1 idx-2)
            :up

+ 2 - 3
src/main/frontend/worker/db/migrate.cljs

@@ -7,6 +7,7 @@
             [datascript.core :as d]
             [datascript.impl.entity :as de]
             [frontend.worker.search :as search]
+            [frontend.worker.util :as worker-util]
             [logseq.common.config :as common-config]
             [logseq.common.util :as common-util]
             [logseq.common.util.date-time :as date-time-util]
@@ -859,7 +860,6 @@
                        :logseq.task/scheduled :logseq.property/scheduled})
    conn search-db))
 
-
 (defn- empty-placeholder-add-block-uuid
   [_conn _search-db]
   [{:db/ident :logseq.property/empty-placeholder
@@ -1154,8 +1154,7 @@
         nil
 
         (neg? compare-result) ; outdated client, db version could be synced from server
-        ;; FIXME: notify users to upgrade to the latest version asap
-        nil
+        (worker-util/post-message :notification ["Your app is using an outdated version that is incompatible with your current graph. Please update your app before editing this graph." :error false])
 
         (pos? compare-result)
         (try

+ 5 - 2
src/main/frontend/worker/db_worker.cljs

@@ -307,9 +307,11 @@
                   compare-result (when version-in-db (db-schema/compare-schema-version version-in-db "64.8"))]
               (when (and compare-result (not (neg? compare-result))) ; >= 64.8
                 (worker-util/post-message :capture-error
-                                          {:error "db-missing-addresses-v2"
+                                          {:error "db-missing-addresses-v3"
                                            :payload {:missing-addresses (str missing-addresses)
-                                                     :db-schema-version (str version-in-db)}})))
+                                                     :db-schema-version (str version-in-db)
+                                                     :graph-git-sha (when conn
+                                                                      (:kv/value (:logseq.kv/graph-git-sha @conn)))}})))
             (worker-util/post-message :notification ["It seems that the DB has been broken. Please run the command `Fix current broken graph`." :error false])
             (throw (ex-info "DB missing addresses" {:missing-addresses missing-addresses}))))
 
@@ -679,6 +681,7 @@
       (sqlite-export/build-export @conn options)
       (catch :default e
         (js/console.error "export-edn error: " e)
+        (js/console.error "Stack:\n" (.-stack e))
         (worker-util/post-message :notification
                                   ["An unexpected error occurred during export. See the javascript console for details."
                                    :error])

+ 1 - 1
src/main/logseq/api.cljs

@@ -844,7 +844,7 @@
     (let [block (state/get-edit-block)
           block (or block
                     (some-> (or (first (state/get-selection-blocks))
-                                (gdom/getElement (state/get-editing-block-dom-id)))
+                                (state/get-editor-block-container))
                             (.getAttribute "blockid")
                             (db-model/get-block-by-uuid)))]
       (get_block (:block/uuid block) opts))))