--- 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.