notesy::events
What happens in notesy, as it happens. A script subscribes to the events it wants, one by one or a whole kind at once, and its handler is called for each, one at a time, in order.
rustuse notesy::{events, log, Event};
pub fn ready() {
events::on(Event::NoteSaved, |event| log::info(`saved ${event.path}`));
events::on(Event::AnyNote, |event| log::info(event.name));
}
| Function | What it does |
|---|---|
events::on(event, handler) |
Calls handler(event) for every event event covers: one of Event's below. |
events::off(event) |
Forgets every handler added for it. |
Event is notesy's list of what a script can hear, so a name that's wrong
doesn't compile, and an editor that knows Rune can offer the rest. (A name
as text, "note.saved" or "note.*", still works too, checked as the
script runs.)
Hearing about notes takes the notes.read permission: every note and vault
event, and Event::SecondInstance, need it. Subscribing to one without it
doesn't stop the script; the refusal goes to its log and the handler never
runs.
#Events
Every event reaches the handler as an object with its name (the text
form) and the fields below. Paths are relative to the vault, with /
between folders.
Event:: |
name |
Fields | When | Needs |
|---|---|---|---|---|
AppReady |
app.ready |
notesy is up, with its plugins loaded | ||
SecondInstance |
app.second_instance |
args (list of strings), cwd |
notesy was started again while running; that start's arguments and folder | notes.read |
VaultOpened |
vault.opened |
name_of_vault |
a vault was opened | notes.read |
NoteOpened |
note.opened |
path |
a note was opened in a tab | notes.read |
NoteClosed |
note.closed |
path |
a note's tab was closed | notes.read |
NoteChanged |
note.changed |
path |
its text changed; a few times a second at most while typing | notes.read |
NoteSaved |
note.saved |
path |
it was saved | notes.read |
NoteCreated |
note.created |
path |
a new note | notes.read |
NoteRenamed |
note.renamed |
from, to |
it was renamed or moved | notes.read |
NoteDeleted |
note.deleted |
path |
it was deleted | notes.read |
DrawingOpened |
drawing.opened |
path |
a drawing (notesy::drawings) was opened in a tab |
notes.read |
DrawingSaved |
drawing.saved |
path |
a drawing's tab wrote it to its file | notes.read |
HiddenChanged |
hidden.changed |
path, hidden |
a note, file or folder was hidden from the tree, or shown again | notes.read |
ZoneChanged |
zone.changed |
zone |
a place this plugin opened for others (notesy::zones) gained or lost an entry: its name, <plugin>/<key> |
ui |
ThemeChanged |
theme.changed |
id, dark (bool) |
the theme changed | |
CommandRan |
command.ran |
id |
a command ran: its id, like plugin.you.saves.sync |
|
SidebarItem |
sidebar.item |
section, item |
an item in one of its sidebar sections was clicked: section is the key it gave sections::add, item the item's id. Only the plugin whose section it is hears it. |
|
KeyDown |
key.down |
repeat (bool: a key held down, repeating), source (a KeySource: where it went), place (the same, in text); with keys.which, key, shift, ctrl, alt, command, which, taken (which key) |
a key went down, anywhere in notesy but its settings and plugins' dialogs: that one did, never which. Shift, Ctrl, Alt or Cmd going down alone isn't one: it's held for the key after it, which says so | keys |
KeyUp |
key.up |
source, place; with keys.which, key and the held modifiers |
a key came back up | keys |
SoundWants |
sound.wants |
id, ms |
a stream it feeds (notesy::sound) has ms left of what it was given: time to push more. Only the plugin whose sound it is hears it. |
sound |
SoundEnded |
sound.ended |
id |
one of its sounds finished, or was stopped. Only the plugin whose sound it is hears it. | sound |
And each kind at once:
Event:: |
Covers |
|---|---|
AnyApp |
app.ready and app.second_instance (app.*) |
AnyVault |
vault.opened (vault.*) |
AnyNote |
every note. event (note.*) |
AnyDrawing |
drawing.opened and drawing.saved (drawing.*) |
AnyKey |
key.down and key.up (key.*) |
AnySound |
sound.wants and sound.ended (sound.*) |
#Keys
A plugin with the keys permission hears keys go down and up, never which
ones: enough for a cat that bangs a bongo as you type, and nothing to spell
out what's typed. Shortcuts count too; the settings and plugins' dialogs,
where secrets are typed, don't.
rustuse notesy::{events, status, Event};
pub fn ready() {
let cat = status::add("bongo");
cat.set("(=^・ω・^=)");
events::on(Event::KeyDown, |key| if !key.repeat { cat.set("(=^・ω・^=)ノ") });
events::on(Event::KeyUp, |key| cat.set("(=^・ω・^=)"));
}
Each says where it went, its source, a place rather than a key: a
plugin that should only answer typing in a note matches on it, and one
that answers every key, like the cat above, needn't. Where a key went
says no more about which it was.
KeySource:: |
Where |
|---|---|
Editor |
a note's editor, being written in |
Board |
a board: one of its text cards being written in, or the board itself |
Palette |
the palette or the quick switcher |
Sidebar |
the sidebar's tree of notes |
Field |
any other field of notesy's: a property, the find bar, a name being changed |
Window |
nothing that takes typing: a shortcut, pressed anywhere |
Plugin |
one of a plugin's views (a tab, a panel, a sidebar section's), yours or another's: a field in it has the keyboard, or it was pressed in last and nothing else has it (a game's panel, a picker) |
place says the same in text: notesy's own by its name (editor,
palette), and a plugin's view as <plugin id>/<view>, the way zones are
named (acme.tasks/search). Every view a plugin adds is a place of its
own, with nothing to declare: a field in it is where keys typed there come
from, and so is the view itself once it's clicked in, until something
else is. More places may come; a match on them keeps a _ for those.
rustuse notesy::{events, log, Event, KeySource};
pub fn ready() {
events::on(Event::KeyDown, |key| {
match key.source {
KeySource::Plugin => log::info(`typing in ${key.place}`),
_ => {}
}
});
}
rustuse notesy::{events, sound, Event, KeySource};
pub fn ready() {
events::on(Event::KeyDown, |key| {
match key.source {
KeySource::Editor => sound::tone(880.0, 30),
KeySource::Board => sound::tone(660.0, 30),
_ => {}
}
});
}
#Which key
With keys.which as well as keys, a plugin hears which key it was,
letters and all, and which modifiers are held: for a vim-style plugin's
motions, shortcuts of its own, a game. which is the key, a Key to match
on: Key::A, Key::Num7, Key::Enter, Key::Escape, Key::ArrowUp,
Key::F5, Key::Space, Key::Minus, Key::OpenBracket; and
which.is_letter(), which.is_digit() and which.is_modifier() (Shift,
Ctrl, Alt or Cmd itself, as a key going up can be). key is its name, lowercase,
its words joined by -: a, 7, enter, escape, arrow-up, f5,
space, minus, open-bracket. It's the key, not what it typed: Shift
and a is a with shift. shift, ctrl, alt and command (Cmd on
a Mac, Ctrl elsewhere) say what's held. taken says whether it was taken
from the editor (editor::take_keys) rather
than reaching the note. A plugin without keys.which gets none of them.
Only keys typed in notesy's window: nothing typed in another program ever
reaches a plugin. And no plugin hears a key at all, not even that one
went, while a secret's being typed: a passphrase, a password, or a field a
plugin's view says is secret (view::input(hint) with secret: true).
rustuse notesy::{events, log, Event, Key};
pub fn ready() {
events::on(Event::KeyDown, |key| {
match key.which {
Key::S if key.command => log::info("saved, by a plugin of its own"),
Key::Escape => log::info("let go"),
_ => {}
}
});
}
Each event object also has its kind, an Event, to match on:
rustuse notesy::{events, log, Event};
pub fn ready() {
events::on(Event::AnyNote, |event| {
match event.kind {
Event::NoteSaved => log::info(`saved ${event.path}`),
Event::NoteDeleted => log::info(`gone: ${event.path}`),
_ => {}
}
});
}
#Its own events
A big plugin's parts can talk without calling each other: a channel of its own carries events from one part to every part listening. Needs nothing.
rustuse notesy::{events, log};
pub fn ready() {
let sync = events::channel("sync");
sync.on(|done| log::info(`${done.notes} notes synced`));
sync.emit(#{ notes: 12 });
}
| Function | What it does |
|---|---|
events::channel(name) |
One of its own channels, by a name of its own (lowercase letters, digits, -, _, .). |
channel.on(handler) |
handler(value) for everything emitted on it. |
channel.emit(value) |
Sends value, plain data (numbers, text, lists and objects of those), to its handlers: after what's running now, in the order it was sent. |
channel.off() |
Forgets every handler on it. |
A channel is its plugin's alone: no other plugin hears it. At most 1,024
events wait on its channels at once; past that, emit is an error.
#Other plugins
Plugins talk to each other only when both say so. One shares, under names
of its own, with the plugins.share permission; one that wants to hear it
names it in its plugin.toml ([plugins] hears = ["ana.sync"], see
plugin.toml), which asks the user for that
too. When they approve it they see what each plugin it hears can do, and a
plugin that hears one that's since been allowed more stops until they look
again.
rustuse notesy::events;
// In Sync, which has plugins.share.
pub fn ready() {
let synced = events::share("synced");
synced.emit(#{ notes: 12, at: "2026-09-12" });
}
rustuse notesy::{events, log};
// In Board, whose plugin.toml has [plugins] hears = ["ana.sync"].
pub fn ready() {
events::from("ana.sync", "synced").on(|done| log::info(`Sync did ${done.notes} notes`));
}
| Function | What it does |
|---|---|
events::share(name) |
What it shares under name (lowercase letters, digits, -, _, .). Needs plugins.share. |
share.emit(value) |
Sends value, plain data, to every running plugin that hears it, after what's running there now. At most 20 a second, of 64 KB each; past that, emit is an error. |
events::from(plugin, name) |
What plugin shares under name: one its [plugins] hears names. |
heard.on(handler) |
handler(value) for everything it shares under that name. |
heard.off() |
Forgets every handler on it. |
A plugin that isn't running hears nothing, and nothing's kept for one that starts later.
#Ordering and dropping
Events wait in a queue for each plugin: up to 512. A plugin that falls that far behind loses the oldest (its log says how many), never a lifecycle call (Scripts).
Every page
- Overview
- plugin.toml: Every field of plugin.toml
- Permissions: What a plugin can ask for, when notesy asks, and what changes it
- Scripts: How a script runs: its lifecycle, events, limits and errors
- Packing and installing: Making, checking, signing, packing and installing
- notesy::log: Lines for its log on the Plugins page
- notesy::events: Hearing what happens, and every event
- notesy::commands: Adding commands, and running notesy's
- notesy::store: Keeping its own data
- notesy::settings: Reading its settings
- notesy::secrets: Keys and tokens, in the system keychain
- notesy::notes: Reading and changing the vault's notes
- notesy::editor: The note in front
- notesy::templates: What templates fill in, and functions of its own for them
- notesy::files: A folder of its own
- notesy::links: Opening pages in the browser, naming links after their pages
- notesy::clipboard: Copying and pasting
- notesy::view: What panels, tabs and sections show
- notesy::sections: Sidebar sections
- notesy::hidden: Hiding notes, files and folders from the tree
- notesy::status: Status bar items
- notesy::tools: Buttons in the formatting bar
- notesy::notices: Notices
- notesy::dialogs: Asking in a dialog, notesy's kinds or its own
- notesy::wizards: Pages one after another, like a setup
- notesy::boards: Boards' cards and arrows, and kinds of card of its own
- notesy::blocks: Drawing its fenced blocks
- notesy::icons: Drawing its own icons
- notesy::net: Requests to the sites it names
- notesy::ipc: Talking to a program on this computer
- notesy::accounts: Signing in to a service
- notesy::json: Reading and writing JSON
- notesy::toml: Reading and writing TOML
- notesy::yaml: Reading and writing YAML, and a note's front matter
- notesy::time: Now, written in the user's time zone, dates read, how long ago
- notesy::math: Trigonometry and the like, for drawing
- Names: Icon, Color, Tone, Side, Method, Command, Zone, Event: Notesy's names as enums
- notesy::preview: Its own screens for notesy preview
- notesy::perf: Timing its own work
- notesy::drawings: Drawings' shapes, and buttons in their tool strip
- notesy::sound: Playing sounds, and making them as it runs
- notesy::tex: Math macros for every note's formulas
- notesy::views: Side panels, tabs of its own, and its part of the tree's hover cards
- notesy::zones: Notesy's own places — its menus, its bars and its headings