mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
docs: update plugin development guide
This commit is contained in:
parent
63f9527080
commit
0a55fa2823
1 changed files with 57 additions and 267 deletions
|
|
@ -1,17 +1,20 @@
|
|||
# WARNING WIP (HELP WANTED)
|
||||
|
||||
These docs are a first draft and at many places plain wrong. I think the best way to figure out how to write a plugin is to check aut the example plugins:
|
||||
|
||||
https://github.com/johannesjo/super-productivity/tree/master/packages/plugin-dev/
|
||||
https://github.com/johannesjo/super-productivity/tree/master/packages/plugin-dev/yesterday-tasks-plugin
|
||||
|
||||
It is not super complicated, I think :)
|
||||
|
||||
---
|
||||
|
||||
# Super Productivity Plugin Development Guide
|
||||
|
||||
This guide covers everything you need to know about creating plugins for Super Productivity.
|
||||
This is a comprehensive documentation of the Super Productivity Plugin System. This guide covers everything you need to know about creating plugins for Super Productivity.
|
||||
|
||||
These docs might not always be perfectly up to date. You find the latest typescript interfaces here:
|
||||
[types.ts](../packages/plugin-api/src/types.ts)
|
||||
|
||||
Personally I think the best way to figure out how to write a plugin is to check out the example plugins:
|
||||
|
||||
- [yesterday-tasks-plugin](../packages/plugin-dev/yesterday-tasks-plugin)
|
||||
- [procrastination-buster](../packages/plugin-dev/procrastination-buster)
|
||||
- [api-test-plugin](../packages/plugin-dev/api-test-plugin)
|
||||
|
||||
If you want to build a sophisticated UI there is a boilerplate available for solidjs:
|
||||
[boilerplate-solid-js](../packages/plugin-dev/boilerplate-solid-js)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
|
@ -25,22 +28,17 @@ This guide covers everything you need to know about creating plugins for Super P
|
|||
|
||||
## Quick Start
|
||||
|
||||
### 1. Use the Plugin Boilerplate or copy example plugin
|
||||
|
||||
https://github.com/johannesjo/super-productivity/tree/master/packages/plugin-dev/boilerplate-solid-js
|
||||
https://github.com/johannesjo/super-productivity/tree/master/packages/plugin-dev/yesterday-tasks-plugin
|
||||
|
||||
### 2. Basic Plugin Structure
|
||||
### 1. Basic Plugin Structure
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── manifest.json # Plugin metadata (required)
|
||||
├── plugin.js # Main plugin code (optional)
|
||||
├── index.html # UI interface (optional)
|
||||
├── plugin.js # Main plugin code that is launched when activated and when Super Productivity starts
|
||||
├── index.html # UI interface (optional) => requires iFrame:true in manifest
|
||||
└── icon.svg # Plugin icon (optional)
|
||||
```
|
||||
|
||||
### 3. Minimal Example
|
||||
### 2. Minimal Example
|
||||
|
||||
**manifest.json:**
|
||||
|
||||
|
|
@ -51,7 +49,7 @@ my-plugin/
|
|||
"version": "1.0.0",
|
||||
"description": "My first Super Productivity plugin",
|
||||
"manifestVersion": 1,
|
||||
"minSupVersion": "8.0.0"
|
||||
"minSupVersion": "14.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -85,21 +83,21 @@ The `manifest.json` file is required for all plugins and defines the plugin's me
|
|||
|
||||
### Manifest Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ----------------- | -------- | -------- | ---------------------------------------------------------- |
|
||||
| `id` | string | ✓ | Unique identifier for your plugin (use kebab-case) |
|
||||
| `name` | string | ✓ | Display name shown to users |
|
||||
| `version` | string | ✓ | Semantic version (e.g., "1.0.0") |
|
||||
| `description` | string | ✓ | Brief description of what your plugin does |
|
||||
| `manifestVersion` | number | ✓ | Currently must be `1` |
|
||||
| `minSupVersion` | string | ✓ | Minimum Super Productivity version required |
|
||||
| `author` | string | | Plugin author name |
|
||||
| `homepage` | string | | Plugin website or repository URL |
|
||||
| `icon` | string | | Path to icon file (SVG recommended) |
|
||||
| `iFrame` | boolean | | Whether plugin uses iframe UI (default: false) |
|
||||
| `sidePanel` | boolean | | Show plugin in side panel (default: false) |
|
||||
| `permissions` | string[] | | The permissions the plugin needs (e.g., ["nodeExecution"]) |
|
||||
| `hooks` | string[] | | App events to listen to |
|
||||
| Field | Type | Required | Description |
|
||||
| ----------------- | -------- | -------- | ------------------------------------------------------------------ |
|
||||
| `id` | string | ✓ | Unique identifier for your plugin (use kebab-case) |
|
||||
| `name` | string | ✓ | Display name shown to users |
|
||||
| `version` | string | ✓ | Semantic version (e.g., "1.0.0") |
|
||||
| `description` | string | ✓ | Brief description of what your plugin does |
|
||||
| `manifestVersion` | number | ✓ | Currently must be `1` |
|
||||
| `minSupVersion` | string | ✓ | Minimum Super Productivity version required |
|
||||
| `author` | string | | Plugin author name |
|
||||
| `homepage` | string | | Plugin website or repository URL |
|
||||
| `icon` | string | | Path to icon file (SVG recommended) |
|
||||
| `iFrame` | boolean | | Whether plugin uses iframe UI (default: false) |
|
||||
| `sidePanel` | boolean | | Show plugin in side panel (default: false), requires `iFrame:true` |
|
||||
| `permissions` | string[] | | The permissions the plugin needs (e.g., ["nodeExecution"]) |
|
||||
| `hooks` | string[] | | App events to listen to |
|
||||
|
||||
### Complete Manifest Example
|
||||
|
||||
|
|
@ -110,7 +108,7 @@ The `manifest.json` file is required for all plugins and defines the plugin's me
|
|||
"version": "2.1.0",
|
||||
"description": "An advanced plugin with UI and hooks",
|
||||
"manifestVersion": 1,
|
||||
"minSupVersion": "8.0.0",
|
||||
"minSupVersion": "14.0.2",
|
||||
"author": "John Doe",
|
||||
"homepage": "https://github.com/johndoe/my-plugin",
|
||||
"icon": "icon.svg",
|
||||
|
|
@ -129,7 +127,8 @@ Pure JavaScript plugins that run in a sandboxed environment with full API access
|
|||
|
||||
**Use when:**
|
||||
|
||||
- You need to register UI components (buttons, menu items)
|
||||
- For setup background stuff that is to be executed even when the plugin ui (iFrame) is not shown
|
||||
- For registering and handling keyboard shortcuts
|
||||
- You want to listen to app hooks/events
|
||||
- You need programmatic interaction with tasks/projects
|
||||
|
||||
|
|
@ -159,7 +158,6 @@ Plugins that render custom UI in a sandboxed iframe.
|
|||
|
||||
- You need custom UI/visualizations
|
||||
- You want to display charts, forms, or complex interfaces
|
||||
- You prefer working with HTML/CSS
|
||||
|
||||
**Important:** When using iframes, you must inline all CSS and JavaScript directly in the HTML file. External stylesheets and scripts are blocked for security reasons.
|
||||
|
||||
|
|
@ -244,39 +242,11 @@ Plugins that render custom UI in a sandboxed iframe.
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Error handling
|
||||
window.addEventListener('error', (event) => {
|
||||
console.error('Plugin error:', event.error);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 3. Hybrid Plugins
|
||||
|
||||
Combine both `plugin.js` and `index.html` for maximum flexibility.
|
||||
|
||||
**plugin.js:**
|
||||
|
||||
```javascript
|
||||
// Register UI elements and hooks
|
||||
PluginAPI.registerSidePanelButton({
|
||||
label: 'Open My Plugin',
|
||||
icon: 'dashboard',
|
||||
onClick: () => {
|
||||
PluginAPI.showIndexHtmlAsView();
|
||||
},
|
||||
});
|
||||
|
||||
// Listen for events and update iframe
|
||||
PluginAPI.registerHook(PluginAPI.Hooks.TASK_UPDATE, () => {
|
||||
// Notify iframe about task update
|
||||
// (iframe will receive this via PluginAPI events)
|
||||
});
|
||||
```
|
||||
|
||||
## Available API Methods
|
||||
|
||||
### Data Operations
|
||||
|
|
@ -416,6 +386,8 @@ PluginAPI.registerHook(PluginAPI.Hooks.ACTION, (action) => {
|
|||
|
||||
### Data Persistence
|
||||
|
||||
You can persist data that will also be synced vai the `persistDataSynced` and `loadSyncedData` APIs. For local storage I recommend using `localStorage`.
|
||||
|
||||
```javascript
|
||||
// Save plugin data
|
||||
await PluginAPI.persistDataSynced('myKey', { count: 42 });
|
||||
|
|
@ -430,50 +402,25 @@ console.log(data); // { count: 42 }
|
|||
### 1. Performance
|
||||
|
||||
- **Lazy load resources**: Don't load everything on plugin initialization
|
||||
- **Debounce expensive operations**: Use throttling for frequent events
|
||||
- **Clean up resources**: Remove event listeners when appropriate
|
||||
- **Be responsive with using resources**: Avoid heavy operations and don't save excessive amounts of data.
|
||||
- **Keep it lightweight**: Super Productivity is not the only app on the users system and your plugin is not the only plugin.
|
||||
|
||||
### 2. Error Handling
|
||||
|
||||
Always wrap async operations in try-catch blocks:
|
||||
|
||||
```javascript
|
||||
async function loadData() {
|
||||
try {
|
||||
const tasks = await PluginAPI.getTasks();
|
||||
// Process tasks
|
||||
} catch (error) {
|
||||
console.error('Failed to load tasks:', error);
|
||||
PluginAPI.showSnack({
|
||||
msg: 'Error loading tasks',
|
||||
type: 'ERROR',
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. User Experience
|
||||
### 2. User Experience
|
||||
|
||||
- **Provide feedback**: Show loading states and confirmations
|
||||
- **Be non-intrusive**: Don't spam notifications
|
||||
- **Follow the app's design**: Use Material icons and consistent styling
|
||||
- **Respect user preferences**: Check dark mode, language settings
|
||||
- **Follow the app's design**: Use the injected theme variables and try to keep styles minimal.
|
||||
- **Respect user preferences**: Check dark mode, and language settings (if possible or stick to english if not)
|
||||
|
||||
### 4. Security
|
||||
### 3. Security
|
||||
|
||||
- **Validate inputs**: Never trust user input
|
||||
- **Avoid eval()**: Use safe JSON parsing instead
|
||||
- **Sanitize HTML**: If displaying user content
|
||||
- **Request minimal permissions**: Only what you need
|
||||
|
||||
### 5. Iframe Development
|
||||
### 4. Don't spam the logs
|
||||
|
||||
When developing iframe plugins:
|
||||
`console.logs` should be kept to a minimum.
|
||||
|
||||
1. **Inline everything**: CSS and JavaScript must be in the HTML file
|
||||
2. **Wait for API ready**: Listen for the `PluginApiReady` event
|
||||
3. **Handle errors gracefully**: Iframes can fail to load
|
||||
4. **Keep it lightweight**: Iframes add overhead
|
||||
|
||||
```html
|
||||
<!-- Good: Everything inlined -->
|
||||
|
|
@ -499,8 +446,7 @@ When developing iframe plugins:
|
|||
|
||||
- JavaScript plugins run in isolated VM contexts
|
||||
- Iframe plugins run in sandboxed iframes with restricted permissions
|
||||
- No access to Node.js APIs unless explicitly granted
|
||||
- No access to file system or network unless through API
|
||||
- No access to file system unless through API
|
||||
|
||||
### API Restrictions
|
||||
|
||||
|
|
@ -523,10 +469,9 @@ In iframe context, these methods are NOT available:
|
|||
|
||||
### 1. Local Development
|
||||
|
||||
1. Enable Developer Mode in Super Productivity settings
|
||||
2. Use "Load Plugin from Folder" to test your plugin
|
||||
3. Open DevTools (F12) to see console logs
|
||||
4. Use the API Test Plugin as reference
|
||||
1. Use "Load Plugin from Folder" to test your plugin
|
||||
2. Open DevTools (F12 or Ctrl+Shift+i) to see console logs
|
||||
3. Use the API Test Plugin as reference
|
||||
|
||||
### 2. Debugging Tips
|
||||
|
||||
|
|
@ -564,7 +509,6 @@ async function testAPI() {
|
|||
|
||||
**API methods failing:**
|
||||
|
||||
- Ensure PluginAPI is ready before use
|
||||
- Check if method is available in current context
|
||||
- Verify permissions in manifest
|
||||
|
||||
|
|
@ -574,172 +518,18 @@ async function testAPI() {
|
|||
- Verify no external dependencies
|
||||
- Look for CSP violations in console
|
||||
|
||||
## Examples
|
||||
|
||||
### Task Reporter Plugin
|
||||
|
||||
```javascript
|
||||
// plugin.js
|
||||
let reportInterval;
|
||||
|
||||
PluginAPI.registerHeaderButton({
|
||||
label: 'Start Report',
|
||||
icon: 'assessment',
|
||||
onClick: () => {
|
||||
if (reportInterval) {
|
||||
clearInterval(reportInterval);
|
||||
reportInterval = null;
|
||||
PluginAPI.showSnack({ msg: 'Reporting stopped', type: 'INFO' });
|
||||
} else {
|
||||
reportInterval = setInterval(generateReport, 3600000); // Every hour
|
||||
PluginAPI.showSnack({ msg: 'Reporting started', type: 'SUCCESS' });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
async function generateReport() {
|
||||
const tasks = await PluginAPI.getCurrentContextTasks();
|
||||
const completed = tasks.filter((t) => t.isDone).length;
|
||||
const total = tasks.length;
|
||||
|
||||
PluginAPI.notify({
|
||||
title: 'Hourly Report',
|
||||
body: `Completed ${completed} of ${total} tasks`,
|
||||
ico: 'pie_chart',
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Dashboard Plugin
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #666;
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Task Dashboard</h1>
|
||||
<div
|
||||
class="dashboard"
|
||||
id="dashboard"
|
||||
>
|
||||
<div class="stat-card">
|
||||
<div
|
||||
class="stat-value"
|
||||
id="totalTasks"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-label">Total Tasks</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div
|
||||
class="stat-value"
|
||||
id="completedTasks"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-label">Completed</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div
|
||||
class="stat-value"
|
||||
id="totalProjects"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-label">Projects</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div
|
||||
class="stat-value"
|
||||
id="totalTags"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-label">Tags</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.addEventListener('PluginApiReady', async () => {
|
||||
async function updateDashboard() {
|
||||
try {
|
||||
const [tasks, projects, tags] = await Promise.all([
|
||||
PluginAPI.getTasks(),
|
||||
PluginAPI.getAllProjects(),
|
||||
PluginAPI.getAllTags(),
|
||||
]);
|
||||
|
||||
document.getElementById('totalTasks').textContent = tasks.length;
|
||||
document.getElementById('completedTasks').textContent = tasks.filter(
|
||||
(t) => t.isDone,
|
||||
).length;
|
||||
document.getElementById('totalProjects').textContent = projects.length;
|
||||
document.getElementById('totalTags').textContent = tags.length;
|
||||
} catch (error) {
|
||||
console.error('Dashboard update failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Initial load
|
||||
updateDashboard();
|
||||
|
||||
// Refresh every 30 seconds
|
||||
setInterval(updateDashboard, 30000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- **Plugin API Types**: [@super-productivity/plugin-api](https://www.npmjs.com/package/@super-productivity/plugin-api)
|
||||
- **Plugin Boilerplate**: [GitHub Repository](https://github.com/johannesjo/super-productivity-plugin-boilerplate)
|
||||
- **Example Plugins**: Check the `src/app/features/plugin/plugins` directory
|
||||
- **Community Plugins**: [Awesome Super Productivity](https://github.com/johannesjo/super-productivity#plugins)
|
||||
- **Plugin Boilerplate**: [boilerplate-solid-js](../packages/plugin-dev/boilerplate-solid-js)
|
||||
- **Example Plugins**: [plugin-dev](../packages/plugin-dev)
|
||||
- **Community Plugins**: Coming Soon!
|
||||
|
||||
## Contributing
|
||||
|
||||
If you create a useful plugin, consider:
|
||||
|
||||
1. Publishing it to npm
|
||||
2. Submitting a PR to add it to the community plugins list
|
||||
3. Sharing it in the Super Productivity discussions
|
||||
1. Posting on reddit or GitHub discussions about it
|
||||
2. Submitting a PR to add it to the community plugins list (coming soon)
|
||||
|
||||
Happy plugin development! 🚀
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue