Michael Naumov261 downloadsPick a note by navigating folders and insert a link to it, from a command or from your own scripts.
Obsidian's own link autocomplete searches the whole vault and ranks it fuzzily, which is exactly wrong when you know the link belongs in one folder and you want the same note to come first every time. This plugin is a link picker you can point at a folder and then navigate — pick a folder to descend into it, pick .. to come back out — with a ranking that is deterministic rather than fuzzy: an exact name beats a prefix, which beats a path match, which beats a scattered word match.
It is also callable. The picker returns a string, so a template or a script can ask for a link and drop the answer straight into a property value.
The documentation is a demo vault. Every feature has a note that explains what it does and how to try it.
Start reading here — it is plain markdown, so it works on GitHub with nothing installed.
A copy of the vault ships with every release. You can access it via any of the following:
link-picker-demo-vault.zip from the Releases. It unzips into a single link-picker-demo-vault-<version> folder.demo-vault/ in this repository... to come back out, so the list is one folder's contents rather than the whole vault. 02 Navigating foldersfolder-notes setup rather than configured again here. 03 RankingRun Link Picker: Insert link... in an editor. Any selected text seeds the query and is replaced by the link you choose.
Inside the picker, Enter on a folder descends into it and Enter on .. goes back up.
Everything else lives in the strip along the bottom, as a control you click and a hotkey you can press instead. Both routes run the same thing, and the controls are what make the picker usable on a phone, where there is no Alt key at all.
| Control | Key | What it does |
|---|---|---|
| No link | Alt + 1 |
Choose nothing, and insert an empty link |
| Create new | Shift + Enter |
Create a note with the name you typed, and link to it |
| All files | Alt + 2 |
Show all files, not only markdown |
| Subfolders | Alt + 3 |
Include subfolder contents |
| Folders only | Alt + 4 |
Show only folders |
| By date | Alt + 5 |
Lead with the most recently updated note (on by default) |
A toggle that is on is filled in, so the strip says what the picker is currently doing. All files and Subfolders grey out while Folders only is on, since between them they would empty the list.
With a query typed, items are ranked in tiers, and only within a tier does anything else break the tie:
/)Segment matching is FuzzyAliases count as names, so a note with three aliases offers three rows that rank separately. Inside a tier, folders come first; then recently opened files, then updated date, then shallower paths, then alphabetical.
The seventh tier is what Segment matching adds, and it is deliberately the weakest: turning it on changes what the picker finds, never the order it finds it in. Brv reaches Bravo without displacing anything you spelled out.
With no query typed, .. and the current folder's own folder note hold the top two rows, so navigating out never means scrolling.
If you link into one folder constantly, navigating there every time is the wrong shape. Add a picker in the settings and it becomes its own command, already rooted where you wanted to be — a vault that files people under People and courts under Legal gets two commands rather than one that asks first.
Each picker carries a name, a folder, whether it starts with subfolders included, a prefix and suffix, a placeholder, and whether it offers to create notes. The prefix and suffix wrap the link, so a prefix of "Person: " is what makes the result drop straight into a note's property list.
A picker's command is identified by an id minted when the picker is created, not by its name, so renaming a picker keeps whatever hotkey you bound to it.
Anything richer than those fields is code rather than configuration — validating a name, deriving a folder from it, seeding frontmatter. That is what the callable API is for.
The picker understands folder notes. A folder's note is not offered under its own file name — Foo/Foo.md says nothing that the Foo row did not already say, and choosing the folder is how you reach it. A folder note's aliases are still offered, so Foo/Foo.md aliased The Foo project is reachable under that name.
Where that note lives is read from the installed folder-notes plugin by default, so a vault that already has folder notes needs no configuration. Folder note location in the settings overrides it.
Auto (read the folder-notes plugin), inside the folder, beside the folder, or none.Auto.Substring (the default: a query term must appear inside a path part as one unbroken run) or Fuzzy (its characters need only appear in order, so Brv finds Bravo). Finding more, never reordering.The picker is callable, and that is where the plugin came from: it is an extraction of a script whose every consumer was a Templater template writing a link into a property value. Those callers want the string, not an edit at a cursor, so the API is not an extra bolted onto a command — it is the other half of the plugin.
It is published through obsidian-dev-utils' cross-plugin API registry, keyed by the plugin id, version-negotiated, and revoked automatically if the plugin is disabled. The contract version is 1.1.0, and it is independent of the plugin's own version.
import { watchPluginApi } from 'obsidian-dev-utils/obsidian/plugin/plugin-api';
interface LinkPickerApi {
select(params: LinkPickerApiSelectParams): Promise<string>;
}
const ref = watchPluginApi<LinkPickerApi>({
apiVersionRange: '^1',
app: this.app,
component: this,
pluginId: 'link-picker'
});
// `ref.value` is `null` until the plugin has loaded, and becomes non-null on its own.
const api = await ref.whenAvailable();
const link = await api.select({ folderPath: 'People', prefix: 'Person: ' });
// → 'Person: [[Ada Lovelace|Ada]]'
select resolves with the link text and rejects when the picker is dismissed — dismissing is the caller's cue that the user backed out, which is different from the empty string that No link returns. Every option is optional; anything omitted falls back to the settings above.
createNote — called when the user picks Create new, and given the folder the picker is currently rooted at plus what they typed. This is the hook the whole API exists for: validating a name, deriving a subfolder from it, seeding frontmatter and applying a template are vault conventions, and none of them are expressible in settings. Without it the plugin creates an empty note.folderPath — the folder the picker opens rooted at. A starting point, not a fence.includeSubfolders — whether it starts with subfolder contents included.initialQuery — seeds the input, so a picker opened over a selection starts filtered by it.prefix and suffix — wrap the link. A prefix of "Person: " produces Person: [[Ada]]; plain strings rather than field names, so "- " or " work too.shouldApplyPrefixSuffixWhenNoLinkSelected — whether the prefix and suffix are still emitted when the user presses No link. Off by default, so declining returns '' rather than a "Person: " with nothing after it.placeholder — the modal's placeholder text.shouldAllowCreate — whether Create new is offered at all.sourcePathOrFile — the note the link is written INTO, which decides whether it comes out relative or absolute. Defaults to the active file, and worth passing explicitly when the note being written to is not the one Obsidian considers active — which is the case while a template renders a brand-new note.excludedPathPatterns, folderNoteConfig, segmentMatchMode, titlePropertyName, updatedPropertyName — per-call overrides of the matching settings.06 Calling it from a script in the demo vault has a runnable version of the above.
The plugin is available in the official Community Plugins repository.
To install the latest beta release of this plugin (regardless if it is available in the official Community Plugins repository or not), follow these steps:
Add plugin button once and wait a few seconds for the plugin to install.By default, debug messages for this plugin are hidden.
To show them, run the following command in the DevTools Console:
window.DEBUG.enable('link-picker');
For more details, refer to the documentation.
All notable changes to this project will be documented in the CHANGELOG.
Contributions are welcome — see CONTRIBUTING to get set up.
See my other Obsidian resources.