Visual vault navigator: folder/tag tree with a cover-image card wall, full-text search (including PDFs), image quick-look, and link preview cards.

A visual way to browse your Obsidian vault: a folder/tag tree on the left, a masonry wall of cover-image cards on the right. Designed as a visual alternative to list-based file explorers.
Interface available in English and 繁體中文 (follows your Obsidian language setting, with a manual override in settings).

| Card wall | Tag tree | Search |
|---|---|---|
![]() |
![]() |
![]() |
[!TIP] These screenshots were taken with the Velocity theme — a good pairing if you want the card wall to look like the shots above. The plugin styles itself from Obsidian's CSS variables, so it follows whatever theme you use, in both light and dark mode.
cover: frontmatter, first embedded image, image files themselves, PDF first pages, and web page og:image for link notesThree layouts, switchable any time from the toolbar's ⋯ More panel — they change how the title and date sit against the cover image:

Shift+Enter sends all results to the card wallutm_*, xmt, slof, fbclid, igsh, gclid…) from URLs — automatically on paste, from the editor right-click menu (one link, the selection, or the whole note), or by right-clicking a link card. Blacklist-based, so functional parameters like YouTube's ?v= are never touched; extra/keep lists are configurable. Parameter stripping is entirely offline. Threads/Instagram /share/ links hide their tracking code in the path instead, so expanding those to the real post URL is an opt-out network request (see below).This plugin makes network requests only for the features below. Nothing is collected, tracked, or sent anywhere else; there is no telemetry.
| Feature | What is sent | To where |
|---|---|---|
| Link previews / link cards | GET requests for URLs found in your notes, to read og:image/metadata |
The sites your notes link to |
| Site icons on link cards | The domain name of each linked site | Google favicon service (www.google.com/s2/favicons) |
| Link cards for Threads / Instagram posts | The post URL being previewed, to read the author name and post text | Meta's oEmbed endpoints (instagram.com, threads.net) |
| Clean links — short link expansion (on by default, can be turned off) | A GET request for the threads.com/share/… or instagram.com/share/… link being expanded, to read its canonical URL |
Threads / Instagram |
About User-Agent headers. Metadata requests are sent with the User-Agent of a common browser or of a link-preview bot (facebookexternalhit, Slackbot-LinkExpanding), trying them in that order until one returns usable metadata. Some sites return 403 to a default User-Agent but serve og: tags to link-preview bots, so without this most cards would come back empty. This is the same mechanism chat apps such as Slack and iMessage use to render link previews, and only public metadata is read — no login, no credentials, no user data.
All caches (link previews, PDF thumbnails) are stored locally in the plugin folder.
| Capability | Why it is needed |
|---|---|
Listing vault files (getFiles, getMarkdownFiles) |
The card wall and the folder/tag tree are built from the file list — the plugin has to know which files exist in order to show them. Nothing is sent anywhere; the list stays in memory. |
Reading files (cachedRead) |
To resolve a note's cover image, render the text excerpt on a card, and build the search index. |
Writing files (create, modify, rename, delete) |
Only in response to an explicit action: creating a note, renaming, moving, batch delete, and the "convert Canvas image to note" command. |
| Clipboard | Only for the copy actions you invoke — copy image, copy wiki-links for the selected cards, and cleaning a pasted URL. |
localStorage |
Read-only, and only Obsidian's own language key, to follow the interface language. The plugin's own settings are stored through Obsidian's plugin data API (data.json) — it never writes to localStorage. |
Until this plugin is available in the community store, install manually:
main.js, manifest.json, and styles.css from the latest release<your vault>/.obsidian/plugins/gallery-navigator/Or use BRAT with this repository.
[!IMPORTANT] Coming from Image Peek or Link Card Preview? Disable them first.
Both of my earlier standalone plugins are now built into Gallery Navigator as the Image peek and Link cards modules. Running either one alongside this plugin means the same work happens twice: two sets of document listeners, two cards rendered for the same URL, and two separate caches for the same downloads.
Their settings and caches are not carried over — link previews will simply be fetched again on first use. If you prefer, you can keep using the standalone versions and turn the matching module off here (Settings → Gallery Navigator → Image peek / Link cards).
npm install
npm run dev # build CSS once, then esbuild watch for JS
npm run dev:css # watch the CSS parts only (handy while styling)
npm run build # production bundle → main.js + styles.css
Source lives in src/. Both main.js and styles.css in the plugin root are build artifacts — don't edit them directly.
JS — src/main.js is the entry point, bundled by esbuild.
CSS — Obsidian only loads a single styles.css, but 3000 lines in one file is unmaintainable, so it is assembled from parts by scripts/build-css.mjs:
| Part | Scope |
|---|---|
src/header.css |
File header comment |
src/gallery.css |
.gn-* — tree, card wall, toolbar |
src/peek.css |
.qp-* — image peek |
src/linkcard.css |
.lcp-* — link cards |
Order matters (later parts can override earlier ones). The build refuses to write styles.css if braces are unbalanced, so a truncated part fails loudly instead of silently breaking the stylesheet.
UI strings — src/i18n.js, English text as keys, with a zh-TW dictionary.
npm run release 0.2.1 # bumps manifest/package/versions, builds, commits, tags
git push origin main && git push origin 0.2.1
Pushing the tag triggers .github/workflows/release.yml, which rebuilds from source, verifies the tag matches manifest.json, attaches build provenance attestations, and creates the release. Don't run gh release create by hand — assets uploaded that way can't be attested.
The version lives in three files (manifest.json, package.json, versions.json) and the release script keeps them in sync. versions.json maps each plugin version to its minimum Obsidian version, so users on older Obsidian builds still resolve to a compatible release.
Never re-tag a published version. Obsidian decides whether an update exists by comparing version numbers, so quietly replacing the assets of an existing release means installed users never receive the fix. Ship a new patch version instead.