BoardKitCore
Tools
Abstract Tool class, tool state machine, and ToolRegistry
Tools process pointer input events and produce element mutations and preview elements. Each tool follows a state machine pattern: idle → active → finishing → idle.
Tool Abstract Class
abstract class Tool {
abstract readonly id: string;
abstract readonly name: string;
abstract state: ToolState;
abstract onPointerDown(event: InputEvent, scene: SceneState): ToolResult;
abstract onPointerMove(event: InputEvent, scene: SceneState): ToolResult;
abstract onPointerUp(event: InputEvent, scene: SceneState): ToolResult;
abstract onCancel(): ToolResult;
}ToolState
type ToolState = 'idle' | 'active' | 'finishing';| State | Description |
|---|---|
idle | Tool is not processing input |
active | Tool is actively processing (e.g., user is drawing) |
finishing | Tool is completing its operation (e.g., finalizing a shape) |
InputEvent
The normalized pointer event that tools receive:
interface InputEvent {
type: 'pointerDown' | 'pointerMove' | 'pointerUp' | 'pointerCancel';
position: Point;
pressure?: number;
tilt?: { x: number; y: number };
button: number;
modifiers: {
shift: boolean;
ctrl: boolean;
alt: boolean;
meta: boolean;
};
timestamp: number;
}ToolResult
The output of a tool's event handler:
interface ToolResult {
mutations?: ElementMutation[];
preview?: Element[];
cursor?: string;
state: ToolState;
}| Field | Description |
|---|---|
mutations | Element mutations to apply to the scene (create, update, delete) |
preview | Temporary elements to render on the interactive layer (e.g., in-progress stroke) |
cursor | CSS cursor to display |
state | The tool's new state after processing |
ToolRegistry
Manages registered tools and provides lookup.
class ToolRegistry {
register(tool: Tool): void;
get(id: string): Tool | undefined;
getAll(): Tool[];
static createDefault(): ToolRegistry;
}createDefault() registers all 8 built-in tools:
import { ToolRegistry, TOOL_IDS } from '@hfu.digital/boardkit-core';
const registry = ToolRegistry.createDefault();
const pen = registry.get(TOOL_IDS.PEN); // PenTool
const shape = registry.get(TOOL_IDS.SHAPE); // ShapeTool
const text = registry.get(TOOL_IDS.TEXT); // TextToolBuilt-in Tools
| Tool | ID | Creates Element | Description |
|---|---|---|---|
| PenTool | pen | StrokeElement | Freehand drawing with pressure support |
| ShapeTool | shape | ShapeElement | Rectangle, ellipse, line, arrow, triangle |
| TextTool | text | TextElement | Click to create a text box |
| StickyNoteTool | stickyNote | StickyNoteElement | Click to create a colored sticky note |
| ConnectorTool | connector | ConnectorElement | Click two elements to connect them |
| SelectTool | select | -- | Click/drag to select and move elements |
| EraserTool | eraser | -- | Click/drag to delete elements |
| LaserTool | laser | -- | Preview-only laser pointer (no mutations) |
TOOL_IDS Constants
const TOOL_IDS = {
PEN: 'pen',
SHAPE: 'shape',
SELECT: 'select',
ERASER: 'eraser',
TEXT: 'text',
HAND: 'hand',
STICKY_NOTE: 'stickyNote',
CONNECTOR: 'connector',
LASER: 'laser',
} as const;Note:
TOOL_IDS.HANDis defined as a constant for tool-switching UI purposes, but there is noHandToolclass. The hand/pan behavior (viewport panning via space+drag and wheel zoom) is handled directly byInputPipelinein@hfu.digital/boardkit-react, not through theToolabstract class.
Creating Custom Tools
Extend the Tool abstract class and register it:
import { Tool, type InputEvent, type ToolResult, type SceneState } from '@hfu.digital/boardkit-core';
class StampTool extends Tool {
readonly id = 'stamp';
readonly name = 'Stamp';
state: ToolState = 'idle';
onPointerDown(event: InputEvent, scene: SceneState): ToolResult {
// Create a stamp element at the click position
return {
mutations: [{ type: 'create', elementId: 'stamp-1', pageId: '...', data: { /* ... */ }, timestamp: Date.now() }],
state: 'idle',
};
}
onPointerMove(_event: InputEvent, _scene: SceneState): ToolResult {
return { state: this.state };
}
onPointerUp(_event: InputEvent, _scene: SceneState): ToolResult {
return { state: 'idle' };
}
onCancel(): ToolResult {
return { state: 'idle' };
}
}
// Register
const registry = ToolRegistry.createDefault();
registry.register(new StampTool());