# Super Smart Random Note Open random notes matched by saved filters, written in (a subset of) Obsidian's own [search query syntax](https://obsidian.md/help/plugins/search). - **Default command** — "Open random note (default)", always available. Query is empty by default, meaning "any note in the vault". - **Custom commands** — define as many named filters as you want in the plugin settings. Each one becomes its own command in the command palette (bindable to a hotkey, launcher plugins, etc.). ## Why a reimplementation instead of Obsidian's real search engine? Obsidian doesn't expose its internal search engine through the public plugin API — there's no documented way to hand it a query string and get back a list of matching files. This plugin ships its own parser/evaluator that follows the same documented operators (see "Supported syntax" below), evaluated directly against each note's content (frontmatter included) plus its tags from the metadata cache. It is a close but **not pixel-perfect** clone of core search — see "Known differences" below. ## Supported syntax | Operator | Meaning | Example | |---|---|---| | *(bare word)* | Note content contains the word | `meeting` | | `"phrase"` | Note content contains the exact phrase | `"kickoff meeting"` | | `/regex/` | Note content matches the regex (flags allowed, e.g. `/foo/i`) | `/\d{4}-\d{2}-\d{2}/` | | `file:` | Match against the file name | `file:.png` | | `path:` | Match against the full file path | `path:"Journal/2026"` | | `content:` | Match against the full note content (same as bare word) | `content:"happy cat"` | | `tag:` | Note has this tag (nested tags included, e.g. `tag:#work` matches `#work/meeting`) | `tag:#idea` | | `line:` | At least one line matches | `line:(mix flour)` | | `section:` | At least one heading section matches (best-effort split on heading lines) | `section:(dog cat)` | | `block:` | At least one paragraph/block matches (best-effort split on blank lines) | `block:(dog cat)` | | `task:` | At least one task line (`- [ ]`/`- [x]`) matches | `task:call` | | `task-todo:` | At least one unchecked task line matches | `task-todo:call` | | `task-done:` | At least one checked (`[x]`) task line matches | `task-done:call` | | `[key]` | Frontmatter property `key` exists and is truthy | `[status]` | | `[key:value]` | Frontmatter property `key` equals `value` (or contains it, for list properties). Wikilink-valued properties (`- "[[Poems]]"`) match on the link's target/alias text, not the bracket syntax | `[status:Draft]`, `[types:poems]` | | `[key:v1 OR v2]` | Property equals any of the given values | `[status:Draft OR Published]` | | `OR` | Logical or (must be uppercase, space-separated) | `meeting OR work` | | `-` | Exclude (prefix, no space before the term) | `meeting -work` | | *(space)* | Implicit AND | `meeting work` | | `(...)` | Grouping | `meeting (work OR personal)` | | `field:(a b)` | Distributes the field over each grouped term | `tag:(idea work)` → has both `#idea` and `#work` | Matching is always case-insensitive, except inside `/regex/` literals (add the `i` flag yourself if you want a case-insensitive regex). ## Known differences from Obsidian's built-in search - No "Match case" toggle — everything but explicit regex is case-insensitive. - `section:` and `block:` use a simple heuristic (split on heading lines / blank lines) rather than Obsidian's real block/section parser. - No fuzzy matching — all text matching is plain substring (or regex). - Bare words and `content:` only look at note content, not the file name — use `file:`/`path:` explicitly if you want to match on the name. - Reads every markdown file's content on each random-note request (via the cached vault read), so very large vaults may see a brief delay. - `[key:value]` properties are read with a small built-in YAML frontmatter parser (not Obsidian's metadataCache, which updates on a debounce and can briefly miss a property you just added) — it covers scalars, quoted strings, inline lists (`[a, b]`), and block lists (`- item`), but not full YAML (no nested maps, multi-line strings, anchors, etc.). - An unrecognized `word:` immediately followed by a space (or the end of the query) — e.g. `types: poems` — is a query error, not a search: it looks exactly like an attempted property filter but silently splitting it into two unrelated bare-word terms (`types:` and `poems`, found anywhere in the file independently) would return a plausible-looking but wrong count. Use `[types:poems]` for a real property filter, or quote it — `"types: poems"` — to search that literal text as a phrase. `foo:bar` (no space) still works as one literal token, e.g. for a URL. ## Settings - **General** — "Open notes in" sets the vault-wide default (same tab / new tab / new window) for every command. - **Commands** — one row per command: the always-present "Default" row plus every custom filter you add. Click a row's name (or its gear icon) to edit its query and optionally override where *that* command opens notes; the search icon previews how many notes currently match; custom rows also get duplicate and delete icons. Type a name at the bottom and hit "Add command" to create a new filter, then edit its query from its row. ## Development ```bash npm install npm run dev # watch build npm run build # type-check + production build npm run lint # eslint (typescript-eslint recommended-type-checked) ``` Copy `main.js`, `manifest.json`, and `styles.css` into `/.obsidian/plugins/super-smart-random-note/` to install manually, or add the repo to [BRAT](https://github.com/TfTHacker/obsidian42-brat). The settings tab implements Obsidian's declarative `getSettingDefinitions()` API (1.13.0+) with the old imperative `display()` kept as a fallback for older versions — both paths share the same row-rendering code. ## Prior art The command-row-plus-modal editing UX is modeled after [obsidian-advanced-random-note](https://github.com/karstenpedersen/obsidian-advanced-random-note). [obsidian-lottery](https://github.com/wangeek/obsidian-lottery) was also a reference for the general idea of filtered random-note picking.