- Introduced PLUGIN_BUTTON_API.md detailing button registration, UI locations, and usage examples. - Added PLUGIN_SYSTEM_IMPLEMENTATION.md summarizing implemented features, usage examples, and API details for the plugin system.
433 lines
9.8 KiB
Markdown
433 lines
9.8 KiB
Markdown
# 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!')
|
||
};
|
||
```
|