Writing a plugin
A folder, a plugin.json, and an HTML file. The manifest field by field, the two sandboxes, and what a plugin's page may ask the IDE for.
There is no build step and no SDK to install. A plugin is a folder with a plugin.json and the
files it names. Write it anywhere you like, then load it from Settings → Plugins → Load
unpacked…; edit, press Reload, look.
The quickest start#
Project Manager → Projects → the Plugin switch on a project (or Semantix plugin when you
create one) marks it as a plugin. If the folder has no plugin.json yet, Semantix writes a starter
one with a working background beside it. It doesn't install it — load it with Load unpacked…
when you're ready.
While a project is marked as a plugin, the agent you chat with in it is told the plugin rules — the fields, the sandboxes, every permission — so you can ask it to build the plugin with you.
The smallest plugin: a background#
{
"id": "my-wallpaper",
"name": "My Wallpaper",
"version": "0.1.0",
"icon": "sparkles",
"contributes": {
"backgrounds": [
{
"id": "main",
"name": "My Wallpaper",
"entry": "background.html",
"sections": ["main", "panel-left", "panel-right"]
}
]
}
}background.html is just the body of a document — markup, <style> and <script>. Semantix
wraps it. Draw on a canvas, and listen for three messages from the IDE:
host:init— the first message, carrying the live theme palette inpayload.theme.host:theme— the palette again, every time it changes. Its payload is the palette:accent,surface,text,textSecondary,border,elevated, andlight(true on a light theme).host:visibility—{ visible }. Stop animating while it's false.
Every message is wrapped as { __semantix: true, v: 1, kind: 'event', type, payload }. The IDE
sends nothing until your document says it's ready:
parent.postMessage({ __semantix: true, v: 1, kind: 'event', type: 'plugin:ready' }, '*');What a background cannot do: fetch anything (no network at all), use localStorage, load files
beside it, or receive clicks. Pictures and fonts must be inline data: URIs. Two libraries are on
the shelf, imported by name from a <script type="module">: three (with
three/addons/controls/OrbitControls.js) and gsap. Nothing else resolves.
Keep loops bounded and animate on requestAnimationFrame — a loop that never yields freezes the
whole window, and the next run will hold your background back
(When it misbehaves).
A page of its own#
Give the plugin an entry — an HTML page — and it becomes a panel. Add a surface and the panel opens
as a tab, with its own button in the header:
{
"id": "note-saver",
"name": "Note saver",
"version": "0.1.0",
"entry": "index.html",
"permissions": ["workspace:read", "workspace:write"],
"contributes": { "surfaces": [{ "id": "main", "name": "Note saver" }] }
}The page talks to the IDE by message. A request is
{ __semantix: true, v: 1, kind: 'rpc-req', id, method, params }, and the answer comes back as
{ kind: 'rpc-res', id, ok, result, error } with the same id. Wrap that in a small call
helper that posts the request and waits for its answer, and writing a file into the open project
is one line:
call('saveToWorkspace', { path: 'notes/hello.md', content: '# Hello\n' });A page's sandbox is stricter than it might look. It has no network — not even its own folder
over fetch — and no browser storage (no localStorage, no IndexedDB). It can load its own
scripts, styles and images with ordinary tags. For anything it needs to keep, it asks the IDE
(plugin:storage, below).
A hidden tab is kept alive, not reloaded: you get host:visibility with visible: false. Pause and
keep your state.
Note
Settings → Plugins has a short in-app summary of this format under Writing one. This page is the full one.
What a page may ask for#
Each permission opens a small set of methods. A method the plugin didn't declare is refused with Unknown method — the same answer as a method that doesn't exist.
| Permission | What it allows |
|---|---|
workspace:read |
The open project's path and name, and events when a file or the project changes |
workspace:write |
Writing a file into the open project (missing folders are created) |
images:generate |
Rendering images and video with the user's own providers — the plugin never sees a key |
images:gallery |
Listing, saving and deleting kept renders |
media:projects |
The media editor's projects |
clipboard:read |
A pasted picture — only while focus is inside the plugin's own page |
plugin:storage |
Its own small JSON values, up to 1 MB each, kept across updates and uninstall |
plugin:chat |
Opening the plugin's chat and agent, and lending them tools |
plugin:knowledge |
Adding and removing the plugin's knowledge notes at run time |
speech:say |
Saying a line in the Studio's voice |
A render with the vendor's content filter off asks the user to confirm they are 18 or older first.
The manifest, field by field#
| Field | Required | Notes |
|---|---|---|
id |
yes | 1–64 letters, digits, -, _ or ., not starting with .. Don't reuse a built-in's id. |
name |
yes | Shown on the card. |
version, description, author |
no | Shown on the card. |
icon |
no | One of activity, blocks, flame, image, puzzle, sparkles, waves. Anything else shows the puzzle piece. |
entry |
no | The page, as a path inside the folder (or inside dist/, if there is one). No .., no leading /. Must exist. |
permissions |
no | From the table above. Meaningless without entry. |
contributes.backgrounds |
no | Each: id, name, entry (must exist), optional description and sections — any of header, panel-left, main, panel-right; main if left out. |
contributes.surfaces |
no | Tabs that open entry. Each: id, name, optional icon. Needs entry. |
contributes.knowledge |
no | A folder of .md notes for the plugin's mind. |
contributes.chat |
no | { "title": …, "instructions": "path.md" } |
contributes.agent |
no | { "name": …, "identity": "path.md", "instructions": "path.md" } — a name of up to 80 characters. |
A plugin must contribute something — a page, a background or a mind — or it won't load.
Renaming is breaking. A saved theme remembers a background by its plugin and background ids, and an open tab remembers its surface id. Change one and the old reference stops matching: the tab explains the plugin no longer adds it, and the theme falls back to nothing.
Warning
Install checks that your pages and backgrounds exist, but not your mind's files. If a knowledge, chat or agent file is missing, the plugin installs and its mind fails later, all at once. Check every path you name.
Where it is now#
Note
activationEvents is read from the manifest and ignored; plugins load when listed. A plugin's
mind can only be opened by its own page. Where something on these pages is marked coming, it
means exactly that.