notesy::boards

Boards are notesy's canvases: cards on an endless canvas, joined by arrows, saved as .canvas files in JSON Canvas (https://jsoncanvas.org), the open format Obsidian's canvases use. A plugin's script can read and change boards, add items to their menus (notesy::menus), and add kinds of card of its own, drawn from a view the way a panel is.

rustuse notesy::{boards, view, Icon};

pub fn ready() {
    let tally = boards::card("tally", #{ title: "Tally", icon: "number", width: 220, height: 160, data: #{ count: 0 } }, |card| {
        let tap = view::button("Tap");
        tap.icon = Icon::Plus;
        tap.target = "tap";
        view::column([view::stats([[card.data.count, "taps"]]), tap])
    });
    tally.on_click(|target, card| {
        // What a host function is handed is used up: a value used again is cloned.
        if let Some(found) = boards::get(card.path.clone(), card.id.clone()) {
            boards::update(card.path, card.id, #{ data: #{ count: found.data.count + 1 } });
        }
    });
}

#Cards and arrows

A board is #{ nodes, edges }, as JSON Canvas writes it. A card (a node) has id, type (text, file, link or group), x, y, width, height and maybe color ("1" to "6", notesy's red, orange, yellow, green, cyan and purple, or a hex color), and by its type: text (markdown), file (its path in the vault) and subpath (#Heading), url, or a group's label. An arrow (an edge) has id, fromNode, toNode, and maybe fromSide and toSide (top, right, bottom, left), fromEnd and toEnd (none or arrow), label and color. Fields another app added are kept as they are.

Boards are named by their path in the vault, with or without .canvas: "Plans", "Projects/Q3.canvas".

Function Takes What it does
boards::read(path) notes.read The board, #{ nodes, edges }; None when there's none
boards::get(path, id) notes.read Card id of the board; None when there's no such card
boards::current() notes.read The board in front, #{ path, selected } (the ids of the cards and arrows picked); None when a board isn't in front
boards::open(path) notes.read Opens the board in a tab, in front
boards::write(path, board) notes.write Puts board (#{ nodes, edges }) in place of what's there; makes the board when there's none
boards::add(path, card) notes.write Adds a card (a new id when it has none) and returns its id. #{ card: "<key>", x, y } adds one of its own kinds of card
boards::update(path, id, fields) notes.write Puts fields over those of card or arrow id; () takes a field away. A card of its own kind keeps data with it
boards::connect(path, from, to, options) notes.write An arrow from card from to card to, with its other fields from options (#{ label, color, toEnd } …); returns its id
boards::remove(path, ids) notes.write Takes a card or an arrow away, or a list of them by id, and the arrows of the cards

A change to a board that's open shows at once and is a step the user can undo; one that isn't open is written to its file.

#Kinds of card of its own

Function What it does
boards::card(key, options, build) Adds a kind of card, and returns it. build(card) makes what a card of it shows, a view (notesy::view).

It takes the ui permission, and notes.read as well: a card is on a board in the vault. key names it as a menu item's does. options is #{ title, icon, width, height, data }: the title the board's menu shows it by ("Add a plugin's card"), its icon, a new card's size (260 by 160 unless it says), and what a new card's data starts as. card is the card, as boards::get gives it, with its data and the board's path.

A card of its own is a text card as JSON Canvas writes it, its title for text, with "notesy": { "card": "<plugin id>/<key>", "data": … } beside the rest: another app (or notesy without the plugin) shows its text.

It's drawn in the board's own units, zoomed with the board. notesy builds it again when the card changes (its data, its size, its color) and after refresh, and draws the last one built in between, within a view's budget (Scripts).

Method What it does
on_click(handler) handler(target, card) when something in one of its cards with a target is clicked: card is #{ id, path }
on_change(handler) handler(target, value, card) when a switch, box, field or choice with a target changes
refresh() Builds every card of this kind again: after the script's own data changed
remove() Takes the kind away: its cards show their text again

Every page