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::tagged(tag) The paths of every note with tag (with or without its #), or a tag under it: project finds #project/q3. As notesy's index has them, so it's quick and has no limit.
notes::having(key) The paths of every note whose front matter has key, whatever it says.
notes::with_property(key, value) The paths of every note whose front matter says key: value: value text, a number or true / false, matched ignoring case and quotes, or held in a list (tags: [a, b] has a). notes::with_property("kanban", true) finds the boards.
notes::search(query) The notes the words in query are in, best first (at most 50): #{ path, title, snippet }. A query is searched as the palette searches it: words and "phrases", narrowed by path:folder, tag:name, -word and /pattern/.
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::open_as(path, options) Opens a note, or any text file in the vault ("scripts/build.sh", "server.log"), in a tab in front, as options says: #{ read_only: true } only to read (nothing typed in it, never saved, its links opened with a click), #{ code: true } in the code view, #{ language: "bash" } in the code view in that language's colors rather than its extension's. Already open, its tab is turned to that: only to read once it's saved (one that can't be saved stays as it was, its changes kept). One a script made only to read isn't one to reopen, and isn't put back when notesy starts again. An error for a note asked for as code, a board or a drawing (boards::open, drawings::open), a picture or anything else that isn't text, a file that isn't there or a path outside the vault, and an option it doesn't know.
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::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 Recently deleted, where it can be put back from for 30 days.

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