Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

* Inline Dataview fields are now recognized inside blockquotes and callouts — `> up:: [[Note]]` (and `> - up:: [[Note]]`) now creates the `up` edge, matching the behavior already shipped on the 1.12 maintenance line (4.14.3, #724). A trailing link on the same line still stays ordinary prose, per the #731 fix.

## 4.X

### [4.21.3](https://github.com/michaelpporter/breadcrumbs/compare/4.21.2...4.21.3) (2026-07-25)
Expand Down
6 changes: 4 additions & 2 deletions src/graph/builders/explicit/typed_link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ export interface InlineField {
value_end: number;
}

// A bare inline field opening a line: "same:: ...", "- same:: ...", "up :: ...".
// A bare inline field opening a line, optionally behind blockquote and/or list
// markers: "same:: ...", "- same:: ...", "> same:: ...", "up :: ...".
// Its value runs to the end of the line.
const LINE_FIELD_REGEX = /^(?:\s*[-*+\d.]+\s+)?([\w][\w\s-]*)\s*::\s*/;
const LINE_FIELD_REGEX =
/^(?:\s*>+\s*)?(?:\s*[-*+\d.]+\s+)?([\w][\w\s-]*)\s*::\s*/;

// Dataview's bracket/paren wrappers, anywhere on the line: "(down:: ...)" /
// "[down:: ...]". The value ends at the matching close bracket.
Expand Down
14 changes: 13 additions & 1 deletion tests/graph/builders/typed_link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,17 @@ describe("typed_link builder — body inline fields", () => {
t.expect(r.edges).toHaveLength(1);
t.expect(r.edges[0]!.target).toBe("y.md");
});

// A callout/blockquote line (e.g. Obsidian's `> [!note]`) still opens with
// a bare inline field.
test("blockquote field builds an edge, trailing link stays prose", async (t) => {
const { plugin, all_files } = inline_case("> (up:: [[Note]]) [[Other]]");
const r = await _add_explicit_edges_typed_link(plugin, all_files);

t.expect(r.edges).toHaveLength(1);
t.expect(r.edges[0]!.edge_type).toBe("up");
t.expect(r.edges[0]!.target).toBe("Note.md");
});
});

describe("parse_inline_fields", () => {
Expand All @@ -234,6 +245,8 @@ describe("parse_inline_fields", () => {
["paren wrapper", "(down:: [[X]])", "down"],
["bracket wrapper", "[down:: [[X]]]", "down"],
["indented list marker", " - down:: [[X]]", "down"],
["blockquote", "> down:: [[X]]", "down"],
["blockquote with list marker", "> - down:: [[X]]", "down"],
["hyphenated field", "my-field:: [[X]]", "my-field"],
// Pinned behaviour: the field-name char class allows internal spaces,
// so a prose prefix ending in `word:: ` is captured verbatim. Harmless
Expand All @@ -247,7 +260,6 @@ describe("parse_inline_fields", () => {
test.each([
["single colon", "down: [[X]]"],
["no field name", ":: [[X]]"],
["non-word line start (blockquote)", "> down:: [[X]]"],
["plain prose", "just a normal sentence"],
["empty line", ""],
])("returns null — %s", (_label, line) => {
Expand Down