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 name | Fires | mojo.event contains |
|---|---|---|
Event: Open | when the document opens | type |
Event: Page View | after navigating to any page | type, page |
Event: Export | before any document export | type, destination |
Event: Publish | before Static Publishing writes the site | type, destination (the folder) |
Event: Daily Note | when a daily note is created | type, page, date |
Event: Research Done | when a research run's report has been written — whether or not you are looking at it | type, 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.
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.
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.)
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.