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 fires when a tool becomes active, and onDeactivate fires when switching away from it (and when the Canvas unmounts). Use them for setup and cleanup such as cursor state or internal state.

Example

See the Tool Plugin Guide for a complete walkthrough.