# Super Productivity Plugin Development Guide 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 - [Quick Start](#quick-start) - [Plugin Manifest](#plugin-manifest) - [Plugin Types](#plugin-types) - [Available API Methods](#available-api-methods) - [Best Practices](#best-practices) - [Security Considerations](#security-considerations) - [Testing Your Plugin](#testing-your-plugin) ## Quick Start ### 1. Basic Plugin Structure ``` my-plugin/ ├── manifest.json # Plugin metadata (required) ├── plugin.js # Host-side plugin code (optional for iframe-only plugins) ├── index.html # UI interface (required when omitting plugin.js; requires iFrame:true in manifest) └── icon.svg # Plugin icon (optional) ``` `plugin.js` is required for plugins that need host-side setup at plugin load time, shortcuts, header buttons, background behavior, or host-side API handlers. A UI-only iframe plugin can ship only `manifest.json` and `index.html` when the manifest sets `iFrame: true`. ### 2. Minimal Example **manifest.json:** ```json { "id": "hello-world", "name": "Hello World Plugin", "version": "1.0.0", "description": "My first Super Productivity plugin", "manifestVersion": 1, "minSupVersion": "14.0.0" } ``` **plugin.js:** ```javascript console.log('Hello World plugin loaded!'); // Show a notification PluginAPI.showSnack({ msg: 'Hello from my plugin!', type: 'SUCCESS', }); // Demo a simple counter await PluginAPI.setCounter('hello-count', 0); PluginAPI.registerHeaderButton({ label: 'Hello (Count: 0)', icon: 'waving_hand', onClick: async () => { const newCount = await PluginAPI.incrementCounter('hello-count'); PluginAPI.showSnack({ msg: `Button clicked! Count: ${newCount}`, type: 'INFO', }); }, }); ``` ## Plugin Manifest The `manifest.json` file is required for all plugins and defines the plugin's metadata and configuration. ### 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), requires `iFrame:true` | | `permissions` | string[] | | The permissions the plugin needs | | `hooks` | string[] | | App events to listen to | | `uiKit` | boolean | | Enable UI Kit CSS reset for iframe plugins (default: true). Set to `false` to disable. | ### Complete Manifest Example ```json { "id": "my-advanced-plugin", "name": "My Advanced Plugin", "version": "2.1.0", "description": "An advanced plugin with UI and hooks", "manifestVersion": 1, "minSupVersion": "14.0.2", "author": "John Doe", "homepage": "https://github.com/johndoe/my-plugin", "icon": "icon.svg", "iFrame": true, "sidePanel": false, "permissions": ["getTasks", "updateTask"], "hooks": ["taskComplete", "taskUpdate", "currentTaskChange"] } ``` ## Plugin Types ### 1. JavaScript Plugins (`plugin.js`) Pure JavaScript plugins that run in a sandboxed environment with full API access. **Use when:** - 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 **Example:** ```javascript // Register multiple UI elements PluginAPI.registerHeaderButton({ label: 'My Button', icon: 'star', onClick: async () => { const tasks = await PluginAPI.getTasks(); console.log(`You have ${tasks.length} tasks`); }, }); PluginAPI.registerHook(PluginAPI.Hooks.TASK_COMPLETE, (taskId) => { console.log(`Task ${taskId} completed!`); }); ``` ### 2. HTML/Iframe Plugins (`index.html`) Plugins that render custom UI in a sandboxed iframe. **Use when:** - You need custom UI/visualizations - You want to display charts, forms, or complex interfaces Iframe-only plugins do not need a `plugin.js` file if all plugin behavior lives inside `index.html`. Super Productivity automatically adds the default menu or side-panel entry from the manifest when the plugin is loaded. **Important:** Iframe plugins are served from a sandboxed blob document and talk to the host only through the filtered Plugin API message bridge. Inline CSS, JavaScript, and small assets directly in `index.html`; arbitrary extra files from the ZIP are not served to the iframe. External URLs can work when the app/runtime CSP allows them, but they are not part of the portable plugin contract. **Example index.html:** ```html My Plugin UI

My Plugin

``` ### Theme Variables & UI Kit Iframe plugins automatically receive: 1. **CSS variables** — All theme variables (colors, spacing, shadows, transitions) are injected as CSS custom properties on `:root`. Use `var(--c-primary)`, `var(--bg)`, `var(--text-color)`, etc. 2. **UI Kit CSS reset** — By default, basic HTML elements (`button`, `input`, `select`, `textarea`, `table`, `a`, `h1`–`h6`, `p`, `code`, `pre`, `hr`, etc.) are styled to match the app's look. This is injected before your plugin's own styles, so your CSS always wins. To disable the UI Kit, add `"uiKit": false` to your manifest. **Button variants:** - Default `