Plugin Architecture
How UsketchPlugin, PluginContext, and the lifecycle work together.
Overview
In uSketch v2, every feature is a plugin. The core framework provides registries and services; plugins register shapes, tools, layers, commands, and event handlers during setup.
The Plugin Interface
type PluginTeardown = () => void | Promise<void>;
interface UsketchPlugin {
readonly id: string;
readonly name: string;
setup(ctx: PluginContext): PluginTeardown | void | Promise<PluginTeardown | void>;
}
| Field | Description |
|---|---|
id | Unique identifier, e.g. "usketch-plugin-shape-rect" |
name | Human-readable display name |
setup(ctx) | Called once during app initialization. Register everything here. Return a teardown function if there is anything to clean up; return void (or omit a return) if not. |
The former plugin.teardown property has been removed. Cleanup is expressed as the function returned from setup. Stashing the cleanup on this is unsafe under React StrictMode (a second setup call silently overwrites the first instance’s teardown closure), so always return it from setup — each createApp call owns its own closure.
PluginContext
The ctx argument passed to setup() provides access to all registries:
interface PluginContext {
store: BoardStore; // State management
layers: LayerManager; // Rendering layer registration
tools: ToolRegistry; // Tool registration
shapes: ShapeRegistry; // Shape type registration
commands: CommandRegistry; // Undo/redo command execution
shortcuts: ShortcutRegistry; // Keyboard shortcut registration
events: EventBus; // Inter-plugin communication
transient: TransientRegistry; // Ephemeral objects (cursors, effects)
lod: LodController; // Level-of-detail (render mode) control
ui: UiRegistry; // UI extension registration (e.g. selection foreground)
externalContent: ExternalContentRegistry; // Handling dropped/pasted external content
actions: ActionRegistry; // Declarative plugin operations; powers the Control HUD
services: ServiceRegistry; // String-keyed registry for plugin-provided services (IoC)
}
Lifecycle
createApp({ store, plugins })
│
├─ 1. Register plugins in PluginRegistry
│
├─ 2. Call setup(ctx) for each plugin (in order)
│ ├─ Register layers
│ ├─ Register tools
│ ├─ Register shapes
│ ├─ Register commands
│ ├─ Register shortcuts
│ └─ Subscribe to events
│
├─ 3. Mount the React tree
│
└─ [On destroy]
└─ Call the teardown returned by setup in LIFO order (reverse of registration)
Plugins are initialized sequentially in the order they appear in the plugins array. This means a plugin can depend on registrations made by an earlier plugin.
Self-Contained Plugins
A shape plugin typically bundles both the shape definition and the drawing tool in a single package. For example, the rectangle plugin registers:
- A shape type (
"rectangle") inShapeRegistry - A drawing tool (
"rectangle-draw") inToolRegistry
This keeps the concept of “rectangle” self-contained in one package.
Naming Conventions
| Category | Package pattern | Example |
|---|---|---|
| Tool plugin | usketch-plugin-tool-{name} | usketch-plugin-tool-select |
| Shape plugin | usketch-plugin-shape-{name} | usketch-plugin-shape-rect |
| Background plugin | usketch-plugin-bg-{name} | usketch-plugin-bg-grid |
| Feature plugin | usketch-plugin-{name} | usketch-plugin-snap |