Support [[Note]] / [[Note|alias]] / [[Note#heading]] wikilinks in the Code-tab Markdown preview — a distinct rendered style, pathless vault-wide resolution on click, and an inline disambiguation menu when a basename is ambiguous.
Plan of record · shipped in
#1212 · builds directly on the
relative-link work (the bug note “repo-relative links open the target
file”, shipped in PR #1190). Verdict: ~half a day, low risk.
How wikilinks render and resolve
A wikilink is not a regular link with different syntax — it should look like a
different kind of reference. Regular Markdown links keep today’s link-blue
underline; a wikilink gets its own violet, bracketed treatment that reads as
“internal note reference,” uniformly, whether or not it resolves.
violet, bracketed — reads as an internal note reference
[[Architecture|the arch doc]]
wikilink · aliased
⟦the arch doc⟧
alias is the visible text; same style
Resolution is lazy — on click, never at render time. The preview doesn’t
pre-check every [[…]] against the file list to grey out dead ones; it renders
them all alike and resolves the one you actually click. That keeps the renderer a
pure presenter (no file-list dependency threaded into it) and matches how the
relative-link path already works.
open the ⟦Note⟧ doc
a/Note.md
b/Note.md
Click on an ambiguous [[Note]] (two Note.md) → a menu anchored to the link lists the matching files; pick one to open it.
The hard part — the vault index — already ships
The obvious worry with Obsidian wikilinks is resolution: [[Architecture]]
is pathless — it finds Architecture.md by basename, scanning the whole repo,
with no directory hint. That sounds like it needs a new file index.
It doesn’t. fsListAll already streams git ls-files --cached --others --exclude-standard into the client (integrations/git/src/browse.ts),
materialised as treePaths() in CodeTab.tsx — a live, gitignore-respecting,
NFC-normalised list of every repo path, already the back-end of the “open this
file” front door.
The pieces
The relative-link work (PR #1190) cut most of the seam: a tagged anchor, a host
callback, and the openInCodeTab front door. Wikilinks add a parser in front
of it and a resolver + menu behind it.
The wikilink feature — parser, resolver, and host callback — lives wholly in @kolu/solid-markdown; the client dispatcher only wires it to the menu and the existing open-in-Code-tab front door. The marked extension mints a distinct data-md-wikilink anchor; resolveWikilink does the pathless, .md-implied vault match and surfaces candidates instead of collapsing to null. The sanitizer gains one allowlisted marker (and a guard that strips it from any escaping href); the front door and the file list are untouched.
Why a separateonNavigateWikilink callback rather than reusing
onNavigateRelative: the two differ on two axes — the resolution model (pathless
vault vs. the doc’s directory) and the need for the clicked element, so the
host can anchor the disambiguation menu to it. The menu itself is no new
machinery — it reuses OptionMenu + useAnchoredPopover, the same anchored
option-list the Dock and minimap pickers use. repoPaths is threaded from
CodeTab (which owns treePaths()) down to the dispatcher, since the front door
resolves internally and never exposes the candidate set the menu needs.
Scope
![[embeds]] (transclusion) — no. Inlining a note’s body or an image is
not a link; it’s a render-time content splice with its own recursion, cycle,
and sizing concerns. ![[…]] is left as inert literal text, never expanded.
Heading scroll-after-open — out of scope. A [[Note#Heading]] opens the
file; scrolling to #Headinginside it is not handled here — the same gap the
relative-link fix left for []() fragments.
Why this stays simple
The earlier instinct to call resolution “moderate effort” was wrong: it assumed a
file index that turned out to already ship. With that corrected, the whole
feature — distinct rendering, pathless resolution, and the ambiguity menu — is a
half-day, low-risk change. Transclusion is a deliberate no; heading-scroll is out
of scope.