super-productivity/packages/plugin-dev
Johannes Millan 01e30b9c7e
Feat/to me it looks like there are lots of 60dd04 (#7280)
* fix(issue): prevent crash from orphan issueProviderId (#7135)

The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.

Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.

* fix(focus-mode): sync tray countdown with in-app timer during breaks

Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.

Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.

Fixes #7278

* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68

Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.

Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3

* fix: restore i18n, UI, and docs work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.

Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior

* fix(electron): restore electron + e2e work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)

Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
  (re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
  (setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
  (electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
  (SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
  (this is literally the fix the bad commit's message claimed to add)

Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
  checks from d64014d086 (later than c558bcab5e) while restoring the
  goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
  clearStaleLevelDbLocks from theirs, fs from ours).

* fix(sync): restore sync-core work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.

This PR restores only the pieces that are genuinely still missing:

- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
  operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
  stale-idb-locks.ts piece was restored in the prior commit)

Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
  comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)

Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
2026-04-20 12:04:38 +02:00
..
ai-productivity-prompts chore(deps): bump the npm_and_yarn group across 8 directories with 2 updates (#7159) 2026-04-09 20:34:47 +02:00
api-test-plugin Feat/plugin UI kit (#6362) 2026-02-04 18:18:22 +01:00
automations fix(sync): handle data validation errors and improve client ID management 2026-04-16 17:41:39 +02:00
boilerplate-solid-js chore(deps): bump the npm_and_yarn group across 5 directories with 1 update (#7172) 2026-04-09 20:34:44 +02:00
brain-dump Implement task parsing with sub-tasks (#7184) 2026-04-19 16:44:19 +02:00
caldav-calendar-provider test(e2e): update Show/hide task panel button label (#7260) 2026-04-17 22:19:22 +02:00
clickup-issue-provider chore(deps): bump the npm_and_yarn group across 8 directories with 2 updates (#7159) 2026-04-09 20:34:47 +02:00
github-issue-provider Feat/to me it looks like there are lots of 60dd04 (#7280) 2026-04-20 12:04:38 +02:00
google-calendar-provider test(e2e): update Show/hide task panel button label (#7260) 2026-04-17 22:19:22 +02:00
procrastination-buster chore(deps): bump the npm_and_yarn group across 5 directories with 1 update (#7172) 2026-04-09 20:34:44 +02:00
scripts feat(calendar): add CalDAV Calendar plugin with time-blocking support 2026-03-28 23:10:08 +01:00
sync-md chore(deps): bump the npm_and_yarn group across 5 directories with 1 update (#7172) 2026-04-09 20:34:44 +02:00
voice-reminder refactor(voice-reminder): extract voice reminder to standalone plugin 2026-03-20 21:36:30 +01:00
yesterday-tasks-plugin Feat/plugin UI kit (#6362) 2026-02-04 18:18:22 +01:00
.gitignore feat(plugin-api): create foundational plugin API package 2025-06-27 18:13:19 +02:00
package-lock.json 18.2.4 2026-04-19 19:21:50 +02:00
package.json test(e2e): try to fix e2e tests 2025-12-10 21:26:48 +01:00
PLUGIN_I18N.md Add ro and ro-md languages (#6631) 2026-02-25 17:23:24 +01:00
QUICK_START.md feat(plugin-api): create foundational plugin API package 2025-06-27 18:13:19 +02:00
README.md feat(plugins): add i18n support to boilerplate-solid-js 2026-01-16 22:34:50 +01:00

Super Productivity Plugin Development

This directory contains tools and examples for developing plugins for Super Productivity.

Quick Commands

# Build all plugins
npm run build

# Install dependencies for all plugins
npm run install:all

# Clean build artifacts
npm run clean:dist

# List available plugins
npm run list

Getting Started

Prerequisites

  • Node.js 18 or higher
  • npm or yarn
  • TypeScript knowledge (recommended)

Quick Start

  1. Copy the example plugin:

    cp -r example-plugin my-plugin
    cd my-plugin
    
  2. Install dependencies:

    npm install
    
  3. Update plugin metadata:

    • Edit manifest.json with your plugin details
    • Update package.json with your plugin name and description
  4. Start development:

    npm run dev
    
  5. Build for production:

    npm run build
    

Project Structure

my-plugin/
├── package.json          # NPM package configuration
├── tsconfig.json         # TypeScript configuration
├── webpack.config.js     # Build configuration
├── manifest.json         # Plugin manifest (metadata)
├── src/
│   └── index.ts         # Main plugin code
├── assets/
│   ├── index.html       # Optional UI (for iframe plugins)
│   └── icon.svg         # Plugin icon
├── scripts/
│   └── package.js       # Script to create plugin.zip
└── dist/                # Build output
    ├── plugin.js        # Compiled plugin code
    ├── manifest.json    # Copied manifest
    └── plugin.zip       # Packaged plugin

Development Workflow

1. Local Development

For rapid development within the Super Productivity repo:

# Build and install to local Super Productivity
npm run install-local

# This copies your built plugin to:
# ../../../src/assets/my-plugin/

Then run Super Productivity in development mode to test your plugin.

2. Watch Mode

Keep the plugin building automatically as you make changes:

npm run dev

3. Type Checking

Ensure your code is type-safe:

npm run typecheck

4. Linting

Check code quality:

npm run lint

Plugin API

The plugin receives a global PluginAPI object with these capabilities:

Configuration

  • cfg - Current app configuration (theme, platform, version)

UI Integration

  • registerMenuEntry() - Add menu items
  • registerHeaderButton() - Add header buttons
  • registerSidePanelButton() - Add side panel buttons
  • registerShortcut() - Register keyboard shortcuts
  • showIndexHtmlAsView() - Display plugin UI

Data Access

  • getTasks() - Get all tasks
  • getArchivedTasks() - Get archived tasks
  • getCurrentContextTasks() - Get current project/tag tasks
  • updateTask() - Update a task
  • addTask() - Create new task
  • getAllProjects() - Get all projects
  • getAllTags() - Get all tags

User Interaction

  • showSnack() - Display snack bar notifications
  • notify() - Show system notifications
  • openDialog() - Open custom dialogs

Data Persistence

  • persistDataSynced() - Save plugin data
  • loadSyncedData() - Load saved data

Internationalization (i18n)

  • translate(key, params?) - Get translated text
  • formatDate(date, format) - Format dates with locale
  • getCurrentLanguage() - Get current language code

See PLUGIN_I18N.md for the complete i18n guide.

Hooks

Register handlers for lifecycle events:

  • taskComplete - Task marked as done
  • taskUpdate - Task modified
  • taskDelete - Task removed
  • currentTaskChange - Active task changed
  • languageChange - App language changed
  • finishDay - End of day

Example Usage

// Register a task complete handler
PluginAPI.registerHook('taskComplete', async (task) => {
  console.log('Task completed:', task);

  PluginAPI.showSnack({
    msg: `Great job completing: ${task.title}`,
    type: 'SUCCESS',
  });
});

// Add a keyboard shortcut
PluginAPI.registerShortcut({
  id: 'my-action',
  label: 'My Plugin Action',
  onExec: async () => {
    const tasks = await PluginAPI.getTasks();
    console.log(`You have ${tasks.length} tasks`);
  },
});

// Use translations (if plugin has i18n support)
const greeting = PluginAPI.translate('MESSAGES.GREETING');
const taskCount = PluginAPI.translate('TASK_COUNT', { count: tasks.length });
const dueDate = PluginAPI.formatDate(task.dueDate, 'short');

Building for Distribution

1. Create Plugin Package

npm run build
npm run package

This creates dist/plugin.zip ready for distribution.

2. File Size Limits

  • Plugin ZIP: 50MB maximum
  • Plugin code (plugin.js): 10MB maximum
  • Manifest: 100KB maximum
  • index.html: 100KB maximum

3. Required Files

Your plugin ZIP must contain:

  • manifest.json - Plugin metadata
  • plugin.js - Main plugin code

Optional files:

  • index.html - UI for iframe plugins
  • icon.svg - Plugin icon
  • i18n/*.json - Translation files for multi-language support

Publishing Your Plugin

  1. Create a GitHub repository for your plugin
  2. Use GitHub Actions to build releases:
name: Build Plugin
on:
  release:
    types: [created]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - run: npm ci
      - run: npm run build
      - run: npm run package
      - uses: softprops/action-gh-release@v1
        with:
          files: dist/plugin.zip
  1. Users can download the .zip file from your releases

NPM Package

You can also publish your plugin source to npm:

  1. Update package.json with your npm scope
  2. Build your plugin: npm run build
  3. Publish: npm publish

Users would need to build it themselves or you can include the built files.

Testing Your Plugin

1. In Development Mode

# Build your plugin
npm run build

# Copy to Super Productivity assets
npm run install-local

# Run Super Productivity in dev mode
cd ../../.. && npm start

2. In Production Build

  1. Build your plugin: npm run package
  2. Open Super Productivity
  3. Go to Settings → Plugins
  4. Click "Upload Plugin"
  5. Select your plugin.zip file

3. Debugging

  • Open browser DevTools to see console logs
  • Check the Console for plugin errors
  • Use console.log() in your plugin code
  • The plugin runs in the main window context

TypeScript Development

Benefits

  1. Type Safety: Full IntelliSense and compile-time checking
  2. API Discovery: Auto-complete for all PluginAPI methods
  3. Refactoring: Safe code refactoring with TypeScript
  4. Documentation: Inline documentation in your IDE

Example with Types

import type { TaskData, ProjectData } from '@super-productivity/plugin-api';

// Type-safe task handling
async function processTask(task: TaskData): Promise<void> {
  if (task.projectId) {
    const projects = await PluginAPI.getAllProjects();
    const project = projects.find((p) => p.id === task.projectId);

    if (project) {
      console.log(`Task "${task.title}" belongs to project "${project.title}"`);
    }
  }
}

// Type-safe hook registration
PluginAPI.registerHook('taskUpdate', (data: unknown) => {
  const task = data as TaskData;
  processTask(task);
});

Best Practices

  1. Error Handling: Always wrap async operations in try-catch
  2. Performance: Don't block the main thread with heavy computations
  3. State Management: Use persistDataSynced() for plugin state
  4. User Experience: Provide clear feedback with snack messages
  5. Permissions: Only request permissions you actually need
  6. Version Compatibility: Set appropriate minSupVersion
  7. Internationalization: Add i18n support to reach more users (see PLUGIN_I18N.md)

Troubleshooting

Plugin not loading

  • Check browser console for errors
  • Verify manifest.json is valid JSON
  • Ensure all required fields are present
  • Check file size limits

TypeScript errors

  • Run npm run typecheck to see all errors
  • Ensure @super-productivity/plugin-api is installed
  • Check tsconfig.json settings

Build issues

  • Delete dist/ and rebuild
  • Check webpack.config.js for errors
  • Ensure all dependencies are installed

Examples

Available Examples

  1. minimal-plugin - The simplest possible plugin (10 lines)
  2. simple-typescript-plugin - TypeScript with minimal tooling
  3. example-plugin - Full featured example with webpack
  4. boilerplate-solid-js - Modern Solid.js boilerplate with i18n support
  5. procrastination-buster - SolidJS plugin with modern UI

Example Features

boilerplate-solid-js demonstrates:

  • SolidJS for reactive UI
  • Vite for fast builds
  • Internationalization (i18n) support with example translations
  • Modern component architecture
  • Plugin-to-iframe communication
  • Best practices for plugin development

example-plugin demonstrates:

  • TypeScript setup with webpack
  • All API methods
  • iframe UI integration
  • State persistence
  • Hook handling
  • Build configuration

procrastination-buster demonstrates:

  • SolidJS for reactive UI
  • Vite for fast builds
  • Modern component architecture
  • Plugin-to-iframe communication
  • Real-world use case

Support