Plugin: properties UI + Bases-style vault tables (#72)#143
Merged
Conversation
Adds `noteser-properties` under public/plugins/ — closes issue #72 by implementing both halves of the Obsidian Bases-parity ask as a plugin rather than core code. PR #142 just landed the VNode event delivery that makes interactive plugin UIs work end-to-end; this is the first feature plugin that exercises the full v1.2 stack. Surfaces: - Sidebar `Properties+` panel. Renders the active note's frontmatter as a key/value list with one `input` VNode per property. "+ Add property" opens a key + value draft row. Each commit calls `ctx.vault.write.updateNote(id, { frontmatter })`. Subscribes to `ctx.vault.events.onActiveNoteChange` and `onNoteSaved` so the panel re-pulls the host-parsed frontmatter on every relevant change. - Fullscreen `Vault tables` view. `ctx.vault.read.getAllNotes()` (falls back to `stream()` for vault-too-large), columns inferred as the union of every note's frontmatter keys, every row rendered as a `box` with a `link` VNode for the title (clicking dispatches the PR #142 wikilink intercept). Top input filters across title, folder path, every stringified frontmatter value. Column headers are `button` VNodes — click to sort, click again to flip direction. "Save current view as note" calls `ctx.vault.write.createNote` with the rendered markdown table. Frontmatter handling: - Source: `NoteWithBody.frontmatter` straight from the host's parser. The plugin never re-parses YAML; js-yaml is not a dependency (the plan's check confirmed it's not in package.json). - Type inference per column: `string` / `number` / `date` (ISO date) / `tag-array` / `boolean`. Heterogeneous columns fall back to `string` so sort stays sane. Test plan: - 44 unit tests cover inference (string/number/date/tag-array/ boolean/empty/heterogeneous), filter (title, folder, tags, status, numbers, case-insensitivity, empty filter), sort (every column type, asc + desc, empties last, _title / _folder special keys, immutability), value coercion (back to numbers / arrays / booleans on edit), and the markdown table serialiser (header + separator, pipe escaping). Mirror lives at src/plugins/propertiesPluginLogic.ts; production logic at public/plugins/noteser-properties/main.js (must move in lock step). Coexistence: the existing core `PropertiesPanel` is unchanged — the plugin adds a second, richer surface alongside it. `npm run lint`, `npx tsc --noEmit`, `npm test -- --ci` all green. Closes #72.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #72 by building a
noteser-propertiesreference plugin underpublic/plugins/. Two surfaces, both via the Plugin API v1.2 stackthat landed across PRs A–F + the post-v1.2 VNode event delivery
follow-up (#142):
Properties+panel — renders the active note'sfrontmatter as a key/value list. One
inputVNode per property;"+ Add property" opens a key + value draft row. Every commit
calls
ctx.vault.write.updateNote(id, { frontmatter })so thehost's existing writeFrontmatter helper re-serialises the block
and the note body stays untouched. Subscribes to
ctx.vault.events.onActiveNoteChange+onNoteSavedso thepanel re-pulls the host-parsed frontmatter on every relevant
change.
Vault tablesview — Obsidian Bases-style tableover the whole vault.
ctx.vault.read.getAllNotes()(falls backto
ctx.vault.read.stream()when the vault is too large for thesingle-envelope path), columns inferred as the union of every
note's frontmatter keys. Filter input matches across title,
folder path, every stringified value. Column headers are
buttonVNodes — click to sort, click again to flip direction. Each row
renders the title as a
linkVNode so the PR Wire VNode event delivery + wikilink intercept (post-v1.2 followup) #142 wikilinkintercept opens the note. "Save current view as note" calls
ctx.vault.write.createNotewith the rendered markdown table.Coexists with the existing core
PropertiesPanel— both stayselectable per Jon's rule.
Why a plugin (not core)
The plugin platform was specifically scoped to unlock these four
issues (#70, #71, #72, #73) as plugins rather than core code; the
v1.2 plan doc calls #72 out by name in §13. Shipping as a plugin
keeps:
cross-references) can replace this without touching core
Frontmatter inference notes
NoteWithBody.frontmatterfrom the host. The pluginnever re-parses YAML;
js-yamlis intentionally NOT adependency (the plan's "already a dep — check
package.json"hint checked — it isn't; the host's lightweight parser already
hands us a typed object).
string/number/date(ISO date pattern)/
tag-array/boolean. Heterogeneous columns reduce tostringso sort stays sane on a mixed vault.priority: 3writtenbare) classify as
numberso a "priority" column sortsnumerically even though the underlying values arrive as JS
strings from some other plugin's writes.
otherwise add noise to vaults with one-off bare
key:lines.tagsalways renders first; remaining columns alphabetical.Filter / sort UX
match across title, folder path, and every stringified
frontmatter value (arrays joined with
,, objects JSON-ified).Empty / whitespace filter keeps every note.
again flips ascending↔descending; clicking a different header
resets to ascending. The active sort is shown in the header
label with a
▲/▼glyph.sort last in ascending direction, reversed by
desc._title,_folder) so they remain sortable even on vaults whosefrontmatter never declares those keys.
Test plan
npm run lintcleannpx tsc --noEmitzero errorsnpm test -- --cigreen (2608 passing, 17 pre-existingskips — the new suite adds 44 tests covering inference,
filter, sort, value coercion, and markdown export)
note with frontmatter, confirm Properties+ panel renders +
lets you edit. Run "Open Vault tables" from the palette,
confirm table appears, sort by a column, click a row to
open that note.
🤖 Generated with Claude Code