feat: Add browser documentation
This commit is contained in:
204
docs-site/docs/plugin-development/examples.md
Normal file
204
docs-site/docs/plugin-development/examples.md
Normal file
@@ -0,0 +1,204 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user