205 lines
4.9 KiB
Markdown
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.
|