Tool System

How tools handle canvas interactions.

ToolDefinition

A tool defines how the canvas responds to user pointer events:

interface ToolDefinition {
  icon: () => ReactElement;
  cursor?: string;
  shortcut?: string;
  order?: number;
  onActivate?: (ctx: ToolContext) => void;
  onDeactivate?: (ctx: ToolContext) => void;
  onPointerDown?: (ctx: ToolContext, event: CanvasPointerEvent) => void;
  onPointerMove?: (ctx: ToolContext, event: CanvasPointerEvent) => void;
  onPointerUp?: (ctx: ToolContext, event: CanvasPointerEvent) => void;
}
FieldDescription
iconComponent rendered in the toolbar.
cursorCSS cursor value when this tool is active (e.g., "crosshair").
shortcutKeyboard shortcut to activate (e.g., "r" for rectangle).
orderPosition in the toolbar. Lower values appear first.
onActivateCalled when the tool becomes active.
onDeactivateCalled when switching away from this tool.
onPointerDown/Move/UpCanvas pointer event handlers.

ToolContext

Tool event handlers receive a ToolContext (a subset of PluginContext):

interface ToolContext {
  store: BoardStore;
  shapes: ShapeRegistry;
  commands: CommandRegistry;
  events: EventBus;
}

This gives tools access to the board state and the ability to execute undoable commands.

CanvasPointerEvent

interface CanvasPointerEvent {
  worldPoint: Point;   // Position in world (canvas) coordinates
  screenPoint: Point;  // Position in screen (pixel) coordinates
  shiftKey: boolean;
  ctrlKey: boolean;
  metaKey: boolean;
  altKey: boolean;
  button: number;
}

Both worldPoint and screenPoint are provided so tools can work in whichever coordinate space is appropriate.

ToolRegistry

Register tools during setup():

ctx.tools.register("rectangle-draw", {
  icon: RectIcon,
  cursor: "crosshair",
  shortcut: "r",
  order: 10,
  onPointerDown,
  onPointerMove,
  onPointerUp,
});

Query tools at runtime:

ctx.tools.get("rectangle-draw");
ctx.tools.getAll();            // ReadonlyMap<string, ToolDefinition>
ctx.tools.getOrdered();        // Sorted by order for toolbar rendering

Active Tool

The currently active tool is stored in BoardStore:

const toolId = ctx.store.getActiveToolId();
ctx.store.setActiveToolId("select");

onActivate and onDeactivate are defined in ToolDefinition for future lifecycle support but are not currently called by the runtime. Only pointer event handlers (onPointerDown, onPointerMove, onPointerUp) are invoked today.

Example

See the Tool Plugin Guide for a complete walkthrough.