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 is not a way round it: it names the copy “something copy”, which is not the name of any event, and a copy of a page that arrived runs nothing until you say yes to it by name — see Code that came with words MojoPad copied for you, below.)

Scripts that changed outside MojoPad

You keep a wiki in a synced folder, or put one back from a backup, or have a program of your own that writes its files — and one day MojoPad asks about scripts you already said yes to. That question is this section. Your yes to a document’s scripts covers the pages that can run as you left them: MojoPad remembers each page that carries code and may run it, outside the wiki, the way it last saved that page itself or the way the page stood when you last said yes.

When the wiki opens and one of those pages is not as it was — or a new one is there — and MojoPad on this Mac did not make the change, your yes does not reach that page. Before anything in the wiki runs, MojoPad asks again and names the pages, each marked new or changed. Run Scripts accepts them as they are now, and you are not asked about them again. Don’t Run Scripts runs nothing in that wiki until you quit MojoPad and forgets nothing: the same question comes back the first time you open the wiki after starting MojoPad again.

To look before you answer, say no. The question comes up before the wiki has drawn a page, so there is nothing to read behind it. Choose Don’t Run Scripts, open the pages it named, and read them: nothing runs meanwhile, and MojoPad asks again the next time it starts. A page you do not want can simply be deleted. A page in Recently Deleted runs nothing, so MojoPad does not ask about it while it is there. It is asked about if something outside MojoPad takes it out again, or if you put back a page you had never said yes to; until you answer, nothing of it runs in the window you put it back in. Another window already open on that wiki is not told, and can run it until that window is closed.

Which pages these are. A page named Event: or Plugin:, a Clipping: page whose words begin #! (formatted or not), and any page holding a scriptlet. A scriptlet written into a page’s file in a way MojoPad itself never writes one does not make the page one that carries code: a page with no other code runs nothing, on any export or preview. If you type a scriptlet of your own into such a page (in a Markdown page’s source too), or MojoPad saves it with that scriptlet now in its own form, it still runs nothing: the code does not count as yours, and the page is asked about by name at the next open. A yes to a page the question names runs its code as it is written, however that is. The Scripts panel says so when a page’s code is held back this way.

When it is your own doing, you are asked once. Editing an Event page on your other Mac, putting back a backup with older scripts in it, having a tool of your own rewrite a page that holds a scriptlet: from here these all look the same — a page that can run, changed by something other than this copy of MojoPad. Say yes and it does not come back. What you write in MojoPad on this Mac never raises it: writing a plugin, renaming a page into an Event page, typing a scriptlet, deleting a script page. Code that comes onto a page with words MojoPad copies there for you can — see Code that came with words MojoPad copied for you, below.

When it is not your doing, it is the warning you would want. A page called Event: Open runs the moment a document opens, and a page file that says nothing about where it came from counts as one you wrote. So anything that can write a file into the wiki’s folder — a sync service, a shared drive, a program you pointed at the wiki that was itself steered by something it read — could otherwise add such a page and have it run under the yes you gave for your own pages. Read the pages the question names before you say yes to them.

What this does not do. What MojoPad remembers is kept in its own settings on this Mac, so it holds against something that writes the wiki’s files, not against a program that can rewrite MojoPad’s settings as well. The first time this version of MojoPad opens a wiki, it takes the script pages it finds as its starting point without asking: those are the pages your yes already covered. Run Page as Shell Script runs whichever page is in front of you, so an ordinary page is not one of the pages above: MojoPad does not ask about a change made to it outside before running it that way. Read the page before you run it. And MojoPad tells a scriptlet you typed from one that came with a page’s file by whether the page gained the percent signs a scriptlet is written with, and, in a Markdown page that held no code, by whether its source writes as one every scriptlet its rendering finds. It leans toward asking. It holds what a page has now against what it last saved of it, and it saves a page a moment after you stop typing: take out words that held a percent sign and type a scriptlet before then (replace “50%” with one, or delete a line that held one and go straight on), and that page may be held back and asked about once, though you typed it.

Code that came with words MojoPad copied for you

You make a page from a template somebody sent you, put back an older version of a page, merge two pages, or duplicate one — and a scriptlet on the page it gives you does not run. The Scripts panel says why, and the next time you open the wiki MojoPad asks about that page by name. This section is why.

  • A template from someone else. A colleague’s meeting template, a daily-note template from a shared wiki: its words become the first words of every page you make from it.
  • An old version. Edit ▸ Page Versions… puts back words that were kept in a file beside the page.
  • Another page’s words. A merge (⌥-drag in the graph, or Merge beside a likely duplicate), Duplicate Page, Save as Template…, Copy a Page in Here, a sticky made into a page, an answer saved as a page, a page a script makes, a page copied in from another wiki.
  • A model’s words in a page you are writing. An Ask answer you put in with ⤵ Insert, a passage ✦ Edit with AI rewrote (Apply or Keep both), and what Write them in or Reference list in Insert from Your Sources… puts on the page.

Your yes to a document’s scripts covers what you type in MojoPad and what it asked you about by name. All of the above put words on a page that you did not type there, and those words came from somewhere that can be written without you: a template page, a saved version, another page, another wiki, a model. A template can hold a scriptlet written so that nothing calls the template code, so the question at the open never names it; copied onto a new page, MojoPad would have written it out in its own form and run it as if you had typed it.

So code that comes with such words, and that nothing has vouched for, waits for you. Nothing on that page runs while the wiki stays open, and at the next open MojoPad asks about it, naming the page: it says MojoPad copied code into it from somewhere other than your typing, and cannot tell whether you wrote it. It may be yours — a version you put back can hold a scriptlet you wrote months ago — and MojoPad asks rather than guess. Run Scripts lets it run from then on; Don’t Run Scripts runs nothing in the wiki and asks again the next time MojoPad starts. To read the page first, say no, open it, and look.

What is vouched for runs as it always did. A template’s own scriptlets still run as the page is made, and what they write is only words. Your own pages, duplicated or merged, carry their code along and it runs. A version that holds only the code the page has now runs as before. A page made from what you paste (File ▸ New Page from Clipboard) is yours, as a paste into a page is. And anything you type on the new page afterwards is yours, and runs. What waits: code the words bring that was not running before — a scriptlet a template’s file held in disguise, a version holding code the page does not have now, a copy of a page whose code may not run here (one that arrived from outside, or one in a wiki whose scripts you said no to), a page of code copied in from another wiki, a scriptlet in a model’s words that the page did not already have. (A passage of yours that Edit with AI gives back with your scriptlet unchanged brings nothing new, and runs.)

Adding words is not adding code. The ways words are added to a page you already have — an assistant’s line, an emailed note, Explode Page…, Create Page from Selection, Move Selection to Page…, a script’s append — refuse anything that would run, as they always have. They now count it the way an export or a preview would see it, not only as it is stored, so a scriptlet spelled to look like words is refused too.

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 makes that page one that may run code, and MojoPad asks about it by name at the next open before anything runs (see Scripts that changed outside MojoPad, above). 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. A page that can run code and is restored this way is asked about, by name, at the next open before anything runs.
  • 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 make it a page that arrived: origin is not recomputed from anything, so a page you wrote stays a page you wrote however many times its text is rewritten. What it does do is change a page that can run, outside MojoPad — so at the next open MojoPad asks whether the document’s scripts may run, names that page as changed, and runs nothing until you say yes. What would stop the page running for good is writing the field in, or replacing the file with one copied from a page that has it.