plugin.toml

Every plugin has a plugin.toml at the top of its folder. notesy reads it strictly: an unknown field, a bad value or a missing one is a problem, and notesy lists every problem at once on the Plugins page rather than stopping at the first.

tomlid = "ana.tasks"
name = "Tasks"
version = "1.2.0"
author = "Ana"
license = "MIT"
description = "Your note's tasks, beside it."
repository = "https://example.com/ana/tasks"
main = "main.rn"
permissions = ["notes.read", "ui"]

#The plugin

Field Required What it is
id yes author.name: lowercase letters, digits and -, each part 1 to 40 characters. The folder it's installed in has the same name. Ids starting notesy. are notesy's.
name yes What the Plugins page calls it.
version yes A semantic version, like 1.2.0.
author yes Who made it.
license yes An SPDX expression, like MIT or MIT OR Apache-2.0.
description no One or two sentences, shown under its name.
repository no Where its source is.
main no Its script, relative to its folder (main.rn). Without one it runs nothing and needs no approval.
notesy no The plugin API it's written for, like "1.0" (a semver requirement). A notesy whose API doesn't match won't load it, and says why. This notesy's API is 1.0.0.
permissions no What it asks to do (below).
readme no What it says of itself: a markdown file in its folder (docs/Guide.md), shown from its page with a Read me button, in a tab only to read. Without one, a README.md in its folder (in any case) is. A file a link leads to from outside its folder isn't shown.

A plugin has to do something: run a script, or add at least one of the things below (themes, icons, syntax, colors, languages, stamps).

Top-level keys like readme go before the first [table] or [[table]]: after one, TOML reads them as that table's.

#Permissions

Listed in permissions, shown to the user before a plugin with a script runs, and checked on every call. The ones in bold are shown in red. How and when notesy asks, and what an update or turning one off does: Permissions.

Id What it lets a plugin do
notes.read Read your notes and their properties
notes.write Create, change, rename and delete notes
files.read Read the other files in your vault, like images and PDFs
files.write Add, change and delete the other files in your vault
editor Read and change the note you're editing, and where the caret is
keys Know when you press and let go of keys, not which ones
keys.which Know which keys you press in notesy, letters and all: what you type, as you type it
commands Add commands to the palette, with shortcuts, and run notesy's
templates Add functions your templates can call
ui Add panels, tabs, status bar items, settings pages, right-click menu items, hover card parts and dialogs
clipboard.read Read what you copy
clipboard.write Put things on your clipboard
notify Show notices
sound Play sounds
files.pick Read the files and folders you pick for it, from anywhere on this computer
files.save Save files on this computer, where you pick
keys.take Take what you type in the editor before notesy does, while it says it is, to act on it itself
links.open Open web pages in your browser
accounts Sign you in to your accounts on the sites it names
plugins.share Share what it chooses with the plugins that ask to hear it

Four more are never listed by hand: naming hosts in [net] asks for net (connect to the internet, only to those sites), naming sockets in [ipc] asks for ipc (talk to programs on this computer, only through those sockets), naming languages in [contributes] blocks asks for markdown (draw its own kinds of block in your notes), and naming plugins in [plugins] hears asks for plugins.hear (hear what they share).

The prompt warns, too, when a plugin can read what's yours and has a way to send it off: the network, opening links (a page's address can carry what it read), or sharing with other plugins.

#[net]

toml[net]
hosts = ["api.example.com", "*.example.org", "localhost:8080"]

The only sites the plugin may reach, through notesy's fetch (notesy::net). *.host means subdomains of it (not the host itself); a port can follow a colon. Plain http is allowed only to this computer and the local network, and those have to be named as such.

#[ipc]

toml[ipc]
sockets = ["run:discord-ipc-*"]

The sockets and pipes on this computer the plugin may open (notesy::ipc). The plugin owns what happens on one: it speaks whatever the program on the other end speaks, and notesy stays out of the middle. Naming one asks for ipc, and needs main.

A socket is named, never found by a path of its own. run:name is where running programs keep theirs (XDG_RUNTIME_DIR, TMPDIR on macOS, \\.\pipe\ on Windows) and own:name is the plugin's own folder for them. A name ending in * covers the names that start that way, which is how the ten Discord opens (discord-ipc-0 to -9) are named at once.

Some notesy refuses outright, whoever asks and whoever signed them: the container runtimes (a socket away from root on this machine), the agents holding your keys, the message buses that start programs for whoever asks, the display servers that see every key you press, and notesy's own. Naming one is an error where it's written, so it never reaches a prompt. Whoever answers has to be you, too: one answering as the system or as another user is refused as it opens, wherever it sits.

The prompt says this first and on its own, above what the plugin would do to your notes, because notesy can't see what passes between a plugin and a program. A plugin's page shows what it has open, with a way to hang it up.

#[plugins]

The plugins whose shares it hears (notesy::events): naming one asks for plugins.hear. Needs main; a plugin can't hear itself (its parts talk on channels of their own), and hears at most 32.

toml[plugins]
hears = ["ana.sync"]

When the user approves it, notesy shows what it hears from whom, with a caution in red when a plugin it hears can read what's yours and this one can reach the internet or open links. When a plugin it hears is later allowed more, this one stops until they approve it again, seeing what changed.

#[[accounts]]

Services the plugin's script signs the user in to (notesy::accounts). Needs main and the accounts permission; a plugin asking for accounts has to name at least one.

toml[[accounts]]
id = "github"
name = "GitHub"
authorize_url = "https://github.com/login/oauth/authorize"
token_url = "https://github.com/login/oauth/access_token"
client_id = "Iv1.0123456789abcdef"
scopes = ["read:user"]
params = { allow_signup = "false" }
hosts = ["api.github.com"]

id (what the script calls it), name (the service, as the user knows it; the approval shows it), authorize_url (its sign-in page), token_url (where the code is traded for tokens), client_id (the app the plugin registered with the service), and any of scopes and params (more parameters for the sign-in page) and hosts (more sites its token goes to, where the service's API is). Both addresses, and every one of its hosts, have to be on hosts in [net]. Its token goes with a request only to its sign-in page's host, its token address's host and its hosts: never to another site the plugin names.

#[[previews]]

Screens of its own for notesy preview, each shown by its script (notesy::preview). Needs main.

toml[[previews]]
name = "board"
summary = "The task board, with a week of sample tasks"

name (lowercase letters, digits, -, _, + and ., starting with a letter: notesy preview ana.tasks/board shows it) and summary (a line for notesy preview --list).

#[contributes]

Files notesy reads itself.

toml[contributes]
themes = ["themes/dusk.toml"]
icons = "icons"
blocks = ["chart"]
Field What it is
themes Theme files in the plugin's folder; they show in Appearance as <id>/<file name>.
icons A folder of icons (an icon pack), shown in Appearance.
blocks Fenced-code languages its script draws as pictures (```chart), with notesy::blocks. Needs main; mermaid and typst are notesy's own.

#[[settings]]

Settings notesy draws a page for (under the plugin, in Settings) and keeps in its store. A script reads them with notesy::settings.

toml[[settings]]
key = "goal"
type = "number"
label = "Daily goal"
description = "Words to write each day."
default = 500
min = 100
max = 5000
step = 50
unit = " words"
style = "slider"

Every setting has key, type, label, and may have description, default, group (settings with the same group share a heading) and visible_if (the key of a switch: shown only while it's on).

type Extra fields What the user gets
switch an on/off switch
number min, max, step, integer, unit, style (stepper, slider or field) a number
text placeholder, multiline, max_length, secret a text field; a secret one goes to the system keychain and is never shown again
choice options, style (dropdown, or segmented for at most five) one of the options
multichoice options, min_selected, max_selected any of the options
list placeholder, max_items, suggest a list of strings; with suggest = true, its script suggests as a new one's typed (notesy::settings)
color alpha a color
note a note in the vault
folder a folder in the vault

An option is a table: { value = "week", label = "This week", description = "…" }.

#[[syntax]]

Text in notes between two marks, drawn a way of its own. Needs no approval.

toml[[syntax]]
name = "mention"
open = "@["
close = "]"
color = "accent"
background = "accent_soft"
bold = true

name (lowercase letters, digits, -, _), open, close, and any of color, background (a theme color's key, one of the plugin's own [[colors]], or #rrggbb), bold, italic, underline, strike, mono. At most 16.

#[[colors]]

Colors every theme gets, which the plugin's syntax and status items (and theme files) can use as <id>/<name>. The theme studio lists them under the plugin.

toml[[colors]]
name = "due"
label = "Due dates"
dark = "#f0a35e"
light = "#b45309"
follows = "warning"

name, label, and dark and light (#rrggbb or #rrggbbaa), or follows: a theme color it takes where it has no color of its own. At most 32.

#[[languages]]

Code languages for fenced blocks, highlighted by notesy from the words and marks listed. Needs no approval.

toml[[languages]]
names = ["ledger"]
keywords = ["account", "commodity"]
types = ["USD", "EUR"]
constants = ["true", "false"]
line_comments = [";"]
block_comment = ["/*", "*/"]
strings = ["\""]
ignore_case = false
calls = true

names (what a fence may say), then either like (one of notesy's own languages these are other names for) or the lists: keywords, types, constants, line_comments, block_comment, strings, ignore_case, calls (a word right before ( is a function). At most 8 languages and 2,000 words.

#[math]

Macros every formula can use, as if written at its start. Needs no approval.

toml[math.macros]
R = '\mathbb{R}'
abs = '\left|#1\right|'

Each key is a macro's name (letters only, without the \); its value is what it stands for, with #1 to #9 for its arguments. At most 64, each at most 512 characters. A note's own math-macros property (a list of \name = body) wins over a plugin's of the same name. A script can define more as it runs, with notesy::tex.

#[spelling]

Words spelled right that the computer's dictionary may not know: a field's own, a product's names. Needs no approval.

toml[spelling]
words = ["notesy", "egui", "Rune"]
dictionaries = ["dicts/medical.aff"]

words, each one word with no spaces (at most 5,000, each at most 64 letters), and dictionaries: Hunspell dictionaries in the plugin's folder, each by its .aff with its .dic beside it (at most 4, each file at most 32 MB), read on every system, macOS and Windows too. They add to the computer's dictionary, never stand in for it: a word is spelled right when the computer's dictionary or any plugin's says so. With no dictionary on the computer, nothing is checked.

#[[icons]]

Icons of the plugin's own, for its views, panels, tabs, status items and commands to name: from images in its folder, or drawn by its script. Needs no approval.

toml[[icons]]
name = "chart"
file = "icons/chart.svg"

[[icons]]
name = "logo"
file = "icons/logo.png"
tint = false

[[icons]]
name = "count"    # no file: its script draws it

name (lowercase letters, digits, - and _), file (an SVG or a PNG in its folder, at most 256 KB; without one, its script draws it with icons::set, and it needs main), and tint: painted in the theme's colors like notesy's own icons (the default), or false to keep the image's own. At most 64. The plugin names one by name alone (icon: "chart"); anything else names it <plugin id>.chart. An icon pack can draw it differently, as with every icon; an <image> in its SVG may be inline data, never a file on this computer.

#[[sounds]]

Sound files in the plugin's folder, for its script to play by name with sound::play. Playing them takes the sound permission, and a script (main).

tomlpermissions = ["sound"]

[[sounds]]
name = "ding"
file = "sounds/ding.ogg"

[[sounds]]
name = "rain"
file = "sounds/rain.flac"

name (lowercase letters, digits, - and _) and file: a WAV, MP3, FLAC or Ogg Vorbis file in its folder, at most 16 MB, never one a link leads to. At most 64. Sound it makes as it runs needs no file: see notesy::sound.

#[[stamps]]

Shapes ready made for drawings: Excalidraw libraries (.excalidrawlib) in the plugin's folder, each item in one a stamp in every drawing's Stamps list, under the plugin's name, with its name and a picture of it. A click puts a copy in the middle of what's in view, picked. Nothing runs: stamps need no script and no permission, so a plugin can be stamps alone.

toml[[stamps]]
file = "stamps/flowchart.excalidrawlib"

file: a library as Excalidraw saves one (its library's "Export to file"), or one from https://libraries.excalidraw.com, at most 4 MB, never one a link leads to. At most 16 libraries; an item of more than 2,000 elements, or of none, is passed over. A library that can't be read says so beside the plugin's name.

#[[opens]]

Kinds of file in the vault the plugin offers to open in a view notesy has for them, rather than in the computer's own app for them. Nothing runs and the plugin reads nothing: notesy opens the file in its view and saves it back, so this needs no script and no permission, and a plugin can be this alone (example.code-editor is).

toml[[opens]]
kinds = ["code", "shell"]   # or ["*"]: code, shell, data and text together
extensions = ["ipynb"]      # and any notesy doesn't know the kind of
view = "code"
label = "Code editor"
read_only = false           # true: what it opens is only to read
Field What it is
kinds Kinds of text file, by the names notesy knows them by: code (.py, .rs, .js, .c, .go … and Dockerfiles), shell (.sh, .zsh, .ps1 …), data (.json, .yaml, .toml, .csv, .env …), text (.log, .rst …), or * for them all.
extensions Extensions besides, without their dot. Never a note's, a board's, a drawing's or a picture's: those are notesy's to open; nor an archive's, a sound's or a film's, which aren't text.
view What they open in. code: an editor for code, its lines numbered and in its language's colors (notesy's grammars, and any a plugin adds with [[languages]]), written in and saved as the file it is, with none of a note's formatting, links, properties or menus.
label What the choice is called where it's offered; the plugin's name if it doesn't say. 1 to 40 characters.
read_only true: what it opens is only to read, however it's opened (the sidebar, a link, a tab put back when notesy starts): nothing typed in it, never saved, its links opened with a click. false unless it says.

Which opens a kind is the user's to say, in Settings, Files ("Open code files"): any plugin that offers to, or the computer's app. With nothing said, the first plugin that offers to opens it; one picked and since removed or turned off gives way to it the same. A plugin offering a kind by some of its extensions alone is offered under that kind saying which ("Python (.py, .pyw)" under code files), and opens only those. A file's menu in the sidebar offers each, and the computer's app, whatever's picked. At most 8 [[opens]], of at most 64 extensions each.

A script opens a file of the vault in a tab itself with notes::open_as, only to read or as code as it says.

#What a script adds

Commands, side panels, tabs, sidebar sections and status bar items aren't named here: a plugin's script adds them as it runs, and can take them away again (notesy::commands, notesy::views, notesy::sections, notesy::status). Whatever it added goes when it stops.

Every page