MojoPad

Scripting: Event Pages

Event pages are plugins that run themselves. Name a page Event: Name and its script fires when that thing happens — the page is the handler.

Page nameFiresmojo.event contains
Event: Openwhen the document openstype
Event: Page Viewafter navigating to any pagetype, page
Event: Exportbefore any document exporttype, destination
Event: Publishbefore Static Publishing writes the sitetype, destination (the folder)
Event: Daily Notewhen a daily note is createdtype, page, date
Event: Research Donewhen a research run's report has been written — whether or not you are looking at ittype, page, asked (the question)

A run finishing is the useful one to hang work on. A run works on its own for as long as it takes, so the answer usually arrives while you are somewhere else. Event: Research Done is handed the report's name, and the report is an ordinary page: read it, pull out what you need, and do the next thing without being there. A plugin that collects the sources a report cites — fetching each one with mojo.fetchData and keeping it with mojo.addFile — is a few lines, and runs while you are making coffee.

How big a file a script may fetch. mojo.fetchData will bring back a file of up to 25 megabytes, which is room for a published paper with its figures. Reading a page as text with mojo.fetch has a smaller ceiling, because a page you are reading has no business being that large. Going over either one returns nothing and says so, naming the limit — worth knowing, because from inside a script a file that was too big and a paper behind a paywall both arrive as nothing at all.

The report cannot run anything itself. It was written by a model, so it is marked as having arrived from elsewhere and never runs, whatever it contains. What runs is the page you wrote, handed the report's name to read. That distinction is the whole reason this is safe.

Inside, the full mojo API is available, plus mojo.event with the details above. A tiny example — keep a visit log. Create Event: Page View containing:

if (!mojo.exists('Log')) await mojo.create('Log')
mojo.append('Log', mojo.today() + ' — ' + mojo.event.page)

Event scripts won't trigger themselves recursively, and errors flash in the status bar instead of interrupting you.

Trying one, and finding out why one didn't run

An event fires while you are somewhere else, which is exactly when you cannot watch it. So open the Scripts palette — it is a tab in the side panel, and also Plugin ▸ Open Script Runner. Every event page in the document is listed there under Event pages, with a Test button beside it. The same pages appear under Plugin ▸ Test Event Page, which does the same thing from the menu. Test runs the page there and then, with a stand-in mojo.event carrying the page you are on, so you can try your code without waiting for a research run to finish or a document to be reopened.

Every run — from the runner, from the Plugin menu, and from an event page, whether you started it or the event did — is kept under Recent runs in that same palette, with its output and the line any failure happened on. That list holds the last few runs, so you can compare this one against the one before it.

And when an event page does not run at all, that is recorded too, with the reason — most often that the page arrived from elsewhere, or that scripts are turned off for this document. A page that simply isn't there is not reported: most documents have no page for most events, and saying so on every visit would be noise. So if nothing appears at all, check the name first — the Event pages list shows every page MojoPad will match, and a page missing from it is a page no event will ever find. The reason is kept once per session, so an event that fires often cannot crowd out the rest of the list.

Only pages you wrote will run

A page that arrived never runs its own code, whatever it is called. That means anything brought in by following a folder, clipping from the web, importing, opening a link, or an AI agent writing into your wiki: those pages can be read, searched, edited and copied like any other, but naming one Event: Open does not make it run when you open the document, and a scriptlet inside one is shown rather than carried out.

This matters because trusting a document is a decision you make once, about the pages in front of you at the time. A followed folder can gain a file next week, and whoever can write into that folder — a sync service, a shared drive, somebody else's computer — would otherwise be writing code that runs on yours under a permission you gave for your own notes. So the answer is per page as well as per document: what you wrote runs, what turned up doesn't.

If you do want an arrived page to run, copy what it says into a page you make yourself. That is one extra step, and it is the step where you read what you are about to run. Editing the page in place will not do it — the mark stays with the page for as long as it exists. For an Event: page, make a new page, paste the text in, give it the exact event name, and delete the one that arrived. (Duplicate names the copy “something copy”, which is not the name of any event, so a duplicate runs but never fires.)

The mark itself, and what carries it

Worth naming, because you cannot see it and you can destroy it. The mark is a field on the page called origin. A page you wrote does not have it at all — that absence is what makes the page yours — and a page that arrived has it set to ambient. A page whose words live in a file on your disk carries external instead, and is treated the same way for the same reason: its body is whatever the file says today.

You will never meet either field working in MojoPad. It matters if something else writes your pages: a script of your own editing the document’s files directly, a backup you are restoring by hand, a tool that rewrites pages in bulk.

The rule for anything writing a page from outside: change the words, leave the bookkeeping exactly as you found it. Four fields are decisions rather than content, and a writer that drops one it did not recognize changes what MojoPad believes about the page:

  • origin — whether the page arrived from elsewhere. Removing it from a page that has it promotes that page to one that may run code. Adding it to a page of your own quietly stops your event pages and scriptlets from running, with no error anywhere.
  • external — the file this page’s words really live in. Inventing one makes a page claim to be a file it is not; dropping one severs a link you set up.
  • trashed — the day the page was deleted. A page carrying it is in Recently Deleted, not in your wiki; clearing it restores the page, and setting it deletes one.
  • encrypted — the page is sealed. Its readable text is genuinely not in the file, and writing plain words beside the sealed ones leaves a page that is protected and legible at the same time.

Editing the words of an Event: page in its file does not stop it running, which is the reassuring half: origin is not recomputed from anything, so a page you wrote stays a page you wrote however many times its text is rewritten. What would stop it is writing the field in, or replacing the file with one copied from a page that has it.