← the Atlas

Markdown preview: Obsidian-style [[wikilinks]]

feature · seedling ·implemented ·

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.

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.

Code tab · rendered Markdown preview
[the limitations doc](LIMITATIONS.md)
regular markdown link
underlined link-blue — unchanged
[[Architecture]]
wikilink
Architecture
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.

@kolu/solid-markdown — the wikilink featureclient / BrowseFileDispatcher — host branchopenInCodeTab — front door (existing)treePaths() — fsListAll / git ls-files (existing)markedWikilink() — parses [[Note]] [[a|b]] [[a#h]] ![[embed]] → data-md-wikilink anchorresolveWikilink(target, repoPaths) — pathless, .md-implied vault match → unique | none | ambiguousonNavigateWikilink(target, anchorEl) host callbackunique ⇒ open · none ⇒ toast · ambiguous ⇒ OptionMenu anchored to the link clicktarget + anchortarget + repo vaultresolutionchosen pathvault paths
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 separate onNavigateWikilink 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

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.