Plugins
Manifest & API reference
Every field in atomcut.plugin.json, every method on window.atomcut, and the capability each one requires.
atomcut.plugin.json
The manifest is validated on install; an invalid or missing field is rejected with a readable error rather than installed half-broken. Every optional field defaults, so a minimal manifest is valid.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
manifestVersion | 1 | Yes | — | Literal — must equal 1, the current schema version. |
id | string | Yes | — | Reverse-DNS style, e.g. com.acme.confetti — alphanumeric segments joined by ., _ or -. The uniqueness key: installing a bundle whose id matches an installed plugin replaces it. |
name | string (1–60 chars) | Yes | — | Display name. |
version | string (semver) | Yes | — | e.g. 1.0.0 — how the host tells an update from a fresh install. |
description | string (≤280 chars) | No | "" | Shown under the name in the manager. |
author | { name, email?, url? } | Yes | — | name is required (min 1 char); email and url are validated if present. |
api | integer | Yes | — | The plugin protocol major you target. A manifest whose api is newer than the host's protocol version is refused at install. |
main | string | No | "ui.html" | The entry HTML file inside the bundle — loaded into the sandbox iframe. |
icon | string | null | No | null | Path to a png/svg/webp/etc. inside the bundle. null falls back to a generated initials avatar. |
capabilities | Capability[] | No | [] | The permissions this plugin asks for — see the capability table below. Declare only what you call. |
categories | string[] | No | [] | Free-form tags shown in the manager. |
window | object | No | see below | The plugin's initial floating window. |
publisherId | string | null | No | null | Reserved for signed-publisher identity (marketplace, verified authors). Parsed and stored today; not yet enforced. |
engines.atomcut | string | No | unset | A semver range the host's app version must satisfy, e.g. ">=1.4.0" — reserved for host-compatibility gating. |
window
The plugin's initial floating window. All fields are optional.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
width | integer, 1–2400 | No | 360 | Initial width, px. |
height | integer, 1–2400 | No | 560 | Initial height, px. |
minWidth | integer > 0 | No | unset | Minimum resize width. |
minHeight | integer > 0 | No | unset | Minimum resize height. |
resizable | boolean | No | true | Whether the window can be resized. |
window.atomcut
The only surface a sandboxed plugin can touch. Every method is async — a postMessage round trip to the host — except getContext(), which resolves from static boot data with no round trip. Calling a method whose capability wasn't granted rejects with an error naming the missing capability; calling an unknown method rejects too — the bridge fails closed.
| Method | Signature | Capability | Notes |
|---|---|---|---|
getContext() | (): Promise<HostContext> | none | The editor's app version and protocol version, this plugin's identity, and the capabilities actually granted (⊆ what you declared). No round trip. |
document.getInfo() | (): Promise<DocInfo> | document:read | The open document's id, name, fps, durationMs and frameCount. |
document.insertText(params) | (params: InsertTextParams): Promise<InsertTextResult> | document:write | Inserts a text layer into the active frame at the playhead. text is required; x/y offset from the frame centre in px, fontSize and color (any CSS color) are optional. Returns the new layer's clipId. |
selection.get() | (): Promise<SelectionInfo> | selection:read | The current activeFrameId, frameIds and clipIds. |
ui.notify(message, level?) | (message: string, level?: NotifyLevel): Promise<void> | ui:notify | Toasts a message in the editor. level is one of info | success | warning | error (default info). |
ui.resize(size) | (size: { width: number; height: number }): Promise<void> | none | Resizes this plugin's own window (clamped to 120–2400px per dimension). |
ui.close() | (): Promise<void> | none | Closes this plugin's window. |
on("selectionchange", handler) | (event, handler: (info: SelectionInfo) => void): () => void | selection:read | Subscribes to selection changes; call the returned function to unsubscribe. See below. |
Capabilities
The whole trust model in one table: a plugin declares the capabilities it needs in capabilities, and every bridge call is checked against what was granted. Nothing here grants raw access to app state — each capability unlocks exactly the methods listed above.
| Capability | Grants |
|---|---|
document:read | Read the document — its frames, layers and properties. |
document:write | Create, edit and remove layers in the document. |
selection:read | See and react to what the user has selected. |
ui:notify | Show toast messages in the editor. |
The selectionchange event
Subscribe with atomcut.on("selectionchange", handler); it returns an unsubscribe function. The host pushes this event whenever the active frame or the selected frames/clips change — only to plugins granted selection:read. The handler receives a SelectionInfo:
interface SelectionInfo {
activeFrameId: string | null; // the comp the editor is focused on
frameIds: string[]; // selected frames
clipIds: string[]; // selected clips/layers
}Call selection.get() once on load to read the current state, then rely on the event for changes — the same shape is returned by both.
New to plugins? Start at Plugins, or see Build a plugin for a working example.