Files
cinny/docs/PLUGIN_BUTTON_API.md
2026-04-22 00:39:42 +10:00

433 lines
9.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plugin Button Registration API
An easy way to register plugin buttons in various UI locations with positioning and grouping support.
## Example Plugins
- **[example-button-plugin/](example-button-plugin/)** - Simple examples showing basic usage patterns
- **[example-showcase-plugin/](example-showcase-plugin/)** - Complete demonstration of ALL 11 locations ⭐
## UI Locations
Buttons render in two visual styles depending on location — **nav list rows** (icon + label, full width) or **icon buttons** (compact, toolbar/header style).
### Nav List Rows
These render as full-width list entries matching the style of built-in items like "Create Room" and "Message Search":
| Location | Where it appears |
|---|---|
| `channel-list` | Space channel list, below Lobby and Message Search |
| `home-section` | Home panel, inside the Rooms category above the room list |
| `direct-messages` | Direct Messages panel, below "Create Chat" and above the CHATS dropdown |
### Icon Buttons
These render as compact icon buttons inline with other toolbar/header controls:
| Location | Where it appears |
|---|---|
| `text-composer-toolbar` | Message composer toolbar (alongside emoji, sticker buttons) |
| `composer-actions` | Left side of the composer, beside the `+` attach button |
| `room-header` | Top room header bar, before the ⋮ menu button |
| `room-menu` | Room ⋮ dropdown menu |
| `message-actions` | Message hover action bar |
| `user-menu` | Right-click popup on the user avatar |
| `search-notification-section` | Notifications page header (right side) |
| `sidebar-actions` | Left sidebar — appears in **two** places: above the Explore Servers icon, and above the Search icon in the sticky bottom section |
> 💡 **Tip:** Install the [example-showcase-plugin](example-showcase-plugin/) to see exactly where each location appears in the UI!
## Basic Usage
### Simple Button
```javascript
module.exports = {
name: 'my-plugin',
version: '1.0.0',
onLoad: (context) => {
context.ui.registerButton({
id: 'my-button',
location: 'text-composer-toolbar',
label: 'My Action',
icon: '🎨',
onClick: () => {
context.log('Button clicked!');
}
});
},
onUnload: () => {
// Buttons are automatically cleaned up on plugin unload
}
};
```
## Positioning
### Before/After Positioning
Place your button before or after existing buttons:
```javascript
context.ui.registerButton({
id: 'my-button',
location: 'text-composer-toolbar',
label: 'My Action',
icon: '✨',
position: {
before: 'emoji-picker-button', // Place before emoji picker
// OR
after: 'sticker-button' // Place after sticker button
}
});
```
### Grouping Buttons
Group related buttons together:
```javascript
context.ui.registerButton({
id: 'action-1',
location: 'text-composer-toolbar',
label: 'Action 1',
icon: '1⃣',
position: {
group: 'my-plugin-tools',
order: 1 // Lower numbers appear first
}
});
context.ui.registerButton({
id: 'action-2',
location: 'text-composer-toolbar',
label: 'Action 2',
icon: '2⃣',
position: {
group: 'my-plugin-tools',
order: 2
}
});
```
### Combined Positioning and Grouping
Place a group before/after other elements:
```javascript
context.ui.registerButton({
id: 'tool-1',
location: 'text-composer-toolbar',
label: 'Tool 1',
icon: '🔧',
position: {
group: 'my-tools',
after: 'emoji-picker-button',
order: 1
}
});
context.ui.registerButton({
id: 'tool-2',
location: 'text-composer-toolbar',
label: 'Tool 2',
icon: '🔨',
position: {
group: 'my-tools',
order: 2
}
});
```
## Complete Example
```javascript
module.exports = {
name: 'custom-tools',
version: '1.0.0',
onLoad: (context) => {
// Nav list row — appears below "Create Chat" in the DMs panel
context.ui.registerButton({
id: 'dm-favorites',
location: 'direct-messages',
label: 'Favourites',
icon: '⭐',
onClick: () => context.log('Favourites clicked!')
});
// Nav list row — appears in the space channel list
context.ui.registerButton({
id: 'quick-access',
location: 'channel-list',
label: 'Quick Access',
icon: '⚡',
onClick: () => context.log('Quick access clicked!')
});
// Icon button — appears in the message composer toolbar
context.ui.registerButton({
id: 'format-bold',
location: 'text-composer-toolbar',
label: 'Bold',
icon: '𝐁',
position: {
group: 'custom-formatting',
order: 1
},
onClick: () => context.log('Bold clicked!')
});
// Icon button — appears in the room header
context.ui.registerButton({
id: 'special-search',
location: 'room-header',
label: 'Special Search',
icon: '🔍',
position: { after: 'search-button' },
onClick: () => context.log('Special search clicked!')
});
// Sidebar icon — appears above Explore and above Search
context.ui.registerButton({
id: 'plugin-panel',
location: 'sidebar-actions',
label: 'Plugin Panel',
icon: '🧩',
onClick: () => context.log('Plugin panel clicked!')
});
},
onUnload: () => {
// All registered buttons are automatically cleaned up
}
};
```
## Unregistering Buttons
Buttons are automatically unregistered when the plugin is unloaded. To manually unregister:
```javascript
context.ui.unregisterButton('my-button-id');
```
## TypeScript Types
```typescript
import type { UIButtonDefinition, UIButtonPosition, UILocation } from '@paarrot/plugin-manager';
const button: UIButtonDefinition = {
id: 'my-button',
location: 'text-composer-toolbar',
label: 'My Action',
icon: '🎨',
position: {
group: 'my-tools',
after: 'emoji-picker',
order: 1
},
onClick: () => console.log('Clicked!')
};
```
## Basic Usage
### Simple Button
```javascript
module.exports = {
name: 'my-plugin',
version: '1.0.0',
onLoad: (context) => {
context.ui.registerButton({
id: 'my-button',
location: 'text-composer-toolbar',
label: 'My Action',
icon: '🎨',
onClick: () => {
context.log('Button clicked!');
}
});
},
onUnload: () => {
// Buttons are automatically cleaned up on plugin unload
}
};
```
## Positioning
### Before/After Positioning
Place your button before or after existing buttons:
```javascript
context.ui.registerButton({
id: 'my-button',
location: 'text-composer-toolbar',
label: 'My Action',
icon: '✨',
position: {
before: 'emoji-picker-button', // Place before emoji picker
// OR
after: 'sticker-button' // Place after sticker button
}
});
```
### Grouping Buttons
Group related buttons together:
```javascript
context.ui.registerButton({
id: 'action-1',
location: 'text-composer-toolbar',
label: 'Action 1',
icon: '1⃣',
position: {
group: 'my-plugin-tools',
order: 1 // Lower numbers appear first
}
});
context.ui.registerButton({
id: 'action-2',
location: 'text-composer-toolbar',
label: 'Action 2',
icon: '2⃣',
position: {
group: 'my-plugin-tools',
order: 2
}
});
```
### Combined Positioning and Grouping
Place a group before/after other elements:
```javascript
context.ui.registerButton({
id: 'tool-1',
location: 'text-composer-toolbar',
label: 'Tool 1',
icon: '🔧',
position: {
group: 'my-tools',
after: 'emoji-picker-button',
order: 1
}
});
context.ui.registerButton({
id: 'tool-2',
location: 'text-composer-toolbar',
label: 'Tool 2',
icon: '🔨',
position: {
group: 'my-tools',
order: 2
}
});
```
## Complete Example
```javascript
module.exports = {
name: 'custom-tools',
version: '1.0.0',
onLoad: (context) => {
// Nav list row — appears below "Create Chat" in the DMs panel
context.ui.registerButton({
id: 'dm-favorites',
location: 'direct-messages',
label: 'Favourites',
icon: '⭐',
onClick: () => context.log('Favourites clicked!')
});
// Nav list row — appears in the space channel list
context.ui.registerButton({
id: 'quick-access',
location: 'channel-list',
label: 'Quick Access',
icon: '⚡',
onClick: () => context.log('Quick access clicked!')
});
// Icon button — appears in the message composer toolbar
context.ui.registerButton({
id: 'format-bold',
location: 'text-composer-toolbar',
label: 'Bold',
icon: '𝐁',
position: {
group: 'custom-formatting',
order: 1
},
onClick: () => context.log('Bold clicked!')
});
// Icon button — appears in the room header
context.ui.registerButton({
id: 'special-search',
location: 'room-header',
label: 'Special Search',
icon: '🔍',
position: { after: 'search-button' },
onClick: () => context.log('Special search clicked!')
});
// Sidebar icon — appears above Explore and above Search
context.ui.registerButton({
id: 'plugin-panel',
location: 'sidebar-actions',
label: 'Plugin Panel',
icon: '🧩',
onClick: () => context.log('Plugin panel clicked!')
});
},
onUnload: () => {
// All registered buttons are automatically cleaned up
}
};
```
## Unregistering Buttons
Buttons are automatically unregistered when the plugin is unloaded. To manually unregister:
```javascript
context.ui.unregisterButton('my-button-id');
```
## TypeScript Types
```typescript
import type { UIButtonDefinition, UIButtonPosition, UILocation } from '@paarrot/plugin-manager';
const button: UIButtonDefinition = {
id: 'my-button',
location: 'text-composer-toolbar',
label: 'My Action',
icon: '🎨',
position: {
group: 'my-tools',
after: 'emoji-picker',
order: 1
},
onClick: () => console.log('Clicked!')
};
```