Files
Toju/docs-site/docs/plugin-development/examples.md
2026-04-29 17:15:01 +02:00

205 lines
4.9 KiB
Markdown

---
sidebar_position: 5
---
# Examples
## Toolbar Message Plugin
`toju-plugin.json`
```json
{
"schemaVersion": 1,
"id": "example.toolbar-message",
"title": "Toolbar Message",
"description": "Adds a toolbar action that sends a reusable message.",
"version": "1.0.0",
"kind": "client",
"scope": "client",
"apiVersion": "1.0.0",
"compatibility": {
"minimumTojuVersion": "1.0.0",
"verifiedTojuVersion": "1.0.0"
},
"entrypoint": "./main.js",
"capabilities": ["messages.send", "ui.pages"]
}
```
`main.js`
```js
export function activate(context) {
const { api } = context;
context.subscriptions.push(api.ui.registerToolbarAction('standup-message', {
label: 'Standup',
run: () => api.messages.send('Standup: yesterday, today, blocked')
}));
}
```
## Settings Page Plugin
```json
{
"schemaVersion": 1,
"id": "example.settings-page",
"title": "Settings Page Example",
"description": "Adds a plugin settings page and stores a local preference.",
"version": "1.0.0",
"kind": "client",
"apiVersion": "1.0.0",
"compatibility": { "minimumTojuVersion": "1.0.0" },
"entrypoint": "./main.js",
"capabilities": ["ui.settings", "storage.local"],
"settings": {
"type": "object",
"properties": {
"enabled": { "type": "boolean", "default": true }
}
}
}
```
```js
export function activate(context) {
const { api } = context;
context.subscriptions.push(api.ui.registerSettingsPage('preferences', {
label: 'Example Preferences',
render: () => {
const root = document.createElement('section');
const button = document.createElement('button');
button.type = 'button';
button.textContent = 'Remember preference';
button.onclick = () => api.storage.set('enabled', true);
root.append(button);
return root;
}
}));
}
```
## Server-Scoped Soundboard
A server-scoped plugin can be installed as a server requirement and auto-installed for server members when marked required.
```json
{
"schemaVersion": 1,
"id": "example.soundboard",
"title": "Server Soundboard",
"description": "Adds a soundboard side panel and announces played sounds.",
"version": "1.0.0",
"kind": "client",
"scope": "server",
"apiVersion": "1.0.0",
"compatibility": { "minimumTojuVersion": "1.0.0" },
"entrypoint": "./main.js",
"capabilities": [
"server.read",
"users.manage",
"ui.sidePanel",
"media.playAudio",
"messages.send"
],
"pluginUser": {
"displayName": "Soundboard",
"label": "Audio helper"
}
}
```
```js
export function activate(context) {
const { api } = context;
const botId = api.server.registerPluginUser({
id: 'soundboard-bot',
displayName: 'Soundboard'
});
context.subscriptions.push(api.ui.registerSidePanel('sounds', {
label: 'Soundboard',
render: () => {
const panel = document.createElement('div');
const button = document.createElement('button');
button.type = 'button';
button.textContent = 'Play chime';
button.onclick = async () => {
await api.media.playAudioClip({ url: './chime.wav', volume: 0.7 });
api.messages.sendAsPluginUser({ pluginUserId: botId, content: 'Played chime' });
};
panel.append(button);
return panel;
}
}));
}
```
## Message Bus Plugin
```json
{
"schemaVersion": 1,
"id": "example.poll-bus",
"title": "Poll Bus",
"description": "Uses the plugin message bus for lightweight P2P poll votes.",
"version": "1.0.0",
"kind": "client",
"apiVersion": "1.0.0",
"compatibility": { "minimumTojuVersion": "1.0.0" },
"entrypoint": "./main.js",
"capabilities": ["events.p2p.publish", "events.p2p.subscribe", "messages.read"]
}
```
```js
export function activate(context) {
const { api } = context;
context.subscriptions.push(api.messageBus.subscribe({
topic: 'poll:votes',
replayLatest: true,
latestMessageLimit: 20,
handler: (event) => api.logger.info('Vote received', event.payload)
}));
api.messageBus.publish({
topic: 'poll:votes',
payload: { option: 'A' },
includeLatestMessages: true,
includeSelf: true,
latestMessageLimit: 20
});
}
```
## Custom DOM Mount
Use `ui.dom` sparingly and cleanly. The runtime tags mounted elements with plugin ownership metadata and removes remaining mounted elements when the plugin unloads.
```js
export function activate(context) {
const badge = document.createElement('div');
badge.textContent = 'Plugin active';
badge.style.position = 'absolute';
badge.style.right = '1rem';
badge.style.bottom = '1rem';
context.subscriptions.push(context.api.ui.mountElement('active-badge', {
target: 'body',
element: badge
}));
}
```
## All-API Fixture
The repo includes an E2E fixture at `toju-app/public/plugins/e2e-all-api/`. It intentionally calls every public plugin API surface so Playwright coverage can validate the runtime. Use it as a compatibility reference, not as the minimal style for production plugins.