super-productivity/packages/plugin-dev/automations
Johannes Millan 4d22a64955
Feat/plugin UI kit (#6362)
* fix(e2e): stabilize undo task delete sync test

Two flakiness sources fixed:
- Click on task element could activate title inline editor, causing
  Backspace to edit text instead of triggering delete. Now clicks the
  drag handle which calls focusSelf() without entering edit mode.
- Replaced deleteTask helper with inline sequence to avoid wasting
  2s of the 5s undo snackbar window on dialog-detection timeout.

* refactor: address code review findings from 2026-02-03

- Extract getBreakCycle helper to replace error-prone `cycle - 1 || 1`
  pattern at 3 call sites
- Add clarifying comment on intentionally broad 'timed out' match
- Reduce Pomodoro E2E test from 9 to 5 sessions (sufficient coverage)
- Remove dead _isTransientNetworkError wrapper from DropboxApi
- Extract stubWindowConfirm helper in task reducer tests

* fix(sync): prevent Formly from clearing provider config on show (#6345)

resetOnHide: true caused Formly to reset field values when provider
fieldGroups transitioned from hidden to visible, discarding user input
if sync was enabled before selecting a provider.

* fix(tasks): fix huge space between emoji and text in tag/project menus

Use matMenuItemIcon attribute on emoji spans so they project into the
icon slot of mat-menu-item instead of the text slot. Update emoji icon
sizing to 24x24px to match mat-icon and add overflow: hidden.

Closes #5977

* fix(tasks): guard against undefined task entities in selectors and archive (#6359)

Prevent TypeError crashes (reading 'dueWithTime', 'dueDay', 'issueProviderId') caused
by orphaned IDs in NgRx state. Fix archive merge to deduplicate IDs, filter orphans,
and use correct entity precedence (young over old). Add defensive null guards to
selectors and archive/task service methods.

* fix(tasks): guard against undefined task in mainListTasksInProject$ (#6360)

* fix(tasks): detect and sanitize orphaned task IDs to prevent startup crashes (#6359, #6360)

Orphaned task IDs (entries in task.ids without matching entities) caused
TypeError on app startup. Fix addresses three layers: validation now
flags orphaned IDs instead of silently skipping them, loadAllData
sanitizes IDs on load as a safety net, and data repair no longer crashes
when encountering orphaned IDs it's trying to fix.

* fix(sync): prevent recurring task duplication across clients

Remove SuperSync special-case that bypassed initial sync wait, causing
repeatable task effects to fire before sync completed. Add post-sync
cleanup effect that detects and removes stale duplicate repeat instances
when multiple active instances exist for the same repeat config.

* fix(sync): restore WebDAV provider compatibility warning text

* feat(sync): mark WebDAV and LocalFile sync options as experimental

* feat(plugins): add UI Kit with inject-first CSS strategy for iframe plugins

Introduce a lightweight CSS reset (UI Kit) that auto-styles basic HTML
elements in plugin iframes to match the host app theme. Injected after
<head> so plugin styles always win by source order.

UI Kit provides: element resets (body, headings, buttons, inputs, tables,
links, code, lists, hr), .btn-primary/.btn-outline button variants, and
.card/.card-clickable components.

All bundled plugins updated to use UI Kit classes, removing redundant
custom CSS (-542 lines net). Pico CSS removed from automations plugin.
sync-md converted from hardcoded colors to host theme variables.

* feat(plugins): extract shared CSS utilities into UI Kit

Move .text-muted, .text-primary, .page-fade and @keyframes fadeIn from
plugin CSS into the UI Kit so all iframe plugins get them automatically.
Add box-shadow focus ring to input:focus for better accessibility.
Remove per-plugin focus overrides now covered by the UI Kit.
2026-02-04 18:18:22 +01:00
..
scripts refactor(automationPlugin): move and rename 2025-12-02 13:30:37 +01:00
src Feat/plugin UI kit (#6362) 2026-02-04 18:18:22 +01:00
.gitignore refactor(automationPlugin): move and rename 2025-12-02 13:30:37 +01:00
.prettierrc refactor(automationPlugin): move and rename 2025-12-02 13:30:37 +01:00
eslint.config.js chore(deps): upgrade ESLint to v9 with flat config 2026-01-10 16:08:11 +01:00
package-lock.json build(deps): bump the npm_and_yarn group across 4 directories with 1 update (#6141) 2026-01-25 10:48:01 +01:00
package.json build(deps): bump the npm_and_yarn group across 4 directories with 1 update (#6141) 2026-01-25 10:48:01 +01:00
README.md build: update links to match our new organization 2026-01-05 14:45:06 +01:00
tsconfig.json refactor(automationPlugin): move and rename 2025-12-02 13:30:37 +01:00
vite.config.ts refactor(automationPlugin): move and rename 2025-12-02 13:30:37 +01:00

Solid.js Boilerplate Plugin for Super Productivity

A modern, TypeScript-based boilerplate for creating Super Productivity plugins using Solid.js.

Features

  • 🚀 Solid.js - Fast, reactive UI framework
  • 📘 TypeScript - Full type safety with Super Productivity Plugin API
  • 🎨 Modern UI - Clean, responsive design with dark mode support
  • 🔧 Vite - Lightning-fast development and build tooling
  • 📦 Ready to Use - Complete setup with examples for all plugin features

Getting Started

Prerequisites

  • Node.js 16+
  • npm or yarn
  • Super Productivity 8.0.0+

Installation

  1. Clone this boilerplate:
cd packages/plugin-dev
cp -r boilerplate-solid-js my-plugin
cd my-plugin
  1. Install dependencies:
npm install
  1. Update plugin metadata in src/manifest.json:
    • Change id to a unique identifier
    • Update name, description, and author
    • Modify permissions and hooks as needed

Development

Run the development server:

npm run dev

This starts Vite in watch mode. Your plugin will rebuild automatically when you make changes.

Building

Build the plugin for production:

npm run build

This creates optimized files in the dist/ directory.

Packaging

Create a ZIP file for distribution:

npm run package

This will:

  1. Build the plugin
  2. Create a ZIP file containing all necessary files
  3. Place the ZIP in the root directory

Deployment (for Plugins with HTML UI)

If your plugin has an index.html file (for UI components, side panels, etc.), use the deploy command instead:

npm run deploy

This will:

  1. Build the plugin
  2. Inline all CSS and JavaScript assets into the HTML file
  3. Create a ZIP file for distribution

Note: The deploy command is necessary for any plugin with HTML UI because Super Productivity loads plugin HTML as data URLs, which cannot access external files. The inline-assets script ensures all assets are embedded directly in the HTML.

Project Structure

src/
├── assets/          # Static assets (icons, images)
│   └── icon.svg     # Plugin icon
├── app/             # Solid.js application
│   ├── App.tsx      # Main app component
│   └── App.css      # App styles
├── index.html       # Plugin UI entry point
├── index.ts         # UI initialization
├── plugin.ts        # Plugin logic and API integration
└── manifest.json    # Plugin metadata

scripts/            # Build and utility scripts
└── build-plugin.js  # Plugin packaging script

dist/               # Build output (gitignored)
├── assets/
├── index.html
├── index.js
├── plugin.js
└── manifest.json

Plugin API Usage

Basic Setup

The plugin API is exposed through the global plugin object in plugin.ts:

import { PluginInterface } from '@super-productivity/plugin-api';

declare const plugin: PluginInterface;

Common API Methods

UI Registration

// Register header button
plugin.registerHeaderButton({
  icon: 'rocket',
  tooltip: 'Open Plugin',
  action: () => plugin.showIndexHtmlAsView(),
});

// Register menu entry
plugin.registerMenuEntry({
  label: 'My Plugin',
  icon: 'rocket',
  action: () => plugin.showIndexHtmlAsView(),
});

// Register keyboard shortcut
plugin.registerShortcut({
  keys: 'ctrl+shift+m',
  label: 'Open My Plugin',
  action: () => plugin.showIndexHtmlAsView(),
});

Data Operations

// Get tasks
const tasks = await plugin.getTasks();
const archivedTasks = await plugin.getArchivedTasks();

// Create task
const newTask = await plugin.addTask({
  title: 'New Task',
  projectId: 'project-id',
});

// Update task
await plugin.updateTask('task-id', {
  title: 'Updated Title',
  isDone: true,
});

// Get projects and tags
const projects = await plugin.getAllProjects();
const tags = await plugin.getAllTags();

Event Hooks

// Task completion
plugin.on('taskComplete', (task) => {
  console.log('Task completed:', task.title);
});

// Task updates
plugin.on('taskUpdate', (task) => {
  console.log('Task updated:', task);
});

// Context changes
plugin.on('contextChange', (context) => {
  console.log('Context changed:', context);
});

Communication with UI

In plugin.ts:

plugin.onMessage('myCommand', async (data) => {
  // Handle message from UI
  return { result: 'success' };
});

In your Solid.js component:

const sendMessage = async (type: string, payload?: any) => {
  return new Promise((resolve) => {
    const messageId = Math.random().toString(36).substr(2, 9);

    const handler = (event: MessageEvent) => {
      if (event.data.messageId === messageId) {
        window.removeEventListener('message', handler);
        resolve(event.data.response);
      }
    };

    window.addEventListener('message', handler);
    window.parent.postMessage({ type, payload, messageId }, '*');
  });
};

// Usage
const result = await sendMessage('myCommand', { foo: 'bar' });

Customization

Styling

The boilerplate includes:

  • CSS custom properties for theming
  • Dark mode support
  • Responsive design
  • Minimal, clean styling

Modify src/app/App.css to customize the appearance.

Adding Features

  1. New UI Components: Add them in src/app/ as .tsx files
  2. New API Endpoints: Add handlers in src/plugin.ts using plugin.onMessage()
  3. New Hooks: Register them in manifest.json and handle in plugin.ts
  4. Permissions: Add required permissions to manifest.json

Best Practices

  1. Type Safety: Always use TypeScript types from @super-productivity/plugin-api
  2. Error Handling: Wrap async operations in try-catch blocks
  3. Performance: Use Solid.js signals and effects efficiently
  4. Security: Never expose sensitive data or operations
  5. User Experience: Provide loading states and error feedback

Deployment

  1. Build the plugin: npm run build
  2. Package it: npm run package
  3. Upload the ZIP file to Super Productivity:
    • Open Super Productivity
    • Go to Settings → Plugins
    • Click "Upload Plugin"
    • Select your ZIP file

Troubleshooting

Plugin not loading

  • Check browser console for errors
  • Verify manifest.json is valid JSON
  • Ensure minSupVersion matches your Super Productivity version

API calls failing

  • Check if you have required permissions in manifest.json
  • Verify Super Productivity is running the correct version
  • Look for error messages in the console

Build errors

  • Run npm run typecheck to check for TypeScript errors
  • Ensure all dependencies are installed
  • Clear node_modules and reinstall if needed

Resources

License

This boilerplate is provided as-is for creating Super Productivity plugins. Feel free to modify and distribute your plugins as you see fit.