super-productivity/packages/plugin-api/README.md
Federico Simonetta 9a7a86c8ec
Feature plugins app state (#7803)
* chore(plugin-api): normalize index exports

* feat(plugin-api): expose app state types and getAppState

* feat(plugins): expose getAppState through PluginAPI

* feat(plugins): expose getAppState in plugin-bridge

* fix(plugins): restore tag selector import

* test(plugins): cover PluginAPI.getAppState

* fix(plugins): expose getAppState for iframe PluginAPI

* docs(plugin-api): add getAppState permission + example

* docs(plugins): concise getAppState entry

* fix(plugins): redact credentials from getAppState snapshot

`getAppState` previously returned `globalConfig` and `projects[]` verbatim,
exposing per-installation secrets to every loaded plugin. Strip the three
known credential surfaces before returning:

- `globalConfig.sync` — WebDAV/Nextcloud passwords, SuperSync access tokens,
  encryption keys
- `globalConfig.misc.unsplashApiKey` — user-supplied API key
- per-project `issueIntegrationCfgs` — Jira/CalDAV passwords, GitLab/Redmine
  tokens, OpenProject/Trello/Linear keys

Add a security-regression spec that seeds sentinel credential strings into
each surface and asserts none appear in `JSON.stringify(snapshot)`.

---------

Co-authored-by: 00sapo <00sapo@noreply.codeberg.org>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-05-27 17:02:29 +02:00

186 lines
4.4 KiB
Markdown

# @super-productivity/plugin-api
Official TypeScript definitions for developing [Super Productivity](https://github.com/super-productivity/super-productivity) plugins.
## Installation
```bash
npm install @super-productivity/plugin-api
```
## Usage
### TypeScript Plugin Development
```typescript
import type {
PluginAPI,
PluginManifest,
PluginHooks,
} from '@super-productivity/plugin-api';
// Your plugin code with full type support
PluginAPI.registerHook(PluginHooks.TASK_COMPLETE, (taskData) => {
console.log('Task completed!', taskData);
PluginAPI.showSnack({
msg: 'Task completed successfully!',
type: 'SUCCESS',
ico: 'celebration',
});
});
// Register a header button
PluginAPI.registerHeaderButton({
label: 'My Plugin',
icon: 'extension',
onClick: () => {
PluginAPI.showIndexHtmlAsView();
},
});
// Register a keyboard shortcut
PluginAPI.registerShortcut({
id: 'my_shortcut',
label: 'My Custom Shortcut',
onExec: () => {
PluginAPI.showSnack({
msg: 'Shortcut executed!',
type: 'SUCCESS',
});
},
});
```
### Plugin Manifest
```json
{
"name": "My Awesome Plugin",
"id": "my-awesome-plugin",
"manifestVersion": 1,
"version": "1.0.0",
"minSupVersion": "13.0.0",
"description": "An awesome plugin for Super Productivity",
"hooks": ["taskComplete", "taskUpdate"],
"permissions": ["showSnack", "getTasks", "addTask", "showIndexHtmlAsView"],
"iFrame": true,
"uiKit": true,
"icon": "icon.svg"
}
```
## Available Types
### Core Types
- `PluginAPI` - Main plugin API interface
- `PluginManifest` - Plugin configuration
- `PluginHooks` - Available hook types
- `PluginBaseCfg` - Runtime configuration
### Data Types
- `TaskData` - Task information
- `ProjectData` - Project information
- `TagData` - Tag information
### UI Types
- `DialogCfg` - Dialog configuration
- `SnackCfg` - Notification configuration
- `PluginMenuEntryCfg` - Menu entry configuration
- `PluginShortcutCfg` - Keyboard shortcut configuration
## Plugin Development Guide
### 1. Available Hooks
```typescript
enum PluginHooks {
TASK_COMPLETE = 'taskComplete',
TASK_UPDATE = 'taskUpdate',
TASK_DELETE = 'taskDelete',
FINISH_DAY = 'finishDay',
LANGUAGE_CHANGE = 'languageChange',
PERSISTED_DATA_CHANGED = 'persistedDataChanged',
ACTION = 'action',
}
```
**`PERSISTED_DATA_CHANGED`** fires on any persistent-data change to this
plugin after the host has finished its initial boot load — including
remote sync deliveries and bulk imports. Handler receives no payload;
re-call `loadSyncedData(key?)` for any key your plugin tracks to get
fresh data (scoped to the calling plugin). Contract: call
`loadSyncedData()` on plugin init for the initial state; then use this
hook for subsequent changes. There is no replay-on-register, no
per-key discrimination in the event, and no guaranteed ordering across
rapid changes. Handlers must be idempotent.
### 2. Required Permissions
Add these to your manifest.json based on what your plugin needs:
- `showSnack` - Show notifications
- `notify` - System notifications
- `showIndexHtmlAsView` - Display plugin UI
- `openDialog` - Show dialogs
- `getTasks` - Read tasks
- `getArchivedTasks` - Read archived tasks
- `getCurrentContextTasks` - Read current context tasks
- `addTask` - Create tasks
- `getAllProjects` - Read projects
- `addProject` - Create projects
- `getAllTags` - Read tags
- `addTag` - Create tags
- `persistDataSynced` - Persist plugin data
- `getAppState` - Read-only snapshot of application state
### 3. Plugin Structure
```
my-plugin/
├── manifest.json
├── plugin.js
├── index.html (optional, if iFrame: true)
└── icon.svg (optional)
```
### 4. Example Plugin
```javascript
// plugin.js
console.log('My Plugin initializing...', PluginAPI);
// Register hook for task completion
PluginAPI.registerHook(PluginAPI.Hooks.TASK_COMPLETE, function (taskData) {
console.log('Task completed!', taskData);
PluginAPI.showSnack({
msg: '🎉 Task completed!',
type: 'SUCCESS',
ico: 'celebration',
});
});
// Register header button
PluginAPI.registerHeaderButton({
label: 'My Plugin',
icon: 'dashboard',
onClick: function () {
PluginAPI.showIndexHtmlAsView();
},
});
// Read full app state
const state = await PluginAPI.getAppState();
```
## License
MIT - See the main Super Productivity repository for details.
## Contributing
Please contribute to the main [Super Productivity repository](https://github.com/super-productivity/super-productivity).