macotron.panel
Open a floating window that shows your HTML or a web page, and pass messages between the page and your plugin.
// Ask for a name, then greet it.
const html = `
<h2>Your name</h2>
<input id="input" placeholder="Ada">
<button class="primary" onclick="send()">Greet</button>
<script>
function send() {
webkit.messageHandlers.macotron.postMessage({ name: input.value });
}
</script>`;
const id = macotron.panel.open({ title: "Greet", width: 320, height: 160, html, closeOnBlur: true });
macotron.panel.onMessage(id, ({ name }) => {
macotron.notify.toast("Hello", name);
macotron.panel.close(id);
});
Opening and closing
open(opts: PanelOptions): string
Opens a panel and returns its id. The panel floats above other windows, shows on every Space, and opens centered with keyboard focus. Throws a TypeError when opts is missing or url is not an http or https URL.
| Option | Type | What it does |
|---|---|---|
title | string | Title bar text. Default "Macotron". |
width, height | number | Content size in points. Default 420 x 520. |
html | string | Body markup. Macotron wraps it in a page with the system font, padding, form styles and light and dark colors. See The host page. |
rawHtml | string | A complete HTML document, used as is with no host styles. It takes priority over html. |
url | string | An http or https page to load instead of html or rawHtml. It loads first-party, so its cookies and localStorage persist between opens, unlike an iframe. |
glass | boolean | string | true or "regular" for Liquid Glass, "clear" for the clearer Liquid Glass, "translucent" for a HUD blur. The host page background becomes transparent so the material shows through. |
frameless | boolean | No title bar, with rounded corners. Escape closes it. |
fullscreen | boolean | Covers the whole screen under the pointer, with no title bar. Escape closes it. |
escapeCloses | boolean | Default true. Pass false so Escape does not close a frameless or fullscreen panel, and close it from your own button instead. |
closeOnBlur | boolean | Closes the panel when it loses keyboard focus, for example when the user clicks another window. |
id | string | Use this id instead of a random one. An open panel with the same id closes first, so calling open again replaces it. |
qr | string | Text to encode as a QR code. Macotron appends the code as an <img> to the end of html or rawHtml. |
In every panel, Command-Plus and Command-Minus zoom the page and Command-0 resets the zoom.
close(id: string): void
Closes the panel. Does nothing for an unknown id.
focus(id: string): boolean
Brings an open panel to the front and gives it keyboard focus without reloading the page. Returns false when no panel has that id. Use it to show a panel again instead of opening a second one.
Every panel closes when plugins reload.
Messages
postMessage(id: string, data: any): void
Sends data to the page as JSON. Messages sent before the page finishes loading wait and arrive in order, so you can call this right after open. Send any value JSON can hold: an object, an array, a string, a number, a boolean or null. Other values, such as a function, arrive as null.
onMessage(id: string, callback: (data: any) => void): void
Runs callback for each message the page sends. You can add more than one handler. Handlers are dropped when the panel closes, so add them again after each open.
If you pass only a callback, onMessage(callback), it receives messages from every panel the plugin opens and stays until plugins reload.
In the page
The page talks to the plugin with three functions that every panel provides:
webkit.messageHandlers.macotron.postMessage(data)sendsdatato theonMessagehandlers.window.__macotronReceive = (data) => { … }receives eachpostMessagefrom the plugin. Amessageevent onwindowalso fires, with the value inevent.data.window.close()ormacotronClose()closes the panel.
// Page side: render whatever the plugin sends.
window.__macotronReceive = (d) => {
if (d && d.html) document.getElementById("list").innerHTML = d.html;
};
The host page
With html, the body is a flex column with 16px padding and a 12px gap, in the system font at 13px. Inputs, text areas, selects and buttons are styled to match macOS in light and dark mode. When the panel opens, or gets focus again, the first element with autofocus, then #input, then the first text area or input, gets the focus.
| Class | What it does |
|---|---|
button.primary | A button in the accent color. |
button.secondary | A quieter button. |
button.block | A full-width, left-aligned button, for lists of choices. |
.toolbar | A row of inputs, selects and buttons at one height. Inputs stretch to fill. |
.grow, .scroll | Fill the leftover height, and scroll the overflow. |
.muted, .ok, .bad | Secondary, green and red text. |
.mono | The monospaced system font. pre and code use it too. |
input.inline | An input at its natural width instead of full width. |
The page also defines CSS variables for the system colors: --macotron-accent, --macotron-accent-text, --macotron-label, --macotron-secondary-label, --macotron-fill, --macotron-control, --macotron-control-text, --macotron-control-border, --macotron-field, --macotron-field-text, --macotron-selected, --macotron-selected-text and --macotron-link.
Events
panel:closed
Fires when a panel goes away for any reason: the close button, Escape, closeOnBlur, close(), or open with the same id. The payload is { id }.
macotron.on("panel:closed", ({ id }) => {
if (id === "chat") localStorage.removeItem("chat.draft");
});
See also
- macotron.menubar for HTML rows inside a menu.
- macotron.launcher for picking from a list without a window.
- The
browser-picker.js,emoji.jsandai-chat.jsplugins.