feat(breadcrumb): add functionality to resolve and hydrate breadcrumb reference titles

This commit is contained in:
Mega Yu
2026-05-13 18:06:54 +08:00
parent 143bfbcd26
commit a858fe9795
3 changed files with 141 additions and 61 deletions

View File

@@ -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))))

View File

@@ -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?})))

View File

@@ -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