소스 검색

fix: don't parse parent from namespace pages

Tienson Qin 1 년 전
부모
커밋
fb2cc14444

+ 41 - 30
deps/common/src/logseq/common/date.cljs

@@ -2,44 +2,51 @@
   "Date related fns shared by worker and frontend namespaces. Eventually some
    of this should go to logseq.common.util.date-time"
   (:require [cljs-time.format :as tf]
-            [logseq.common.util :as common-util]))
+            [logseq.common.util :as common-util]
+            [clojure.string :as string]))
 
 (def default-journal-filename-formatter "yyyy_MM_dd")
 
+(defonce built-in-journal-title-formatters
+  (list
+   "do MMM yyyy"
+   "do MMMM yyyy"
+   "MMM do, yyyy"
+   "MMMM do, yyyy"
+   "E, dd-MM-yyyy"
+   "E, dd.MM.yyyy"
+   "E, MM/dd/yyyy"
+   "E, yyyy/MM/dd"
+   "EEE, dd-MM-yyyy"
+   "EEE, dd.MM.yyyy"
+   "EEE, MM/dd/yyyy"
+   "EEE, yyyy/MM/dd"
+   "EEEE, dd-MM-yyyy"
+   "EEEE, dd.MM.yyyy"
+   "EEEE, MM/dd/yyyy"
+   "EEEE, yyyy/MM/dd"
+   "dd-MM-yyyy"
+     ;; This tyle will mess up other date formats like "2022-08" "2022Q4" "2022/10"
+     ;;  "dd.MM.yyyy"
+   "MM/dd/yyyy"
+   "MM-dd-yyyy"
+   "MM_dd_yyyy"
+   "yyyy/MM/dd"
+   "yyyy-MM-dd"
+   "yyyy-MM-dd EEEE"
+   "yyyy_MM_dd"
+   "yyyyMMdd"
+   "yyyy年MM月dd日"))
+
+(defonce slash-journal-title-formatters
+  (filter #(string/includes? % "/") built-in-journal-title-formatters))
+
 (defn journal-title-formatters
   [date-formatter]
   (->
    (cons
     date-formatter
-    (list
-     "do MMM yyyy"
-     "do MMMM yyyy"
-     "MMM do, yyyy"
-     "MMMM do, yyyy"
-     "E, dd-MM-yyyy"
-     "E, dd.MM.yyyy"
-     "E, MM/dd/yyyy"
-     "E, yyyy/MM/dd"
-     "EEE, dd-MM-yyyy"
-     "EEE, dd.MM.yyyy"
-     "EEE, MM/dd/yyyy"
-     "EEE, yyyy/MM/dd"
-     "EEEE, dd-MM-yyyy"
-     "EEEE, dd.MM.yyyy"
-     "EEEE, MM/dd/yyyy"
-     "EEEE, yyyy/MM/dd"
-     "dd-MM-yyyy"
-     ;; This tyle will mess up other date formats like "2022-08" "2022Q4" "2022/10"
-     ;;  "dd.MM.yyyy"
-     "MM/dd/yyyy"
-     "MM-dd-yyyy"
-     "MM_dd_yyyy"
-     "yyyy/MM/dd"
-     "yyyy-MM-dd"
-     "yyyy-MM-dd EEEE"
-     "yyyy_MM_dd"
-     "yyyyMMdd"
-     "yyyy年MM月dd日"))
+    built-in-journal-title-formatters)
    (distinct)))
 
 (defn normalize-date
@@ -70,6 +77,10 @@
   [title date-formatter]
   (boolean (normalize-journal-title title date-formatter)))
 
+(defn ^:api valid-journal-title-with-slash?
+  [title]
+  (some #(valid-journal-title? title %) slash-journal-title-formatters))
+
 (defn ^:api date->file-name
   "Date object to filename format"
   [date journal-filename-formatter]

+ 8 - 1
deps/common/src/logseq/common/util/namespace.cljs

@@ -17,4 +17,11 @@
        (not= (string/trim page-name) namespace-char)
        (not (string/starts-with? page-name "../"))
        (not (string/starts-with? page-name "./"))
-       (not (common-util/url? page-name))))
+       (not (common-util/url? page-name))))
+
+(defn get-last-part
+  "Get last part of a namespace page"
+  [page-name]
+  (if (namespace-page? page-name)
+    (last (string/split page-name parent-char))
+    page-name))

+ 2 - 1
deps/db/src/logseq/db.cljs

@@ -624,7 +624,8 @@
   [entity]
   (if (contains? #{"page" "class"} (:block/type entity))
     (let [parents' (->> (get-page-parents entity)
-                        (remove (fn [e] (= :logseq.class/Root (:db/ident e)))))]
+                        (remove (fn [e] (= :logseq.class/Root (:db/ident e))))
+                        vec)]
       (string/join
        ns-util/parent-char
        (map :block/title (conj (vec parents') entity))))

+ 30 - 17
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -17,7 +17,8 @@
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.utf8 :as utf8]
-            [logseq.db.frontend.class :as db-class]))
+            [logseq.db.frontend.class :as db-class]
+            [logseq.common.date :as common-date]))
 
 (defn heading-block?
   [block]
@@ -235,7 +236,7 @@
                                            v' (text/parse-property k v mldoc-ast user-config)]
                                        [k' v' mldoc-ast v])
                                      (do (swap! *invalid-properties conj k)
-                                       nil)))))
+                                         nil)))))
                           (remove #(nil? (second %))))
           page-refs (get-page-ref-names-from-properties properties user-config)
           block-refs (extract-block-refs properties)
@@ -387,6 +388,14 @@
         (let [type (if class? "class" (or (:block/type page) "page"))]
           (assoc page :block/type type))))))
 
+(defn- db-invalid-namespace-page?
+  "Namespace page neither exists nor journal"
+  [db db-based? page]
+  (and db-based?
+       (not (ldb/get-page db page))
+       (text/namespace-page? page)
+       (not (common-date/valid-journal-title-with-slash? page))))
+
 (defn- with-page-refs-and-tags
   [{:keys [title body tags refs marker priority] :as block} db date-formatter parse-block]
   (let [db-based? (ldb/db-based-graph? db)
@@ -402,12 +411,16 @@
                       (= (first form) "Custom")
                       (= (second form) "query"))
          (when-let [page (get-page-reference form (:format block))]
-           (swap! *refs conj page))
+           (when-let [page' (when-not (db-invalid-namespace-page? db db-based? page)
+                              page)]
+             (swap! *refs conj page')))
          (when-let [tag (get-tag form)]
            (let [tag (text/page-ref-un-brackets! tag)]
-             (when (common-util/tag-valid? tag)
-               (swap! *refs conj tag)
-               (swap! *structured-tags conj tag))))
+             (when-let [tag' (when-not (db-invalid-namespace-page? db db-based? tag)
+                               tag)]
+               (when (common-util/tag-valid? tag')
+                 (swap! *refs conj tag')
+                 (swap! *structured-tags conj tag')))))
          form))
      (concat title body))
     (swap! *refs #(remove string/blank? %))
@@ -450,10 +463,10 @@
                     (remove nil?)
                     (map (fn [ref]
                            (let [ref' (if-let [entity (ldb/get-case-page db (:block/title ref))]
-                              (if (= (:db/id parse-block) (:db/id entity))
-                                ref
-                                (select-keys entity [:block/uuid :block/title :block/name]))
-                              ref)]
+                                        (if (= (:db/id parse-block) (:db/id entity))
+                                          ref
+                                          (select-keys entity [:block/uuid :block/title :block/name]))
+                                        ref)]
                              (cond-> ref'
                                (:block.temp/original-page-name ref)
                                (assoc :block.temp/original-page-name (:block.temp/original-page-name ref)))))))
@@ -656,13 +669,13 @@
       (update :block/properties-text-values dissoc :id)
       (update :block/properties-order #(vec (remove #{:id} %)))
       (update :block/title (fn [c]
-                         (let [replace-str (re-pattern
-                                            (str
-                                             "\n*\\s*"
-                                             (if (= :markdown (:block/format block))
-                                               (str "id" gp-property/colons " " (:block/uuid block))
-                                               (str (gp-property/colons-org "id") " " (:block/uuid block)))))]
-                           (string/replace-first c replace-str ""))))))
+                             (let [replace-str (re-pattern
+                                                (str
+                                                 "\n*\\s*"
+                                                 (if (= :markdown (:block/format block))
+                                                   (str "id" gp-property/colons " " (:block/uuid block))
+                                                   (str (gp-property/colons-org "id") " " (:block/uuid block)))))]
+                               (string/replace-first c replace-str ""))))))
 
 (defn block-exists-in-another-page?
   "For sanity check only.

+ 2 - 1
deps/graph-parser/src/logseq/graph_parser/text.cljs

@@ -150,4 +150,5 @@
             new-val
             v'))))))
 
-(def namespace-page? ns-util/namespace-page?)
+(def namespace-page? ns-util/namespace-page?)
+(def get-namespace-last-part ns-util/get-last-part)

+ 3 - 28
deps/outliner/src/logseq/outliner/core.cljs

@@ -320,32 +320,10 @@
       pages)
      (remove nil?))))
 
-(defn- build-page-parents
-  [db m date-formatter raw-title]
-  (let [refs (:block/refs m)
-        tags (:block/tags m)
-        *changed-uuids (atom {})
-        refs' (split-namespace-pages db refs tags date-formatter
-                                     {:*changed-uuids *changed-uuids})
-        tags' (map (fn [tag]
-                     (or (first (filter (fn [ref] (= (:block/uuid ref) (:block/uuid tag))) refs')) tag))
-                   tags)
-        raw-title' (if (seq @*changed-uuids)
-                     (reduce
-                      (fn [raw-content [old-id new-id]]
-                        (string/replace raw-content (str old-id) (str new-id)))
-                      raw-title
-                      @*changed-uuids)
-                     raw-title)]
-    (assoc m
-           :block/refs refs'
-           :block/title raw-title'
-           :block/tags tags')))
-
 (extend-type Entity
   otree/INode
-  (-save [this txs-state conn repo date-formatter {:keys [retract-attributes? retract-attributes]
-                                                   :or {retract-attributes? true}}]
+  (-save [this txs-state conn repo _date-formatter {:keys [retract-attributes? retract-attributes]
+                                                    :or {retract-attributes? true}}]
     (assert (ds/outliner-txs-state? txs-state)
             "db should be satisfied outliner-tx-state?")
     (let [data this
@@ -386,10 +364,7 @@
               (outliner-validate/validate-block-title db (:block/title m*) block-entity))
           m (cond-> m*
               db-based?
-              (dissoc :block/pre-block? :block/priority :block/marker :block/properties-order))
-          m (if db-based?
-              (build-page-parents db m date-formatter (:block/title m))
-              m)]
+              (dissoc :block/pre-block? :block/priority :block/marker :block/properties-order))]
       ;; Ensure block UUID never changes
       (let [e (d/entity db db-id)]
         (when (and e block-uuid)

+ 7 - 1
src/main/frontend/components/block.cljs

@@ -772,7 +772,7 @@
   "Component for a page. `page` argument contains :block/name which can be (un)sanitized page name.
    Keys for `config`:
    - `:preview?`: Is this component under preview mode? (If true, `page-preview-trigger` won't be registered to this `page-cp`)"
-  [state {:keys [label children preview? disable-preview? show-non-exists-page?] :as config} page]
+  [state {:keys [label children preview? disable-preview? show-non-exists-page? tag?] :as config} page]
   (let [entity (if (e/entity? page)
                  page
                  ;; Use uuid when available to uniquely identify case sensitive contexts
@@ -811,6 +811,12 @@
         (page-inner config {:block/title (:block/name page)
                             :block/name (:block/name page)} children label)
 
+        (:block/name page)
+        [:span (str (when tag? "#")
+                    (when-not tag? page-ref/left-brackets)
+                    (:block/name page)
+                    (when-not tag? page-ref/right-brackets))]
+
         :else
         nil))))
 

+ 115 - 116
src/main/frontend/components/cmdk/core.cljs

@@ -1,44 +1,43 @@
 (ns frontend.components.cmdk.core
-  (:require
-   [cljs-bean.core :as bean]
-   [clojure.string :as string]
-   [frontend.components.block :as block]
-   [frontend.components.cmdk.list-item :as list-item]
-   [frontend.components.title :as title]
-   [frontend.extensions.pdf.utils :as pdf-utils]
-   [frontend.context.i18n :refer [t]]
-   [frontend.db :as db]
-   [frontend.db.model :as model]
-   [frontend.handler.command-palette :as cp-handler]
-   [frontend.handler.editor :as editor-handler]
-   [frontend.handler.page :as page-handler]
-   [frontend.handler.route :as route-handler]
-   [frontend.handler.whiteboard :as whiteboard-handler]
-   [frontend.handler.notification :as notification]
-   [frontend.modules.shortcut.core :as shortcut]
-   [frontend.handler.db-based.page :as db-page-handler]
-   [frontend.search :as search]
-   [frontend.state :as state]
-   [frontend.ui :as ui]
-   [frontend.util :as util]
-   [frontend.util.page :as page-util]
-   [goog.functions :as gfun]
-   [goog.object :as gobj]
-   [logseq.shui.ui :as shui]
-   [promesa.core :as p]
-   [rum.core :as rum]
-   [frontend.mixins :as mixins]
-   [logseq.common.util.block-ref :as block-ref]
-   [logseq.common.util :as common-util]
-   [frontend.modules.shortcut.utils :as shortcut-utils]
-   [frontend.config :as config]
-   [logseq.common.path :as path]
-   [electron.ipc :as ipc]
-   [frontend.util.text :as text-util]
-   [goog.userAgent]
-   [frontend.db.async :as db-async]
-   [logseq.db :as ldb]
-   [logseq.common.util.namespace :as ns-util]))
+  (:require [cljs-bean.core :as bean]
+            [clojure.string :as string]
+            [electron.ipc :as ipc]
+            [frontend.components.block :as block]
+            [frontend.components.cmdk.list-item :as list-item]
+            [frontend.components.title :as title]
+            [frontend.config :as config]
+            [frontend.context.i18n :refer [t]]
+            [frontend.db :as db]
+            [frontend.db.async :as db-async]
+            [frontend.db.model :as model]
+            [frontend.extensions.pdf.utils :as pdf-utils]
+            [frontend.handler.command-palette :as cp-handler]
+            [frontend.handler.db-based.page :as db-page-handler]
+            [frontend.handler.editor :as editor-handler]
+            [frontend.handler.notification :as notification]
+            [frontend.handler.page :as page-handler]
+            [frontend.handler.route :as route-handler]
+            [frontend.handler.whiteboard :as whiteboard-handler]
+            [frontend.mixins :as mixins]
+            [frontend.modules.shortcut.core :as shortcut]
+            [frontend.modules.shortcut.utils :as shortcut-utils]
+            [frontend.search :as search]
+            [frontend.state :as state]
+            [frontend.ui :as ui]
+            [frontend.util :as util]
+            [frontend.util.page :as page-util]
+            [frontend.util.text :as text-util]
+            [goog.functions :as gfun]
+            [goog.object :as gobj]
+            [goog.userAgent]
+            [logseq.common.path :as path]
+            [logseq.common.util :as common-util]
+            [logseq.common.util.block-ref :as block-ref]
+            [logseq.db :as ldb]
+            [logseq.graph-parser.text :as text]
+            [logseq.shui.ui :as shui]
+            [promesa.core :as p]
+            [rum.core :as rum]))
 
 (defn translate [t {:keys [id desc]}]
   (when id
@@ -84,7 +83,7 @@
                      (str "Create class called '" (get-class-from-input q) "'")
                      (str "Create page called '" q "'"))
              :source-create :page}]
-        (remove nil?)))))
+           (remove nil?)))))
 
 ;; Take the results, decide how many items to show, and order the results appropriately
 (defn state->results-ordered [state search-mode]
@@ -107,7 +106,7 @@
                             (take 5 items))))
         node-exists? (let [blocks-result (keep :source-block (get-in results [:nodes :items]))]
                        (when-not (string/blank? input)
-                         (or (some-> (last (string/split input ns-util/parent-char))
+                         (or (some-> (text/get-namespace-last-part input)
                                      string/trim
                                      db/get-page)
                              (some (fn [block]
@@ -159,9 +158,9 @@
 
 (defn state->highlighted-item [state]
   (or (some-> state ::highlighted-item deref)
-    (some->> (state->results-ordered state (:search/mode @state/state))
-      (mapcat last)
-      (first))))
+      (some->> (state->results-ordered state (:search/mode @state/state))
+               (mapcat last)
+               (first))))
 
 (defn state->action [state]
   (let [highlighted-item (state->highlighted-item state)]
@@ -181,12 +180,12 @@
 (defmethod load-results :initial [_ state]
   (let [!results (::results state)
         command-items (->> (cp-handler/top-commands 100)
-                        (remove (fn [c] (= :window/close (:id c))))
-                        (map #(hash-map :icon "command"
-                                :icon-theme :gray
-                                :text (translate t %)
-                                :shortcut (:shortcut %)
-                                :source-command %)))]
+                           (remove (fn [c] (= :window/close (:id c))))
+                           (map #(hash-map :icon "command"
+                                           :icon-theme :gray
+                                           :text (translate t %)
+                                           :shortcut (:shortcut %)
+                                           :source-command %)))]
     (reset! !results (assoc-in default-results [:commands :items] command-items))))
 
 ;; The commands search uses the command-palette handler
@@ -195,18 +194,18 @@
         !results (::results state)]
     (swap! !results assoc-in [group :status] :loading)
     (let [commands (->> (cp-handler/top-commands 1000)
-                     (map #(assoc % :t (translate t %))))
+                        (map #(assoc % :t (translate t %))))
           search-results (if (string/blank? @!input)
                            commands
                            (search/fuzzy-search commands @!input {:extract-fn :t}))]
       (->> search-results
-        (map #(hash-map :icon "command"
-                :icon-theme :gray
-                :text (translate t %)
-                :shortcut (:shortcut %)
-                :source-command %))
-        (hash-map :status :success :items)
-        (swap! !results update group merge)))))
+           (map #(hash-map :icon "command"
+                           :icon-theme :gray
+                           :text (translate t %)
+                           :shortcut (:shortcut %)
+                           :source-command %))
+           (hash-map :status :success :items)
+           (swap! !results update group merge)))))
 
 (defn highlight-content-query
   "Return hiccup of highlighted content FTS result"
@@ -287,22 +286,22 @@
     (swap! !results assoc-in [group :status] :loading)
     (p/let [files* (search/file-search @!input 99)
             files (remove
-                    (fn [f]
-                      (and
-                        f
-                        (string/ends-with? f ".edn")
-                        (or (string/starts-with? f "whiteboards/")
+                   (fn [f]
+                     (and
+                      f
+                      (string/ends-with? f ".edn")
+                      (or (string/starts-with? f "whiteboards/")
                           (string/starts-with? f "assets/")
                           (string/starts-with? f "logseq/version-files")
                           (contains? #{"logseq/metadata.edn" "logseq/pages-metadata.edn" "logseq/graphs-txid.edn"} f))))
-                    files*)
+                   files*)
             items (map
-                    (fn [file]
-                      (hash-map :icon "file"
-                        :icon-theme :gray
-                        :text file
-                        :file-path file))
-                    files)]
+                   (fn [file]
+                     (hash-map :icon "file"
+                               :icon-theme :gray
+                               :text file
+                               :file-path file))
+                   files)]
       (swap! !results update group merge {:status :success :items items}))))
 
 (defmethod load-results :themes [group _state]
@@ -328,7 +327,7 @@
   [input]
   (or (when (string/starts-with? input "/")
         (subs input 1))
-    (last (common-util/split-last "/" input))))
+      (last (common-util/split-last "/" input))))
 
 (defmethod load-results :filters [group state]
   (let [!results (::results state)
@@ -368,7 +367,7 @@
 (defmethod load-results :default [_ state]
   (let [filter-group (:group @(::filter state))]
     (if (and (not (some-> state ::input deref seq))
-          (not filter-group))
+             (not filter-group))
       (do (load-results :initial state)
           (load-results :filters state))
       (if filter-group
@@ -443,8 +442,8 @@
 (defn- open-file
   [file-path]
   (if (or (string/ends-with? file-path ".edn")
-        (string/ends-with? file-path ".js")
-        (string/ends-with? file-path ".css"))
+          (string/ends-with? file-path ".js")
+          (string/ends-with? file-path ".css"))
     (route-handler/redirect! {:to :file
                               :path-params {:path file-path}})
     ;; open this file in directory
@@ -547,14 +546,14 @@
         b2 (.-bottom target-rect)]
     (when-not (<= t1 t2 b2 b1)          ; not visible
       (.scrollIntoView target
-        #js {:inline "nearest"
-             :behavior "smooth"}))))
+                       #js {:inline "nearest"
+                            :behavior "smooth"}))))
 
 (rum/defc mouse-active-effect!
   [*mouse-active? deps]
   (rum/use-effect!
-    #(reset! *mouse-active? false)
-    deps)
+   #(reset! *mouse-active? false)
+   deps)
   nil)
 
 (rum/defcs result-group
@@ -828,13 +827,13 @@
 (defn rand-tip
   []
   (rand-nth
-    [[:div.flex.flex-row.gap-1.items-center.opacity-50.hover:opacity-100
-      [:div "Type"]
-      (shui/shortcut "/")
-      [:div "to filter search results"]]
-     [:div.flex.flex-row.gap-1.items-center.opacity-50.hover:opacity-100
-      (shui/shortcut ["mod" "enter"])
-      [:div "to open search in the sidebar"]]]))
+   [[:div.flex.flex-row.gap-1.items-center.opacity-50.hover:opacity-100
+     [:div "Type"]
+     (shui/shortcut "/")
+     [:div "to filter search results"]]
+    [:div.flex.flex-row.gap-1.items-center.opacity-50.hover:opacity-100
+     (shui/shortcut ["mod" "enter"])
+     [:div "to open search in the sidebar"]]]))
 
 (rum/defcs tip <
   {:init (fn [state]
@@ -854,31 +853,31 @@
 (rum/defc hint-button
   [text shortcut opts]
   (shui/button
-    (merge {:class "hint-button [&>span:first-child]:hover:opacity-100 opacity-40 hover:opacity-80"
-            :variant :ghost
-            :size  :sm}
-      opts)
-    [[:span.opacity-60 text]
+   (merge {:class "hint-button [&>span:first-child]:hover:opacity-100 opacity-40 hover:opacity-80"
+           :variant :ghost
+           :size  :sm}
+          opts)
+   [[:span.opacity-60 text]
      ;; shortcut
-     (when (not-empty shortcut)
-       (for [key shortcut]
-         [:div.ui__button-shortcut-key
-          (case key
-            "cmd" [:div (if goog.userAgent/MAC "⌘" "Ctrl")]
-            "shift" [:div "⇧"]
-            "return" [:div "⏎"]
-            "esc" [:div.tracking-tightest {:style {:transform   "scaleX(0.8) scaleY(1.2) "
-                                                   :font-size   "0.5rem"
-                                                   :font-weight "500"}} "ESC"]
-            (cond-> key (string? key) .toUpperCase))]))]))
+    (when (not-empty shortcut)
+      (for [key shortcut]
+        [:div.ui__button-shortcut-key
+         (case key
+           "cmd" [:div (if goog.userAgent/MAC "⌘" "Ctrl")]
+           "shift" [:div "⇧"]
+           "return" [:div "⏎"]
+           "esc" [:div.tracking-tightest {:style {:transform   "scaleX(0.8) scaleY(1.2) "
+                                                  :font-size   "0.5rem"
+                                                  :font-weight "500"}} "ESC"]
+           (cond-> key (string? key) .toUpperCase))]))]))
 
 (rum/defc hints
   [state]
   (let [action (state->action state)
         button-fn (fn [text shortcut & {:as opts}]
                     (hint-button text shortcut
-                      {:on-click #(handle-action action (assoc state :opts opts) %)
-                       :muted    true}))]
+                                 {:on-click #(handle-action action (assoc state :opts opts) %)
+                                  :muted    true}))]
     (when action
       [:div.hints
        [:div.text-sm.leading-6
@@ -918,12 +917,12 @@
    [:div "Search only:"]
    [:div group-name]
    (shui/button
-     {:variant  :ghost
-      :size     :icon
-      :class    "p-1 scale-75"
-      :on-click (fn []
-                  (reset! (::filter state) nil))}
-     (shui/tabler-icon "x"))])
+    {:variant  :ghost
+     :size     :icon
+     :class    "p-1 scale-75"
+     :on-click (fn []
+                 (reset! (::filter state) nil))}
+    (shui/tabler-icon "x"))])
 
 (rum/defcs cmdk
   < rum/static
@@ -1003,12 +1002,12 @@
                    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")])))]
+            (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]

+ 21 - 14
src/main/frontend/components/title.cljs

@@ -1,23 +1,30 @@
 (ns frontend.components.title
   (:require [clojure.string :as string]
             [frontend.db :as db]
-            [logseq.db :as ldb]))
+            [logseq.db :as ldb]
+            [datascript.impl.entity :as de]))
 
 (defn block-unique-title
   "Multiple pages/objects may have the same `:block/title`.
    Notice: this doesn't prevent for pages/objects that have the same tag or created by different clients."
   [block]
-  (if (and (seq (:block/tags block))
-           (not (ldb/journal? block)))
-    (str (:block/title block)
-         " "
-         (string/join
-          ", "
-          (keep (fn [tag]
-                  (let [tag (if (number? tag)
-                              (db/entity tag)
-                              tag)]
+  (let [block-e (cond
+                  (de/entity? block)
+                  block
+                  (uuid? (:block/uuid block))
+                  (db/entity [:block/uuid (:block/uuid block)])
+                  :else
+                  block)
+        tags (remove (fn [t] (some-> (:block/raw-title block-e) (ldb/inline-tag? t)))
+                     (map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))]
+    (if (and (seq tags)
+             (not (ldb/journal? block)))
+      (str (:block/title block)
+           " "
+           (string/join
+            ", "
+            (keep (fn [tag]
                     (when-let [title (:block/title tag)]
-                      (str "#" title))))
-                (:block/tags block))))
-    (:block/title block)))
+                      (str "#" title)))
+                  tags)))
+      (:block/title block))))

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

@@ -45,9 +45,8 @@
             [logseq.common.util.page-ref :as page-ref]
             [logseq.db :as ldb]
             [logseq.graph-parser.db :as gp-db]
-            [promesa.core :as p]
             [logseq.graph-parser.text :as text]
-            [logseq.common.util.namespace :as ns-util]))
+            [promesa.core :as p]))
 
 (def <create! page-common-handler/<create!)
 (def <delete! page-common-handler/<delete!)
@@ -322,7 +321,7 @@
                      (string/replace-first (str (t :new-page) " ") ""))
           wrapped? (= page-ref/left-brackets (common-util/safe-subs edit-content (- pos 2) pos))
           chosen-last-part (if (text/namespace-page? chosen)
-                             (last (string/split chosen ns-util/parent-char))
+                             (text/get-namespace-last-part chosen)
                              chosen)
           wrapped-tag (if (and (util/safe-re-find #"\s+" chosen-last-part) (not wrapped?))
                         (page-ref/->page-ref chosen-last-part)

+ 3 - 2
src/main/frontend/worker/search.cljs

@@ -11,7 +11,8 @@
             [logseq.common.util :as common-util]
             [logseq.db :as ldb]
             [clojure.set :as set]
-            [logseq.common.util.namespace :as ns-util]))
+            [logseq.common.util.namespace :as ns-util]
+            [logseq.graph-parser.text :as text]))
 
 ;; TODO: use sqlite for fuzzy search
 ;; maybe https://github.com/nalgeon/sqlean/blob/main/docs/fuzzy.md?
@@ -144,7 +145,7 @@ DROP TRIGGER IF EXISTS blocks_au;
   (try
     (p/let [namespace? (ns-util/namespace-page? q)
             last-part (when namespace?
-                        (some-> (last (string/split q ns-util/parent-char))
+                        (some-> (text/get-namespace-last-part q)
                                 get-match-input))
             bind (cond
                    (and namespace? page)

+ 145 - 145
src/rtc_e2e_test/client_steps.cljs

@@ -12,22 +12,22 @@
 (def ^:private step0
   {:client1
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)
-           tx-data (const/tx-data-map :create-page)]
-       (helper/transact! conn tx-data)
-       (is (=
-            #{[:update-page const/page1-uuid]
-              [:update const/page1-uuid
-               [[:block/title "[\"~#'\",\"basic-edits-test\"]" true]
-                [:block/created-at "[\"~#'\",1724836490809]" true]
-                [:block/updated-at "[\"~#'\",1724836490809]" true]
-                [:block/type "[\"~#'\",\"page\"]" true]]]
-              [:move const/block1-uuid]
-              [:update const/block1-uuid
-               [[:block/updated-at "[\"~#'\",1724836490810]" true]
-                [:block/created-at "[\"~#'\",1724836490810]" true]
-                [:block/title "[\"~#'\",\"block1\"]" true]]]}
-            (set (map helper/simplify-client-op (client-op/get-all-ops const/downloaded-test-repo)))))))
+    (let [conn (helper/get-downloaded-test-conn)
+          tx-data (const/tx-data-map :create-page)]
+      (helper/transact! conn tx-data)
+      (is (=
+           #{[:update-page const/page1-uuid]
+             [:update const/page1-uuid
+              [[:block/title "[\"~#'\",\"basic-edits-test\"]" true]
+               [:block/created-at "[\"~#'\",1724836490809]" true]
+               [:block/updated-at "[\"~#'\",1724836490809]" true]
+               [:block/type "[\"~#'\",\"page\"]" true]]]
+             [:move const/block1-uuid]
+             [:update const/block1-uuid
+              [[:block/updated-at "[\"~#'\",1724836490810]" true]
+               [:block/created-at "[\"~#'\",1724836490810]" true]
+               [:block/title "[\"~#'\",\"block1\"]" true]]]}
+           (set (map helper/simplify-client-op (client-op/get-all-ops const/downloaded-test-repo)))))))
    :client2 nil})
 
 (def ^:private step1
@@ -35,53 +35,53 @@
   client2: start rtc, wait page1, remote->client2"
   {:client1
    (m/sp
-     (let [r (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))]
-       (is (nil? r))
-       (m/? (helper/new-task--wait-all-client-ops-sent))))
+    (let [r (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))]
+      (is (nil? r))
+      (m/? (helper/new-task--wait-all-client-ops-sent))))
    :client2
    (m/sp
-     (let [r (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))]
-       (is (nil? r)))
-     (m/?
-      (c.m/backoff
-       (take 4 c.m/delays)
-       (m/sp
-         (let [conn (helper/get-downloaded-test-conn)
-               page1 (d/pull @conn '[*] [:block/uuid const/page1-uuid])
-               block1 (d/pull @conn '[*] [:block/uuid const/block1-uuid])]
-           (when-not (:block/uuid page1)
-             (throw (ex-info "wait page1 synced" {:missionary/retry true})))
-           (is
-            (= {:block/title "basic-edits-test"
-                :block/name "basic-edits-test"
-                :block/type "page"}
-               (select-keys page1 [:block/title :block/name :block/type])))
-           (is
-            (= {:block/title "block1"
-                :block/order "a0"
-                :block/parent {:db/id (:db/id page1)}}
-               (select-keys block1 [:block/title :block/order :block/parent]))))))))})
+    (let [r (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))]
+      (is (nil? r)))
+    (m/?
+     (c.m/backoff
+      (take 4 c.m/delays)
+      (m/sp
+       (let [conn (helper/get-downloaded-test-conn)
+             page1 (d/pull @conn '[*] [:block/uuid const/page1-uuid])
+             block1 (d/pull @conn '[*] [:block/uuid const/block1-uuid])]
+         (when-not (:block/uuid page1)
+           (throw (ex-info "wait page1 synced" {:missionary/retry true})))
+         (is
+          (= {:block/title "basic-edits-test"
+              :block/name "basic-edits-test"
+              :block/type "page"}
+             (select-keys page1 [:block/title :block/name :block/type])))
+         (is
+          (= {:block/title "block1"
+              :block/order "a0"
+              :block/parent {:db/id (:db/id page1)}}
+             (select-keys block1 [:block/title :block/order :block/parent]))))))))})
 
 (def ^:private step2
   "client1: insert 500 blocks, wait for changes to sync to remote
   client2: wait for blocks to sync from remote"
   {:client1
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)]
-       (helper/transact! conn (const/tx-data-map :insert-500-blocks))
-       (m/? (helper/new-task--wait-all-client-ops-sent))))
+    (let [conn (helper/get-downloaded-test-conn)]
+      (helper/transact! conn (const/tx-data-map :insert-500-blocks))
+      (m/? (helper/new-task--wait-all-client-ops-sent))))
    :client2
    (c.m/backoff
     (take 4 c.m/delays)
     (m/sp
-      (let [conn (helper/get-downloaded-test-conn)
-            page (d/pull @conn '[*] [:block/uuid const/page2-uuid])]
-        (when-not (:block/uuid page)
-          (throw (ex-info "wait page to be synced" {:missionary/retry true})))
-        (let [blocks (ldb/sort-by-order (ldb/get-page-blocks @conn (:db/id page)))]
-          (is (= 500 (count blocks)))
-          (is (= (map #(str "x" %) (range 500))
-                 (map :block/title blocks)))))))})
+     (let [conn (helper/get-downloaded-test-conn)
+           page (d/pull @conn '[*] [:block/uuid const/page2-uuid])]
+       (when-not (:block/uuid page)
+         (throw (ex-info "wait page to be synced" {:missionary/retry true})))
+       (let [blocks (ldb/sort-by-order (ldb/get-page-blocks @conn (:db/id page)))]
+         (is (= 500 (count blocks)))
+         (is (= (map #(str "x" %) (range 500))
+                (map :block/title blocks)))))))})
 
 (def ^:private step3
   "client1:
@@ -95,32 +95,32 @@
   1. wait the block&its properties to be synced"
   {:client1
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)
-           tx-data1 (const/tx-data-map :step3-add-task-properties-to-block1)
-           tx-data2 (const/tx-data-map :step3-toggle-status-TODO)
-           tx-data3 (const/tx-data-map :step3-toggle-status-DOING)]
-       (helper/transact! conn tx-data1)
-       (m/? (helper/new-task--wait-all-client-ops-sent))
-       (helper/transact! conn tx-data2)
-       (m/? (helper/new-task--wait-all-client-ops-sent))
-       (helper/transact! conn tx-data3)
-       (m/? (helper/new-task--wait-all-client-ops-sent))))
+    (let [conn (helper/get-downloaded-test-conn)
+          tx-data1 (const/tx-data-map :step3-add-task-properties-to-block1)
+          tx-data2 (const/tx-data-map :step3-toggle-status-TODO)
+          tx-data3 (const/tx-data-map :step3-toggle-status-DOING)]
+      (helper/transact! conn tx-data1)
+      (m/? (helper/new-task--wait-all-client-ops-sent))
+      (helper/transact! conn tx-data2)
+      (m/? (helper/new-task--wait-all-client-ops-sent))
+      (helper/transact! conn tx-data3)
+      (m/? (helper/new-task--wait-all-client-ops-sent))))
    :client2
    (c.m/backoff
     (take 4 c.m/delays)
     (m/sp
-      (let [conn (helper/get-downloaded-test-conn)
-            block1 (d/pull @conn
-                           [{:block/tags [:db/ident]}
-                            {:logseq.task/status [:db/ident]}
-                            {:logseq.task/deadline [:block/journal-day]}]
-                           [:block/uuid const/block1-uuid])]
-        (when-not (= :logseq.task/status.doing (:db/ident (:logseq.task/status block1)))
-          (throw (ex-info "wait block1's task properties to be synced" {:missionary/retry true})))
-        (is (= {:block/tags [{:db/ident :logseq.class/Task}],
-                :logseq.task/status {:db/ident :logseq.task/status.doing}
-                :logseq.task/deadline {:block/journal-day 20240907}}
-               block1)))))})
+     (let [conn (helper/get-downloaded-test-conn)
+           block1 (d/pull @conn
+                          [{:block/tags [:db/ident]}
+                           {:logseq.task/status [:db/ident]}
+                           {:logseq.task/deadline [:block/journal-day]}]
+                          [:block/uuid const/block1-uuid])]
+       (when-not (= :logseq.task/status.doing (:db/ident (:logseq.task/status block1)))
+         (throw (ex-info "wait block1's task properties to be synced" {:missionary/retry true})))
+       (is (= {:block/tags [{:db/ident :logseq.class/Task}],
+               :logseq.task/status {:db/ident :logseq.task/status.doing}
+               :logseq.task/deadline {:block/journal-day 20240907}}
+              block1)))))})
 (def ^:private step4
   "client1:
 
@@ -154,49 +154,49 @@
   - send a message to client1 contains client2's block tree to client1"
   {:client1
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)
-           tx-data1 (const/tx-data-map :move-blocks-concurrently-1)
-           tx-data2 (const/tx-data-map :move-blocks-concurrently-client1)]
-       (helper/transact! conn tx-data1)
-       (m/? (helper/new-task--wait-all-client-ops-sent))
-       (m/? (helper/new-task--client1-sync-barrier-2->1 "move-blocks-concurrently-signal"))
-       (m/? helper/new-task--stop-rtc)
-       (helper/transact! conn tx-data2)
-       (is (nil? (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))))
-       (m/? (helper/new-task--wait-all-client-ops-sent))
-       (m/? (helper/new-task--client1-sync-barrier-2->1 "step5"))
-       (let [message (m/? (helper/new-task--wait-message-from-other-client
-                           (fn [message] (= "move-blocks-concurrently-page-blocks" (:id message)))
-                           :retry-message "move-blocks-concurrently-page-blocks"))
-             client2-page-blocks (:page-blocks message)
-             client1-page-blocks (ldb/get-page-blocks @conn (:db/id (d/entity @conn [:block/uuid const/page3-uuid]))
-                                                      :pull-keys '[:block/uuid :block/title :block/order
-                                                                   {:block/parent [:block/uuid]}])]
-         (is (= (set client1-page-blocks) (set client2-page-blocks))))))
+    (let [conn (helper/get-downloaded-test-conn)
+          tx-data1 (const/tx-data-map :move-blocks-concurrently-1)
+          tx-data2 (const/tx-data-map :move-blocks-concurrently-client1)]
+      (helper/transact! conn tx-data1)
+      (m/? (helper/new-task--wait-all-client-ops-sent))
+      (m/? (helper/new-task--client1-sync-barrier-2->1 "move-blocks-concurrently-signal"))
+      (m/? helper/new-task--stop-rtc)
+      (helper/transact! conn tx-data2)
+      (is (nil? (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))))
+      (m/? (helper/new-task--wait-all-client-ops-sent))
+      (m/? (helper/new-task--client1-sync-barrier-2->1 "step5"))
+      (let [message (m/? (helper/new-task--wait-message-from-other-client
+                          (fn [message] (= "move-blocks-concurrently-page-blocks" (:id message)))
+                          :retry-message "move-blocks-concurrently-page-blocks"))
+            client2-page-blocks (:page-blocks message)
+            client1-page-blocks (ldb/get-page-blocks @conn (:db/id (d/entity @conn [:block/uuid const/page3-uuid]))
+                                                     :pull-keys '[:block/uuid :block/title :block/order
+                                                                  {:block/parent [:block/uuid]}])]
+        (is (= (set client1-page-blocks) (set client2-page-blocks))))))
    :client2
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)]
-       (m/?
-        (c.m/backoff
-         (take 4 c.m/delays)
-         (m/sp
-           (let [page3 (d/pull @conn '[*] [:block/uuid const/page3-uuid])
-                 page3-blocks (some->> (:db/id page3)
-                                       (ldb/get-page-blocks @conn))]
-             (when-not (:block/uuid page3)
-               (throw (ex-info "wait page3 synced" {:missionary/retry true})))
-             (is (= 6 (count page3-blocks)))))))
-       (m/? (helper/new-task--client2-sync-barrier-2->1 "move-blocks-concurrently-signal"))
-       (m/? helper/new-task--stop-rtc)
-       (helper/transact! conn (const/tx-data-map :move-blocks-concurrently-client2))
-       (is (nil? (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))))
-       (m/? (helper/new-task--wait-all-client-ops-sent))
-       (m/? (helper/new-task--client2-sync-barrier-2->1 "step5"))
-       (m/? (helper/new-task--send-message-to-other-client
-             {:id "move-blocks-concurrently-page-blocks"
-              :page-blocks (ldb/get-page-blocks @conn (:db/id (d/entity @conn [:block/uuid const/page3-uuid]))
-                                                :pull-keys '[:block/uuid :block/title :block/order
-                                                             {:block/parent [:block/uuid]}])}))))})
+    (let [conn (helper/get-downloaded-test-conn)]
+      (m/?
+       (c.m/backoff
+        (take 4 c.m/delays)
+        (m/sp
+         (let [page3 (d/pull @conn '[*] [:block/uuid const/page3-uuid])
+               page3-blocks (some->> (:db/id page3)
+                                     (ldb/get-page-blocks @conn))]
+           (when-not (:block/uuid page3)
+             (throw (ex-info "wait page3 synced" {:missionary/retry true})))
+           (is (= 6 (count page3-blocks)))))))
+      (m/? (helper/new-task--client2-sync-barrier-2->1 "move-blocks-concurrently-signal"))
+      (m/? helper/new-task--stop-rtc)
+      (helper/transact! conn (const/tx-data-map :move-blocks-concurrently-client2))
+      (is (nil? (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))))
+      (m/? (helper/new-task--wait-all-client-ops-sent))
+      (m/? (helper/new-task--client2-sync-barrier-2->1 "step5"))
+      (m/? (helper/new-task--send-message-to-other-client
+            {:id "move-blocks-concurrently-page-blocks"
+             :page-blocks (ldb/get-page-blocks @conn (:db/id (d/entity @conn [:block/uuid const/page3-uuid]))
+                                               :pull-keys '[:block/uuid :block/title :block/order
+                                                            {:block/parent [:block/uuid]}])}))))})
 
 (def ^:private step6
   "Delete blocks test-1
@@ -214,45 +214,45 @@ client2:
 - check block-tree"
   {:client1
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)
-           tx-data1 (const/tx-data-map :step6-delete-blocks-client1-1)
-           tx-data2 (const/tx-data-map :step6-delete-blocks-client1-2)]
-       (helper/transact! conn tx-data1)
-       (m/? (helper/new-task--wait-all-client-ops-sent))
-       (m/? (helper/new-task--client1-sync-barrier-1->2 "step6"))
-       (m/? helper/new-task--stop-rtc)
-       (helper/transact! conn tx-data2)
-       (let [r (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))]
-         (is (nil? r))
-         (m/? (helper/new-task--wait-all-client-ops-sent)))))
+    (let [conn (helper/get-downloaded-test-conn)
+          tx-data1 (const/tx-data-map :step6-delete-blocks-client1-1)
+          tx-data2 (const/tx-data-map :step6-delete-blocks-client1-2)]
+      (helper/transact! conn tx-data1)
+      (m/? (helper/new-task--wait-all-client-ops-sent))
+      (m/? (helper/new-task--client1-sync-barrier-1->2 "step6"))
+      (m/? helper/new-task--stop-rtc)
+      (helper/transact! conn tx-data2)
+      (let [r (m/? (rtc-core/new-task--rtc-start const/downloaded-test-repo const/test-token))]
+        (is (nil? r))
+        (m/? (helper/new-task--wait-all-client-ops-sent)))))
    :client2
    (m/sp
-     (let [conn (helper/get-downloaded-test-conn)]
-       (m/? (helper/new-task--client2-sync-barrier-1->2 "step6"))
-       (m/?
-        (c.m/backoff
-         (take 4 c.m/delays)
-         (m/sp
-           (let [page (d/pull @conn '[*] [:block/uuid const/step6-page-uuid])
-                 page-blocks (when-let [page-id (:db/id page)]
-                               (ldb/get-page-blocks @conn page-id
-                                                    :pull-keys '[:block/uuid {:block/parent [:block/uuid]}]))]
-             (when-not (= 1 (count page-blocks))
-               (throw (ex-info "wait delete-blocks changes synced"
-                               {:missionary/retry true
-                                :page-blocks page-blocks})))
-             (is (= {:block/uuid const/step6-block3-uuid
-                     :block/parent {:block/uuid const/step6-page-uuid}}
-                    (select-keys (first page-blocks) [:block/uuid :block/parent])))))))))})
+    (let [conn (helper/get-downloaded-test-conn)]
+      (m/? (helper/new-task--client2-sync-barrier-1->2 "step6"))
+      (m/?
+       (c.m/backoff
+        (take 4 c.m/delays)
+        (m/sp
+         (let [page (d/pull @conn '[*] [:block/uuid const/step6-page-uuid])
+               page-blocks (when-let [page-id (:db/id page)]
+                             (ldb/get-page-blocks @conn page-id
+                                                  :pull-keys '[:block/uuid {:block/parent [:block/uuid]}]))]
+           (when-not (= 1 (count page-blocks))
+             (throw (ex-info "wait delete-blocks changes synced"
+                             {:missionary/retry true
+                              :page-blocks page-blocks})))
+           (is (= {:block/uuid const/step6-block3-uuid
+                   :block/parent {:block/uuid const/step6-page-uuid}}
+                  (select-keys (first page-blocks) [:block/uuid :block/parent])))))))))})
 
 (defn- wrap-print-step-info
   [steps client]
   (map-indexed
    (fn [idx step]
      (m/sp
-       (helper/log "start step" idx)
-       (some-> (get step client) m/?)
-       (helper/log "end step" idx)))
+      (helper/log "start step" idx)
+      (some-> (get step client) m/?)
+      (helper/log "end step" idx)))
    steps))
 
 (def ^:private all-steps [step0 step1 step2 step3 step4 step5 step6])