notesy::panels and notesy::views

Panels show beside the note, in the right side panel; views are tabs of their own. A plugin's script adds them as it runs, says what they show, and can take them away again. Both take the ui permission.

rustuse notesy::{log, panels, view, views};

pub fn ready() {
    let tasks = panels::add("tasks", #{ title: "Tasks", icon: "check" }, tasks);
    tasks.on_click(|target, note| log::info(`clicked ${target}`));
    let board = views::add("board", #{ title: "Task board", icon: "list" }, |note| view::note("Every task, on one board."));
}

fn tasks(note) {
    let note = match note {
        Some(note) => note,
        None => return view::empty("check", "No note open", "Open a note to see its tasks."),
    };
    view::note(`${note.words()} words in ${note.title()}`)
}

#Adding them

Function What it does
panels::add(key, options, build) Adds a side panel, with a command that shows and hides it, and returns it. build(note) makes what it shows (notesy::view): note is Some(note) for the note in front, a Note (its path, text, properties, headings, tasks … a method away), or None when there's none. A plugin that may read neither the note in front nor the vault's notes (editor or notes.read) always gets None: ui alone shows nothing of your notes to it.
views::add(key, options, build) Adds a kind of tab, with an "Open" command in the palette, and returns it. build(note) gets None: a tab isn't about a note.

key names it for good: lowercase letters, digits, -, _ and ., starting with a letter. notesy remembers a pinned panel by it, so keep it the same from one version to the next. A panel and a tab can't share a key. options is its title, or #{ title, icon } (an icon by name). Adding one with a key it has already changes its title and what builds it.

#A panel, a view

Method What it does
refresh() Builds it again wherever it shows: after the script's own data changed.
on_click(handler) handler(target, path) when something in it with a target is clicked; path is the note's, or None.
on_change(handler) handler(target, value, path) when a switch, box, field or choice in it with a target changes (notesy::view); it's built again after.
on_suggest(handler) handler(target, text, path) once typing pauses in a field of it that asks for suggestions (suggest: true); it answers with suggest, then or once it knows (notesy::view).
suggest(target, text, items) What it suggests for text in its field target: a list of text, or of #{ text, detail, icon, value }. Shown while that's what was last typed; a pick reaches on_change with its value (its text, unless it has one).
open() A view only: opens it, in a tab.
show() A panel only: shows it in the side panel, as its command does (it doesn't hide it): for a command of its own, or a preview.
remove() Takes it away, with its command.

#When it's built

notesy asks the script for a view when it first shows, when the note in front changes (each edit, or another note), and after refresh. In between it draws the last one it got, so a slow script never slows the window: at worst the panel is a moment behind. Building a view runs within 5,000,000 instructions (Scripts); one that fails shows why in its place, and counts as a failure.

#Where they show

A plugin's panels are listed under its name in the list of every panel (the side panel's name, or the tab bar's panel button), where the user can pin them to the tab bar. Its views open from the palette ("Open: Task board"), like any tab. Whatever a script added goes when it stops.

Every page