mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-25 08:53:50 +00:00
- Add @super-productivity/plugin-api package with TypeScript definitions - Define core plugin interfaces, types, and manifest structure - Add plugin hooks system for event-driven architecture - Create plugin API type definitions and constants - Add documentation and development guidelines
4.6 KiB
4.6 KiB
Super Productivity Plugin System - Improvement Plan
Document Version: 2.0
Date: 2025-06-27
Status: DRAFT - Pending Review
Executive Summary
This document outlines a comprehensive plan to improve the Super Productivity plugin system. The improvements focus on security, performance, developer experience, and architectural simplification while maintaining backward compatibility where possible.
Table of Contents
- Current State Analysis
- Identified Issues
- Proposed Solutions
- Implementation Roadmap
- Risk Assessment
- Success Metrics
Current State Analysis
Architecture Overview
The current plugin system consists of:
- Plugin Service (
plugin.service.ts): Main orchestrator - Plugin Bridge (
plugin-bridge.service.ts): API communication layer - Plugin Runner (
plugin-runner.ts): Code execution - Plugin API (
plugin-api.ts): API implementation - Plugin Package (
@super-productivity/plugin-api): Type definitions
Plugin Types
- JavaScript Plugins (
plugin.js): Execute in sandboxed environment - Iframe Plugins (
index.html): Render UI in sandboxed iframes - Hybrid Plugins: Combine both approaches
Current Capabilities
- Hook system for app events
- UI component registration (buttons, menus, shortcuts)
- Data access (tasks, projects, tags)
- Persistence layer
- Node.js execution (Electron only, with permission)
Identified Issues
1. Type System Complexity
Duplicate Types
// plugin-api package
export interface TaskData { ... }
// Internal model
export interface TaskCopy { ... }
// Confusing mappings
taskCopyToTaskData(task: TaskCopy): TaskData
taskDataToPartialTaskCopy(data: Partial<TaskData>): Partial<TaskCopy>
Problems:
- Maintenance overhead
- Confusion for developers
- Potential type mismatches
- Unnecessary complexity
2. Performance Issues
Loading Strategy
// Current: All plugins loaded on startup
await this._loadBuiltInPlugins();
await this._loadUploadedPlugins();
Problems:
- No lazy loading
- Memory overhead for disabled plugins
- Slow app startup
- No lifecycle management
3. API Inconsistencies
Message Passing Complexity
// Different patterns for iframe vs plugin.js
// Complex handler registration
// Multiple message types
// Confusing async patterns
Problems:
- Inconsistent behavior
5. Developer Experience
Current Pain Points
- Complex boilerplate
- Limited debugging tools
- Poor error messages
- No hot reload
- Missing development tools
Proposed Solutions
1. Simplified Type System
Unified Types
// Single source of truth in plugin-api package
export interface Task {
id: string;
title: string;
notes?: string;
// ... unified properties
}
// No more TaskData vs TaskCopy confusion
// Direct use in both plugin and app code
Runtime Validation
// Use Typia for automatic validation
import typia from 'typia';
const validateTask = typia.createValidate<Task>();
// Automatic validation on API boundaries
async updateTask(id: string, updates: unknown): Promise<void> {
const validated = validateTask(updates);
if (!validated.success) throw new Error(validated.errors);
// ... proceed with update
}
3. Performance Optimizations
Lazy Loading
class PluginLoader {
private loadedPlugins = new Map<string, Plugin>();
async loadPlugin(id: string): Promise<Plugin> {
if (this.loadedPlugins.has(id)) {
return this.loadedPlugins.get(id)!;
}
// Load only when needed
const plugin = await this.fetchAndCompile(id);
this.loadedPlugins.set(id, plugin);
return plugin;
}
unloadPlugin(id: string): void {
// Free memory when not needed
this.loadedPlugins.delete(id);
}
}
Plugin Lifecycle
interface PluginLifecycle {
onActivate(): Promise<void>;
onDeactivate(): Promise<void>;
}
4. Streamlined API
Unified RPC System
// Single API interface for all contexts
class PluginRPC {
async call<T>(method: string, ...args: any[]): Promise<T> {
return this.transport.send({ method, args });
}
}
// Type-safe client
const api = createPluginClient<PluginAPI>();
const tasks = await api.getTasks(); // Fully typed
Type Changes
// OLD
interface TaskCopy { ... }
interface TaskData { ... }
// NEW
interface Task { ... } // Single unified type