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