mirror of
https://github.com/logseq/logseq.git
synced 2026-05-19 18:32:41 +00:00
feat(breadcrumb): add functionality to resolve and hydrate breadcrumb reference titles
This commit is contained in:
@@ -3218,6 +3218,21 @@
|
||||
(when (:db/id seg)
|
||||
(db/entity (:db/id seg))))
|
||||
|
||||
(defn- missing-breadcrumb-ref-ids
|
||||
[segments]
|
||||
(->> segments
|
||||
(mapcat :title-ref-ids)
|
||||
distinct
|
||||
(remove (fn [id]
|
||||
(some-> (db/entity [:block/uuid id]) :block/title string?)))
|
||||
vec))
|
||||
|
||||
(defn- <hydrate-breadcrumb-ref-titles!
|
||||
[repo segments]
|
||||
(let [ref-ids (missing-breadcrumb-ref-ids segments)]
|
||||
(when (seq ref-ids)
|
||||
(db-async/<get-blocks repo ref-ids {:children? false}))))
|
||||
|
||||
(rum/defc breadcrumb-search-overflow-tooltip
|
||||
[title]
|
||||
(ui/tooltip
|
||||
@@ -3238,8 +3253,14 @@
|
||||
target-db-id (:db/id target-entity)
|
||||
load-full-hidden! (fn []
|
||||
(when (and target-db-id (nil? @full-hidden))
|
||||
(p/let [parents (db-async/<get-block-parents repo target-db-id 1000)]
|
||||
(let [parents (remove nil? (concat parents [from-property]))
|
||||
(p/let [parents (db-async/<get-block-parents repo target-db-id 1000)
|
||||
_ (<hydrate-breadcrumb-ref-titles!
|
||||
repo
|
||||
(breadcrumb-segments target-entity parents))]
|
||||
(let [target-entity (or (db/entity target-db-id) target-entity)
|
||||
from-property (or (some-> from-property :db/id db/entity) from-property)
|
||||
parents (remove nil? (concat (db/get-block-parents repo (:block/uuid target-entity) {:depth 1000})
|
||||
[from-property]))
|
||||
segments (breadcrumb-segments target-entity parents)
|
||||
view (breadcrumb-model/build-breadcrumb-view
|
||||
segments
|
||||
@@ -3349,13 +3370,19 @@
|
||||
load-depth (:load-depth (breadcrumb-model/variant-options effective-variant))]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(p/let [block (db-async/<get-block (state/get-current-repo)
|
||||
block-id
|
||||
{:children? false
|
||||
:skip-refresh? true})
|
||||
_ (when-let [id (:db/id block)]
|
||||
(db-async/<get-block-parents (state/get-current-repo) id load-depth))]
|
||||
(set-block! block)))
|
||||
(let [repo (state/get-current-repo)]
|
||||
(p/let [block (db-async/<get-block repo
|
||||
block-id
|
||||
{:children? false
|
||||
:skip-refresh? true})
|
||||
parents (when-let [id (:db/id block)]
|
||||
(db-async/<get-block-parents repo id load-depth))
|
||||
;; Parent blocks can arrive before the UI DB has loaded page refs
|
||||
;; used in their titles. Hydrate only those refs before rendering.
|
||||
_ (<hydrate-breadcrumb-ref-titles! repo (breadcrumb-segments block parents))]
|
||||
(set-block! (or (when-let [uuid (:block/uuid block)]
|
||||
(db/entity [:block/uuid uuid]))
|
||||
block)))))
|
||||
[])
|
||||
(when block
|
||||
(breadcrumb-aux config repo block-id opts))))
|
||||
|
||||
@@ -51,10 +51,6 @@
|
||||
(when-let [[_ text] (re-matches #"^\s*(?:;;|//|#|--)\s+(.+)$" line)]
|
||||
text))
|
||||
|
||||
(defn- useful-code-line?
|
||||
[line]
|
||||
(some? (strip-code-comment-marker line)))
|
||||
|
||||
(defn- useful-query-line?
|
||||
[line]
|
||||
(not (query-line? line)))
|
||||
@@ -82,55 +78,65 @@
|
||||
(string/replace #"(?i)#\+END_\w+" "")
|
||||
string/trim))
|
||||
|
||||
(defn- resolve-uuid-refs
|
||||
[entity text]
|
||||
;; Breadcrumb renderers hydrate title refs before displaying when possible.
|
||||
;; Keep unresolved UUID refs unchanged for partial entities or fallback paths.
|
||||
(or (when (and (string? text)
|
||||
(re-find db-content/id-ref-pattern text))
|
||||
(db-content/recur-replace-uuid-in-block-title
|
||||
(assoc entity :block/title text)
|
||||
10))
|
||||
text))
|
||||
|
||||
(defn normalize-breadcrumb-text
|
||||
"Extracts a short plain-text label from block raw-title.
|
||||
Returns at most max-segment-text-length characters of the first
|
||||
non-empty line (plus a trailing ellipsis if truncated).
|
||||
Does NOT invoke mldoc parse or markup rendering."
|
||||
[raw-title]
|
||||
(when (string? raw-title)
|
||||
(let [line (some (fn [line]
|
||||
(when-not (structural-marker-line? line)
|
||||
line))
|
||||
(normalized-lines raw-title))
|
||||
cleaned (when line (strip-markdown-markup line))]
|
||||
(truncate-segment-text cleaned))))
|
||||
(let [line (some (fn [line]
|
||||
(when-not (structural-marker-line? line)
|
||||
line))
|
||||
(normalized-lines raw-title))
|
||||
cleaned (when line (strip-markdown-markup line))]
|
||||
(truncate-segment-text cleaned)))
|
||||
|
||||
(defn- normalize-typed-breadcrumb-text
|
||||
(defn- first-content-line
|
||||
[raw-title]
|
||||
(some (fn [line]
|
||||
(when-not (structural-marker-line? line)
|
||||
line))
|
||||
(normalized-lines raw-title)))
|
||||
|
||||
(defn- breadcrumb-label-line
|
||||
[raw-title block-type]
|
||||
(when (string? raw-title)
|
||||
(let [line (case block-type
|
||||
:code
|
||||
(some (fn [line]
|
||||
(when (useful-code-line? line)
|
||||
(strip-code-comment-marker line)))
|
||||
(normalized-lines raw-title))
|
||||
(case block-type
|
||||
:code
|
||||
(some strip-code-comment-marker (normalized-lines raw-title))
|
||||
|
||||
:query
|
||||
(some (fn [line]
|
||||
(when (and (not (structural-marker-line? line))
|
||||
(useful-query-line? line))
|
||||
line))
|
||||
(normalized-lines raw-title))
|
||||
:query
|
||||
(some (fn [line]
|
||||
(when (and (not (structural-marker-line? line))
|
||||
(useful-query-line? line))
|
||||
line))
|
||||
(normalized-lines raw-title))
|
||||
|
||||
(:note :quote)
|
||||
(some (fn [line]
|
||||
(when-not (structural-marker-line? line)
|
||||
line))
|
||||
(normalized-lines raw-title))
|
||||
(:note :quote)
|
||||
(first-content-line raw-title)
|
||||
|
||||
(normalize-breadcrumb-text raw-title))
|
||||
cleaned (when line (strip-markdown-markup line))]
|
||||
(truncate-segment-text cleaned))))
|
||||
(first-content-line raw-title))))
|
||||
|
||||
(defn- entity-display-title
|
||||
[entity raw-title]
|
||||
(or (when (and (string? raw-title)
|
||||
(re-find db-content/id-ref-pattern raw-title))
|
||||
(db-content/recur-replace-uuid-in-block-title
|
||||
(assoc entity :block/title raw-title)
|
||||
10))
|
||||
raw-title))
|
||||
(defn- normalize-breadcrumb-line
|
||||
[entity line]
|
||||
(let [line (resolve-uuid-refs entity line)
|
||||
cleaned (when line (strip-markdown-markup line))]
|
||||
(truncate-segment-text cleaned)))
|
||||
|
||||
(defn- normalize-typed-breadcrumb-text
|
||||
[entity raw-title block-type]
|
||||
(normalize-breadcrumb-line entity (breadcrumb-label-line raw-title block-type)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Block type detection
|
||||
@@ -192,19 +198,18 @@
|
||||
"Converts a page or block entity map to a breadcrumb segment map.
|
||||
|
||||
Segment keys:
|
||||
:db/id - DataScript entity id
|
||||
:block/uuid - block uuid
|
||||
:type - :page | :block | :code | :query | :note | :quote | :math
|
||||
:text - short plain-text label (may be nil for empty blocks)
|
||||
:full-text - full normalized single-line text for title/aria-label
|
||||
:icon - icon value from :logseq.property/icon, or nil
|
||||
:page? - true for page segments"
|
||||
:db/id - DataScript entity id
|
||||
:block/uuid - block uuid
|
||||
:type - :page | :block | :code | :query | :note | :quote | :math
|
||||
:text - short plain-text label (may be nil for empty blocks)
|
||||
:full-text - full normalized single-line text for title/aria-label
|
||||
:title-ref-ids - UUID refs from the source line that can become the label
|
||||
:icon - icon value from :logseq.property/icon, or nil
|
||||
:page? - true for page segments"
|
||||
[entity]
|
||||
(when entity
|
||||
(let [page? (some? (:block/name entity))
|
||||
raw-title (entity-display-title
|
||||
entity
|
||||
(or (:block/raw-title entity) (:block/title entity)))
|
||||
raw-title (or (:block/raw-title entity) (:block/title entity))
|
||||
;; DB version: structural type is stored in :logseq.property.node/display-type
|
||||
;; (Code/Quote/Math blocks) or inferred from :block/tags (Query family).
|
||||
;; org-mode markers like #+BEGIN_SRC in raw-title are also recognised as
|
||||
@@ -212,9 +217,11 @@
|
||||
db-display-type (display-type->block-type (:logseq.property.node/display-type entity))
|
||||
tag-type (when-not page? (entity-tags->block-type entity))
|
||||
block-type (or db-display-type tag-type (detect-block-type raw-title page?))
|
||||
title-line (when-not page?
|
||||
(breadcrumb-label-line raw-title block-type))
|
||||
text (if page?
|
||||
(or (:block/title entity) (:block/name entity))
|
||||
(normalize-typed-breadcrumb-text raw-title block-type))
|
||||
(normalize-typed-breadcrumb-text entity raw-title block-type))
|
||||
full-text (if page?
|
||||
(or (:block/title entity) (:block/name entity) "")
|
||||
(or text ""))
|
||||
@@ -224,6 +231,8 @@
|
||||
:type block-type
|
||||
:text text
|
||||
:full-text full-text
|
||||
:title-ref-ids (when (and (not page?) (string? title-line))
|
||||
(db-content/get-matched-ids title-line))
|
||||
:icon icon
|
||||
:page? page?})))
|
||||
|
||||
|
||||
@@ -147,7 +147,51 @@
|
||||
:block/name "aaa"
|
||||
:block/title "aaa"}]})]
|
||||
(is (= "See [[aaa]]" (:text seg)))
|
||||
(is (= "See [[aaa]]" (:full-text seg))))))
|
||||
(is (= "See [[aaa]]" (:full-text seg)))))
|
||||
|
||||
(testing "block uuid tag refs are resolved to tag labels"
|
||||
(let [ref-uuid #uuid "00000000-0000-0000-0000-000000000002"
|
||||
seg (model/block->breadcrumb-segment
|
||||
{:db/id 1
|
||||
:block/uuid #uuid "00000000-0000-0000-0000-000000000001"
|
||||
:block/raw-title (str "Tagged #[[" ref-uuid "]]")
|
||||
:block/refs [{:db/id 2
|
||||
:block/uuid ref-uuid
|
||||
:block/name "aaa"
|
||||
:block/title "aaa"}]})]
|
||||
(is (= "Tagged #aaa" (:text seg)))
|
||||
(is (= "Tagged #aaa" (:full-text seg)))))
|
||||
|
||||
(testing "unloaded uuid refs stay unchanged in partial UI db"
|
||||
(let [ref-uuid #uuid "00000000-0000-0000-0000-000000000002"
|
||||
seg (model/block->breadcrumb-segment
|
||||
{:db/id 1
|
||||
:block/uuid #uuid "00000000-0000-0000-0000-000000000001"
|
||||
:block/raw-title (str "See [[" ref-uuid "]]")
|
||||
:block/refs [{:db/id 2
|
||||
:block/uuid ref-uuid}]})]
|
||||
(is (= (str "See [[" ref-uuid "]]") (:text seg)))
|
||||
(is (= (str "See [[" ref-uuid "]]") (:full-text seg))))))
|
||||
|
||||
(deftest block->breadcrumb-segment-title-ref-ids-test
|
||||
(testing "returns uuid refs from the breadcrumb label line"
|
||||
(let [visible-uuid #uuid "00000000-0000-0000-0000-000000000002"
|
||||
hidden-uuid #uuid "00000000-0000-0000-0000-000000000003"
|
||||
seg (model/block->breadcrumb-segment
|
||||
{:db/id 1
|
||||
:block/uuid #uuid "00000000-0000-0000-0000-000000000001"
|
||||
:block/raw-title (str "See [[" visible-uuid "]]\nIgnore [[" hidden-uuid "]]")})]
|
||||
(is (= [visible-uuid]
|
||||
(vec (:title-ref-ids seg))))))
|
||||
|
||||
(testing "includes tag-style uuid refs"
|
||||
(let [ref-uuid #uuid "00000000-0000-0000-0000-000000000002"
|
||||
seg (model/block->breadcrumb-segment
|
||||
{:db/id 1
|
||||
:block/uuid #uuid "00000000-0000-0000-0000-000000000001"
|
||||
:block/raw-title (str "Tagged #[[" ref-uuid "]]")})]
|
||||
(is (= [ref-uuid]
|
||||
(vec (:title-ref-ids seg)))))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; block->breadcrumb-segment — structural type detection
|
||||
|
||||
Reference in New Issue
Block a user