web based infinite canvas
at main 97 lines 2.7 kB view raw
1import type { Action } from "../actions"; 2import type { EditorState, ToolId } from "../reactivity"; 3 4/** 5 * Tool interface - defines behavior for each editor tool 6 * 7 * Tools are explicit state machines that handle user input actions. 8 * Each tool decides how to respond to actions and can update editor state. 9 */ 10export interface Tool { 11 /** Unique identifier for this tool */ 12 readonly id: ToolId; 13 14 /** 15 * Called when the tool becomes active 16 * 17 * @param state - Current editor state 18 * @returns Updated editor state 19 */ 20 onEnter(state: EditorState): EditorState; 21 22 /** 23 * Called when an action occurs while this tool is active 24 * 25 * @param state - Current editor state 26 * @param action - The action to handle 27 * @returns Updated editor state 28 */ 29 onAction(state: EditorState, action: Action): EditorState; 30 31 /** 32 * Called when the tool becomes inactive 33 * 34 * @param state - Current editor state 35 * @returns Updated editor state 36 */ 37 onExit(state: EditorState): EditorState; 38} 39 40/** 41 * Route an action to the currently active tool 42 * 43 * @param state - Current editor state 44 * @param action - Action to route 45 * @param tools - Map of tool ID to tool instance 46 * @returns Updated editor state after tool handles the action 47 */ 48export function routeAction(state: EditorState, action: Action, tools: Map<ToolId, Tool>): EditorState { 49 const currentTool = tools.get(state.ui.toolId); 50 if (!currentTool) return state; 51 return currentTool.onAction(state, action); 52} 53 54/** 55 * Switch from current tool to a new tool 56 * 57 * Calls onExit on the current tool (if it exists), then onEnter on the new tool. 58 * 59 * @param state - Current editor state 60 * @param newToolId - ID of tool to switch to 61 * @param tools - Map of tool ID to tool instance 62 * @returns Updated editor state with new tool active 63 */ 64export function switchTool(state: EditorState, newToolId: ToolId, tools: Map<ToolId, Tool>): EditorState { 65 if (state.ui.toolId === newToolId) { 66 return state; 67 } 68 69 const currentTool = tools.get(state.ui.toolId); 70 let nextState = state; 71 if (currentTool) { 72 nextState = currentTool.onExit(nextState); 73 } 74 75 nextState = { ...nextState, ui: { ...nextState.ui, toolId: newToolId } }; 76 77 const newTool = tools.get(newToolId); 78 if (newTool) { 79 nextState = newTool.onEnter(nextState); 80 } 81 82 return nextState; 83} 84 85/** 86 * Create a map of tools from an array 87 * 88 * @param toolList - Array of tool instances 89 * @returns Map of tool ID to tool instance 90 */ 91export function createToolMap(toolList: Tool[]): Map<ToolId, Tool> { 92 const map = new Map<ToolId, Tool>(); 93 for (const tool of toolList) { 94 map.set(tool.id, tool); 95 } 96 return map; 97}