# Canvas Modularization Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Split `components/canvas/canvas.tsx` into clear, testable domain hooks while preserving all current canvas behavior and public APIs. **Architecture:** Keep `components/canvas/canvas.tsx` as the composition root and extract the largest logic clusters into domain-specific hooks. Start with sync and reconciliation because they are the most complex and most reusable, then move connection and drop flows, and only then extract a presentational view if it still adds value. **Tech Stack:** Next.js 16, React 19, Convex, React Flow, TypeScript --- ### Task 1: Lock in the current modularization seams **Files:** - Modify: `components/canvas/canvas.tsx` - Reference: `components/canvas/CLAUDE.md` - Reference: `docs/plans/2026-04-03-canvas-modularization-design.md` **Step 1:** Annotate the major responsibility boundaries inside `components/canvas/canvas.tsx` by grouping the existing code into the future modules: sync engine, reconciliation, interactions, connections, drop, and render. **Step 2:** List the exact state, refs, callbacks, and mutations owned by each future hook in a scratch note or temporary checklist before moving code. **Step 3:** Identify cross-cutting refs that must stay shared at the orchestrator level, especially optimistic ID handoff state and React Flow instance access. **Step 4:** Remove the scratch note once the extraction map is clear. ### Task 2: Extract pure helper logic from effect bodies first **Files:** - Modify: `components/canvas/canvas.tsx` - Modify: `components/canvas/canvas-helpers.ts` - Create: `components/canvas/canvas-flow-reconciliation-helpers.ts` - Test: `components/canvas/__tests__/canvas-flow-reconciliation-helpers.test.ts` **Step 1:** Move deterministic edge reconciliation helpers out of `useLayoutEffect` bodies into `components/canvas/canvas-flow-reconciliation-helpers.ts`. **Step 2:** Move deterministic node reconciliation helpers out of `useLayoutEffect` bodies into `components/canvas/canvas-flow-reconciliation-helpers.ts`. **Step 3:** Add tests for optimistic edge carry, endpoint remapping, and position pin cleanup. **Step 4:** Update `components/canvas/canvas.tsx` to call the extracted helpers without changing behavior. ### Task 3: Extract the sync engine hook **Files:** - Create: `components/canvas/use-canvas-sync-engine.ts` - Modify: `components/canvas/canvas.tsx` - Reference: `lib/canvas-op-queue.ts` - Reference: `lib/canvas-local-persistence.ts` - Test: `components/canvas/__tests__/use-canvas-sync-engine.test.ts` **Step 1:** Create `useCanvasSyncEngine` with the queue state, online state, and mutation wrappers that are currently centered around `components/canvas/canvas.tsx:258` and `components/canvas/canvas.tsx:984`. **Step 2:** Move deferred optimistic-node handling into the new hook, including pending resize, pending data, and pending move-after-create behavior. **Step 3:** Return a clear API from the hook for create, move, resize, update-data, remove-edge, batch-remove, and split-edge flows. **Step 4:** Replace the local inline sync logic in `components/canvas/canvas.tsx` with the hook. **Step 5:** Add focused tests for optimistic create handoff and deferred resize/update behavior. ### Task 4: Extract the flow reconciliation hook **Files:** - Create: `components/canvas/use-canvas-flow-reconciliation.ts` - Modify: `components/canvas/canvas.tsx` - Modify: `components/canvas/canvas-flow-reconciliation-helpers.ts` - Test: `components/canvas/__tests__/use-canvas-flow-reconciliation.test.ts` **Step 1:** Create `useCanvasFlowReconciliation` to own the current Convex-to-local `useLayoutEffect` synchronization for nodes and edges. **Step 2:** Pass in only the data it needs: Convex nodes, Convex edges, storage URLs, theme mode, local setters, and shared optimistic refs. **Step 3:** Keep drag-lock and resize-lock behavior unchanged while moving the effects. **Step 4:** Add tests or targeted assertions for the drag-lock and optimistic carry behavior that are hardest to reason about from the UI. ### Task 5: Extract node interaction logic **Files:** - Create: `components/canvas/use-canvas-node-interactions.ts` - Modify: `components/canvas/canvas.tsx` - Reference: `components/canvas/canvas-node-change-helpers.ts` **Step 1:** Move `onNodesChange`, edge intersection highlighting, drag start, drag, and drag stop into `useCanvasNodeInteractions`. **Step 2:** Keep the returned API explicit: handlers plus any local highlight state helpers. **Step 3:** Preserve all resize side effects and split-edge-on-drop behavior exactly. **Step 4:** Manually verify drag, resize, and edge split interactions on the canvas page. ### Task 6: Extract connection flows **Files:** - Create: `components/canvas/use-canvas-connections.ts` - Modify: `components/canvas/canvas.tsx` - Reference: `components/canvas/canvas-reconnect.ts` - Reference: `components/canvas/canvas-connection-drop-menu.tsx` **Step 1:** Move `onConnect`, `onConnectEnd`, connection drop menu state, and `handleConnectionDropPick` into `useCanvasConnections`. **Step 2:** Keep validation centralized around `validateCanvasConnection` and `validateCanvasConnectionByType`. **Step 3:** Reuse the existing reconnect hook by adapting it through the new connection hook rather than rewriting reconnect behavior. **Step 4:** Manually verify valid connection creation, invalid connection rejection, reconnect, and connection-drop node creation. ### Task 7: Extract drag-and-drop creation flows **Files:** - Create: `components/canvas/use-canvas-drop.ts` - Modify: `components/canvas/canvas.tsx` - Reference: `components/canvas/canvas-media-utils.ts` **Step 1:** Move `onDragOver` and `onDrop` into `useCanvasDrop`. **Step 2:** Keep support for both sidebar/browser JSON payloads and raw node type strings. **Step 3:** Preserve file upload drop behavior, including image dimension lookup and upload error toasts. **Step 4:** Manually verify node drop and image file drop flows. ### Task 8: Decide whether a presentational view extraction is still useful **Files:** - Create: `components/canvas/canvas-view.tsx` - Modify: `components/canvas/canvas.tsx` **Step 1:** Measure the remaining size and readability of `components/canvas/canvas.tsx` after hook extraction. **Step 2:** If the file still mixes visual structure with too much prop assembly, move the JSX tree into `components/canvas/canvas-view.tsx`. **Step 3:** Keep `canvas-view.tsx` presentational only; it should not own data-fetching, queue logic, or reconciliation effects. **Step 4:** Skip this task entirely if the orchestrator is already clearly readable. ### Task 9: Verify behavior after each extraction phase **Files:** - Verify only **Step 1:** Run `pnpm lint components/canvas/canvas.tsx components/canvas/*.ts components/canvas/*.tsx` or the project-equivalent lint command for touched files. **Step 2:** Run `pnpm tsc --noEmit` or the project-equivalent type-check command. **Step 3:** Run the targeted canvas tests added during the refactor. **Step 4:** Manually verify these flows on the real canvas page: create node, drag node, resize node, connect nodes, reconnect edge, delete node, drop node from sidebar, and drop image file. ### Task 10: Finish with small, reviewable commits **Files:** - Commit only **Step 1:** Commit after Task 3 with a message like `refactor(canvas): extract sync engine hook`. **Step 2:** Commit after Task 4 with a message like `refactor(canvas): extract flow reconciliation hook`. **Step 3:** Commit after Tasks 5 to 7 with messages scoped to interactions, connections, and drop handling. **Step 4:** If Task 8 is done, commit it separately as `refactor(canvas): extract canvas view`.