> ## Documentation Index
> Fetch the complete documentation index at: https://empty-ad9a3406.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Event Bus

> Pub/sub system for plugin-to-plugin communication

## Overview

The Event Bus (`api.bus`) is a simple pub/sub system that lets plugins communicate without importing each other.

```javascript theme={null}
// Plugin A emits an event
api.bus.emit('todo:added', { text: 'Buy milk', done: false });

// Plugin B listens for it
api.bus.on('todo:added', (data) => {
  console.log('New todo:', data.text);
});
```

## API

### `bus.on(event, callback)`

Subscribe to an event.

```javascript theme={null}
api.bus.on('theme:changed', (data) => {
  document.body.style.background = data.color;
});
```

### `bus.off(event, callback)`

Unsubscribe. Must pass the same function reference.

```javascript theme={null}
function handler(data) { /* ... */ }

api.bus.on('my-event', handler);
// later:
api.bus.off('my-event', handler);
```

### `bus.once(event, callback)`

Subscribe to an event, but auto-remove the listener after the first emission.

```javascript theme={null}
api.bus.once('board:ready', (data) => {
  console.log('Ready! (only fires once)');
});
```

### `bus.emit(event, data)`

Publish an event to all subscribers. Errors in listeners are caught and logged without interrupting other listeners.

```javascript theme={null}
api.bus.emit('theme:changed', { color: '#1a1a2e' });
```

### `bus.removeAll([event])`

Remove all listeners for a specific event, or all events if no name is given.

```javascript theme={null}
api.bus.removeAll('todo:added');  // Clear all listeners for this event
api.bus.removeAll();              // Nuclear option: clear everything
```

## Naming Convention

Use `namespace:action` to avoid collisions between plugins:

| ✅ Good                | ❌ Bad    |
| --------------------- | -------- |
| `todo:added`          | `added`  |
| `kanban:card:moved`   | `move`   |
| `theme:changed`       | `change` |
| `sticky-note:created` | `new`    |

## Built-in Events

| Event                    | Payload                               | When                                |
| ------------------------ | ------------------------------------- | ----------------------------------- |
| `board:ready`            | `{ boardEl }`                         | Board initialized                   |
| `board:allPluginsLoaded` | `{ total, enabled }`                  | All plugins loaded                  |
| `board:restarted`        | —                                     | Board restarted via `api.restart()` |
| `board:resize`           | `{ width, height }`                   | Window resized                      |
| `plugin:loaded`          | `{ id, meta, container }`             | Single plugin loaded                |
| `plugin:unload`          | `pluginId`                            | Plugin toggled off or deleted       |
| `plugin:unloaded`        | `pluginId`                            | Plugin fully torn down              |
| `plugin:docked`          | `{ pluginId, el, target }`            | Plugin docked                       |
| `plugin:undocked`        | `{ pluginId, el }`                    | Plugin undocked                     |
| `plugin:updated`         | `{ pluginId, el }`                    | Plugin container modified           |
| `plugin:dragstart`       | `{ el }`                              | Drag started                        |
| `plugin:dragend`         | `{ el }`                              | Drag ended                          |
| `contextmenu:open`       | `{ x, y, menu }`                      | Right-click menu opened             |
| `storage:change`         | `{ key, value, oldValue, pluginId? }` | Storage value changed               |

See [Built-in Events](/api/events) for full details.

## Example: Cross-Plugin Communication

```javascript theme={null}
// === todo-list/plugin.js ===
export function setup(api) {
  const todos = [];

  function addTodo(text) {
    todos.push({ text, done: false });
    api.bus.emit('todo:added', { text, count: todos.length });
  }
}

// === stats/plugin.js ===
export function setup(api) {
  let count = 0;

  api.bus.on('todo:added', (data) => {
    count = data.count;
    renderStats();
  });
}
```

<Warning>
  Plugins should never directly import or reference each other. Always use the event bus for communication.
</Warning>
