notesy::notes

The vault's notes: reading them with notes.read, and changing them with notes.write. Paths are relative to the vault, with / between folders (Daily/2026-09-12.md); a path without an extension gets .md. A path out of the vault is an error.

rustuse notesy::{log, notes};

pub fn ready() {
    let plan = notes::read("Projects/Launch plan.md")?;
    log::info(`${plan.title()}: ${plan.tasks().len()} tasks, due ${plan.property("due")?}`);
    for path in notes::list("Daily") {
        log::info(path);
    }
    for hit in notes::search("launch") {
        log::info(`${hit.title}: ${hit.snippet}`);
    }
}

#Reading (notes.read)

Function What it gives
notes::read(path) The note, a Note, Some(note), or None when there's no such note. A note open in a tab is read as it is there, unsaved edits and all.
notes::list(folder) The paths of the notes in folder, and in the folders inside it; "" for the whole vault.
notes::search(query) The notes the words in query are in, best first (at most 50): #{ path, title, snippet }.
notes::open(path) Opens the note in a tab of its own, in front, as when the user opens it. An error when there's no such note.
notes::property_types(path) Each of its properties' types as notesy has them, an object (#{ tags: "tags", due: "date", source: "url" }): what Settings, Properties says for the key, else what the name says (tags, aliases, color), else what the value looks like. The types: text, number, checkbox, date, datetime, list, tags, link, url, color, icon, choice, created, updated, or a plugin's own. None when there's no such note.
notes::folder_note(folder) The folder's own note, its path ("Projects/Projects.md"): the note inside it by its name, in any case, where what's said about the folder lives (its properties, its tags, a line about it). notesy shows the folder by the note's title, color and icon, and hides the note under it. None when it has none.
notes::properties(path) Its front matter read as YAML, an object (#{ tags: ["launch"], due: "2026-09-30", owner: #{ name: "Ana" } }), empty when it has none, or None when there's no such note. Front matter that isn't key: value YAML is an error.

#A note

What notes::read gives, what a panel or a tab is built for (notesy::panels and notesy::views) and what editor::note() gives: a Note, with what a script usually wants of a note a method away. Each is worked out from its text when it's asked for.

Method What it gives
note.path() Its path in the vault, like Projects/Launch plan.md.
note.text() All its text, front matter and all.
note.body() Its text after its front matter.
note.properties() Its front matter read as YAML (notesy::yaml): an object, empty when it has none (or has front matter that can't be read).
note.property(key) One of its properties, Some(value), or None.
note.title() Its title property, or its first heading, or its file's name.
note.tags() Its tags, from its front matter and its text, as notesy finds them.
note.headings() #{ level, text, line } for each heading; lines count from 1.
note.links() #{ to, kind, line } for each link: kind is note (a wiki link, or one to a note) or url.
note.tasks() #{ text, done, line } for each - [ ] and - [x] line.
note.words() How many words its body has.

A Note is the note as it was when it was handed over; read it again for what it says now.

#Changing (notes.write)

Function What it does
notes::write(path, text) Replaces the note's text. A note open in a tab changes there, as one step the user can undo, and saves as they would; any other is written straight away. An error when there's no such note.
notes::create(path, text) A new note, and any folders it's in. An error when there's one there already.
notes::rename(path, name) A new name for the note, in the same folder: links to it change too, as when the user renames it.
notes::delete(path) Moves the note to the Trash, where it can be brought back from.

Changes reach other plugins as the usual events (note.changed, note.created, note.renamed, note.deleted), and the script's own handlers hear them too.

#How it works

A script's thread asks notesy and waits for the answer, which comes on notesy's next frame, a few milliseconds later; the window never waits for the script. notesy checks the plugin's permissions again for every call. A call notesy can't answer within five seconds is an error.

Every page