# Migrating to @blinksgg/canvas v1.0 This guide covers breaking changes and migration steps from v0.x to v1.0. ## Requirements - **React 19.0+** — React 18 is not supported (React Compiler, context-as-JSX) - **Jotai 2.6+** — Required for atom families and store API - **Node 18+** — For ESM support ## Breaking Changes Summary ### From v0.14+ (Gesture System v2) **`onAction` callback type changed**: `GestureEvent` → `InputEvent` The `Canvas` component's `onAction` prop now receives `InputEvent` (union of pointer + keyboard events) instead of `GestureEvent` (pointer only). ```typescript // Before (v0.x) onAction={(event: GestureEvent, resolution) => { const { subject, worldPosition } = event; }} // After (v1.0) onAction={(event: InputEvent, resolution) => { if (event.kind === 'key') return; // skip keyboard events const { subject, worldPosition } = event; // narrowed to pointer }} ``` **Old gesture config replaced**: The v1 gesture config shape (event-to-action mapping objects) was replaced by the v2 specificity-based binding system. ```typescript // Before (v0.x) — removed // After (v1.0) — use binding contexts import { gesturesV2 } from '@blinksgg/canvas'; // Bindings are configured via contexts, not flat config ``` ### From v0.23+ (Removed Exports) These were deprecated in v0.16 and removed in v0.23: | Removed | Replacement | |---------|-------------| | `findPortAtPosition()` | Use `hitTestPort()` from `@blinksgg/canvas/utils` | | `resolveGestureIntent()` | Use `gesturesV2.resolve()` | | `isBackgroundGesture()` | Check `event.subject.kind === 'background'` | | `useSupabaseClient()` | Use `CanvasDbProvider` with storage adapter | ### From v0.6+ (React 19) - `Context.Provider` syntax → `` (React 19 style) - Manual `React.memo()` / `useCallback()` / `useMemo()` removed — React Compiler handles memoization - Ref cleanup functions used instead of `useEffect` return for pointer capture ## Import Path Changes All import paths are stable and unchanged since v0.14: ```typescript import { Canvas, useCanvasSelection, graphAtom } from '@blinksgg/canvas'; import { commandRegistry } from '@blinksgg/canvas/commands'; import { graphAtom, selectedNodeIdsAtom } from '@blinksgg/canvas/core'; import { useNodeDrag, useFitToBounds } from '@blinksgg/canvas/hooks'; import { gesturesV2 } from '@blinksgg/canvas'; // or from '@blinksgg/canvas/gestures' import { CanvasStorageAdapter } from '@blinksgg/canvas/db'; ``` ## Storage Adapter Migration If you were using the old Supabase provider props: ```typescript // Before (v0.x) — Supabase-specific props // After (v1.0) — adapter pattern import { CanvasDbProvider, SupabaseStorageAdapter } from '@blinksgg/canvas/db'; const adapter = new SupabaseStorageAdapter(supabaseClient); ``` Or use callback props for simple persistence (no adapter needed): ```typescript { await saveToYourBackend(nodeId, graphId, uiProps); }} /> ``` ## Peer Dependencies Required: - `react` ^19.0.0 - `react-dom` ^19.0.0 - `jotai` ^2.6.0 Optional (install only if using the feature): - `d3-force` ^3.0.0 — for `useForceLayout` hook - `@tanstack/react-query` ^5.17.0 — for query-based atoms - `jotai-tanstack-query` — for `atomsWithQuery` integration - `@blocknote/core`, `@blocknote/react`, `@blocknote/shadcn` ^0.45.0 — for `NoteNode` ## Gesture System v1 → v2 Mapping | v1 Gesture Config | v2 Equivalent | |-------------------|---------------| | `onNodeDoubleTap: 'fit-to-view'` | Default binding: double-tap node → `fit-to-view` | | `onBackgroundDoubleTap: 'create-node'` | Default binding: double-tap background → `create-node` | | `onNodeRightClick: 'context-menu'` | Default binding: right-click node → `context-menu` | | `onNodeLongPress: 'toggle-lock'` | Default binding: long-press node → `toggle-lock` | All default bindings are pre-configured. Override via `gestureConfig` prop or `addGestureRuleAtom`. ## Quick Start (v1.0) ```tsx import { Provider as JotaiProvider } from 'jotai'; import { Canvas, CanvasStyleProvider, graphAtom } from '@blinksgg/canvas'; function App() { return ( (
{node.label}
)} onAction={(event, resolution) => { if (event.kind === 'key') return; console.log(resolution.actionId, event.subject); }} />
); } ```