# 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 # 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) ``` ### 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 (e.g., ["nodeExecution"]) | | `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": ["nodeExecution"], "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 **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. **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 `