# Changelog All notable changes to `@blinksgg/canvas` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [3.0.0] - 2026-03-11 — "React Compiler" ### BREAKING - Peer dependencies now require `react` / `react-dom` **^19.2.0** (was ^19.0.0) - Removed all manual `useCallback` / `useMemo` wrappers — React Compiler auto-optimizes ### Changed - Integrated **React Compiler** (`babel-plugin-react-compiler`) into Vitest pipeline - Added `@vitejs/plugin-react` for Babel transform support - Removed `useCallback` from `useGestureSystem.ts` (4), `GestureProvider.tsx` (1), `useGestureResolver.ts` (1) - Removed `useMemo` from `useGestureSystem.ts` (1), `GestureProvider.tsx` (1), `keyboard.ts` (1) - Replaced `useMemo(() => nextOwnerId++, [])` with `useState(() => nextOwnerId++)` in `GestureProvider.tsx` ### Tests - All 761 passing tests remain green — zero regressions ## [2.5.0] - 2026-03-11 — "Hook Integration" ### Changed - Exported `easeInOutCubic` from `useAnimatedLayout.ts` for independent testing ### Added - **`animated-layout.test.ts`** — 9 tests: easeInOutCubic boundary values, monotonicity, range - **`useActionExecutor-hook.test.ts`** — 6 tests: renderHook integration, return shape - **`usePlugin-hook.test.ts`** — 5 tests: register/unregister lifecycle, re-render stability - **`useLayout-hook.test.ts`** — 4 tests: useLayout, useGetGraphBounds, useFitToBounds ### Tests - 24 new tests across 4 new test files (first React hook integration tests) - Total passing: 473 across 54 test files ## [2.4.0] - 2026-03-11 — "Deep Logic" ### Added - **`drag-state-machine.test.ts`** — 11 tests: buildDragPositions, computeDragUpdates, isDragPrevented - **`action-types-enums.test.ts`** — 9 tests: ActionCategory enum, BuiltInActionId uniqueness - **`event-types-enums.test.ts`** — 10 tests: CanvasEventType, EVENT_TYPE_INFO completeness - **`gesture-rules-types.test.ts`** — 6 tests: GestureDescriptor/Pattern/Rule shapes ### Tests - 36 new tests across 4 new test files - Total passing: 449 across 50 test files ## [2.3.0] - 2026-03-11 — "Edge Coverage" ### Added - **`actions-node.test.ts`** — 8 tests: selection + node action registration verification - **`actions-viewport.test.ts`** — 6 tests: viewport + history action registration - **`modifier-helpers.test.ts`** — 9 tests: isRepeatBlocked, getSelectedNodeIds, resolveFocusableNodeId, getCurrentSubject - **`pointer-bindings-v2.test.ts`** — 6 tests: array integrity, unique IDs, pattern types - **`plugin-types.test.ts`** — 5 tests: PluginError construction, CanvasPlugin interface shape ### Tests - 34 new tests across 5 new test files - Total passing: 413 across 46 test files ## [2.2.0] - 2026-03-11 — "Total Coverage" ### Changed - Split `built-in-actions.ts` (342L) into `actions-node.ts` and `actions-viewport.ts`; original slimmed to 44L barrel ### Added - **`keyboard-bindings.test.ts`** — 6 tests: array integrity, unique IDs, standard shortcuts - **`gesture-classification.test.ts`** — 3 tests: findNearestNode contract, empty graph - **`viewport-commands.test.ts`** — 7 tests: command shapes, registration, descriptions - **`serialization-commands.test.ts`** — 5 tests: export/import shapes, registration - **`store-atoms.test.ts`** — 13 tests: default values, isCommandActive, currentInput, commandProgress - **`command-executor.test.ts`** — 4 tests: collectInput rejection, cancelCommand ### Tests - 38 new tests across 6 new test files - Total passing: 379 across 41 test files ## [2.1.0] - 2026-03-11 — "Store & Adapter" ### Added - **`command-line-store.test.ts`** — 11 tests covering open/close, search, select command, provide/skip/goBack input, error state - **`storage-adapter.test.ts`** — 10 tests covering InMemoryStorageAdapter CRUD for nodes/edges, batch ops, error handling ### Tests - 21 new tests across 2 new test files - Total passing: 341 across 35 test files ## [2.0.0] - 2026-03-11 — "Clean Architecture" ### ⚠ BREAKING CHANGES - Removed deprecated re-exports `addEdgeToLocalGraphAtom` and `removeEdgeFromLocalGraphAtom` from `graph-mutations.ts` — import from `graph-mutations-edges` instead - Renamed `gesturesV2` → `gestures` namespace export in main barrel ### Changed - `CANVAS_VERSION` bumped to `'2.0.0'` - Updated 4 source files and 16 test files to import edge atoms from `graph-mutations-edges` ### Added - **`action-executor.test.ts`** — 8 tests covering createActionContext, createActionContextFromTouchEvent - **`settings-store.test.ts`** — 12 tests covering default state, setEventMapping, resetSettings, togglePanel, virtualization, hasUnsavedChanges ### Tests - 20 new tests across 2 new test files - Total passing: 320 across 33 test files ## [1.98.0] - 2026-03-11 — "Commands & Dispatch" ### Added - **`dispatcher.test.ts`** — 10 tests covering registerAction/unregisterAction, dispatch simple and phase handlers, none action - **`clipboard-commands.test.ts`** — 10 tests covering copy/cut/paste/duplicate/deleteSelected definitions and registration - **`group-commands.test.ts`** — 10 tests covering group/ungroup/collapse/expand definitions and registration ### Tests - 30 new tests across 3 new test files - Total passing: 300 across 31 test files ## [1.95.0] - 2026-03-11 — "Final Polish" ### Added - **`port-types.test.ts`** — 16 tests covering calculatePortPosition, getNodePorts, canPortAcceptConnection, arePortsCompatible - **`command-registry.test.ts`** — 11 tests covering register/unregister, get/has, search, aliases, sorting - **`keyboard-contexts.test.ts`** — 7 tests covering unique IDs, priorities, arrow key and nudge bindings - **`canvas-styles.test.ts`** — 7 tests covering theme structure, mergeWithDefaults, light/dark differences ### Tests - 44 new tests across 4 new test files - Total passing: 270 across 28 test files ## [1.9.0] - 2026-03-11 — "Derived & Types" ### Added - **`event-types.ts`** — CanvasEventType enum, EventTypeInfo, EVENT_TYPE_INFO lookup - **`action-types.ts`** — ActionCategory enum, BuiltInActionId, ActionContext, ActionHelpers, ActionDefinition - **`settings-state-types.ts`** — EventActionMapping, SettingsPreset, CanvasSettingsState, DEFAULT_MAPPINGS - **`graph-derived-atoms.test.ts`** — 8 tests covering highestZIndex, nodeKeys, edgeKeys, reactivity - **`settings-types.test.ts`** — 10 tests covering enum integrity, EVENT_TYPE_INFO coverage, action ID uniqueness - **`canvas-serializer-validation.test.ts`** — 7 tests covering missing fields, wrong version, invalid nodes - **`viewport-store-actions.test.ts`** — 8 tests covering zoom/pan defaults, reset, coordinate transforms ### Changed - **`settings-types.ts`** — re-export barrel (419→~40L) ### Tests - 33 new tests across 4 new test files - Total passing: 226 across 24 test files ## [1.8.0] - 2026-03-11 — "Pipeline Polish" ### Added - **`gesture-provider-utils.ts`** — isEditableTarget, setHeldKeyValue, applyHeldKeyDelta, getCurrentSubject, getSubjectPosition, buildGuardContext - **`gesture-provider-utils.test.ts`** — 13 tests covering DOM detection, held-key immutability, key delta application - **`timed-state-runner.test.ts`** — 8 tests covering state transitions, tap, double-tap, long-press, cancel, destroy - **`contexts.test.ts`** — 5 tests covering PALM_REJECTION and ACTIVE_INTERACTION context priorities and integrity - **`command-store.test.ts`** — 5 tests covering open/close command line, phase transitions ### Changed - **`GestureProvider.tsx`** — delegates to `gesture-provider-utils` (347→~215L) ### Tests - 31 new tests across 4 new test files - Total passing: 193 across 20 test files ## [1.7.0] - 2026-03-11 — "Gesture Refine" ### Added - **`keyboard-bindings.ts`** — 24 keyboard shortcut bindings extracted from DEFAULT_CONTEXT - **`pointer-bindings.ts`** — 22 pointer/touch gesture bindings extracted from DEFAULT_CONTEXT - **`minimap-utils.ts`** — computeGraphBounds, getMinimapTransform, minimapToWorld - **`pointer-contexts.test.ts`** — 12 tests covering binding integrity, no duplicate IDs, pick mode contexts - **`minimap-utils.test.ts`** — 8 tests covering bounds calculation, transform, world conversion - **`canvas-serializer-roundtrip.test.ts`** — 9 tests covering export/import round-trip, viewport, offset, validation ### Changed - **`pointer-contexts.ts`** — assembles DEFAULT_CONTEXT from imported arrays (346→~120L) - **`Minimap.tsx`** — delegates to `minimap-utils` (310→~278L) ### Tests - 30 new tests across 3 new test files - Total passing: 162 across 16 test files ## [1.6.0] - 2026-03-11 — "Hook Coverage" ### Added - **`useCanvasGraph.test.ts`** — 6 tests covering nodeKeys, edgeKeys, getNodeAttributes - **`useCanvasSelection.test.ts`** — 8 tests covering select, deselect, toggle, add, remove, edge selection - **`useCanvasViewport.test.ts`** — 7 tests covering zoom, pan, viewportRect, constants - **`useCanvasHistory.test.ts`** — 6 tests covering recordSnapshot, undo, redo, clear, labels - **`useVirtualization.test.ts`** — 5 tests covering metrics, enable/disable/toggle - **`useCanvasSettings.test.ts`** — 8 tests covering mappings, presets, panel toggle, reset - **`plugin-registry.test.ts`** — 12 tests covering register/unregister, dependencies, conflicts, lifecycle - **`useDragStateMachine.ts`** — buildDragPositions, computeDragUpdates, isDragPrevented - **`useDragConstraints.ts`** — snapToGrid, clampToBounds, applyDragConstraints - **`modifier-helpers.ts`** — isRepeatBlocked, getSelectedNodeIds, getCurrentSubject, updateKeySubject - **`gesture-classification.ts`** — findNearestNode, cycleFocus, navigateFocus, activateFocusedNode ### Changed - **`useNodeDrag.ts`** — delegates to `useDragStateMachine` helpers (367→~310L) - **`input-action-helpers.ts`** — delegates to `modifier-helpers` + `gesture-classification` (374→~210L) ### Tests - 52 new tests across 7 new test files - Total passing: 132 across 13 test files ## [1.5.0] - 2026-03-11 — "Test & Harden" ### Added - **`graph-mutations.test.ts`** — 10 tests covering addNode, deleteNode, deleteEdge, loadGraphFromDb, drag lifecycle - **`clipboard-store.test.ts`** — 10 tests covering copy, cut, paste, duplicate, edge remapping, guards - **`group-store.test.ts`** — 12 tests covering collapse/expand, parent/child, circular guard, moveToGroup - **`history-store.test.ts`** — 10 tests covering push, undo, redo, clear, labels, edge cases - **`built-in-actions.test.ts`** — 14 tests covering registration, all 18 handler delegations, guards - **`ContextMenuAction`** — standalone touch-friendly action row component - **`ContextMenuDivider`** — section header/separator component - **`ComboboxOption`** — option row with icon, label, description, highlight - **`ComboboxSearch`** — search input with ARIA combobox role - **`EdgePath`** — drag handle visual indicator (44px touch target) - **`EdgeLabel`** — inline edge label positioning component ### Changed - **`NodeContextMenu.tsx`** — delegates to `ContextMenuAction` + `ContextMenuDivider` (367→~310L) - **`NodeTypeCombobox.tsx`** — delegates to `ComboboxOption` + `ComboboxSearch` (374→~310L) - **`EdgeOverlay.tsx`** — delegates handle to `EdgePath` (349→~310L) ### Tests - 57 new tests across 5 new test files - Total passing: 97 across 8 test files ## [1.4.0] - 2026-03-11 — "Stable DB" ### Added - **Adapter conformance test harness** — `runAdapterConformanceTests(name, factory)` validates any `CanvasStorageAdapter` implementation against the full CRUD + batch contract (15 tests) - **Batch operations** — optional `createNodes`, `deleteNodes`, `createEdges`, `deleteEdges` on `CanvasStorageAdapter` with sequential fallback in `InMemoryStorageAdapter` - **`PortHandle`** — standalone port handle component with drag, hover, and compatibility highlighting - **`SettingsPresets`** — preset management component (apply, save-as, delete, reset) - **`SettingsEventMap`** — event-to-action mapping table component - **`CollectorInputPhase`** — progress/prompt display for command input collection - **`CollectorSelectInput` / `CollectorBooleanInput`** — extracted select option and boolean input components with shared `ShortcutButton` ### Changed - **`@blinksgg/canvas/db`** — promoted from **Beta → Stable** - **`NodePorts.tsx`** — refactored from 406L → ~110L, delegates to `PortBar` + `PortHandle` - **`CommandInputCollector.tsx`** — refactored from 397L → ~165L, delegates to `CollectorInputPhase` + `CollectorSelectInput` - **`SettingsPanel.tsx`** — refactored from 392L → ~125L, delegates to `SettingsPresets` + `SettingsEventMap` - **`storage-adapter.test.ts`** — refactored to use conformance harness; retains only atom-level tests ### Tests - **`adapter-conformance.test.ts`** — 14 CRUD + 2 batch tests against `InMemoryStorageAdapter` - **`storage-adapter.test.ts`** — 3 atom integration tests ## [1.3.0] - 2026-03-11 — "Serialize" ### Added - **Canvas serialization** — `exportGraph(store)` / `importGraph(store, snapshot)` for full JSON snapshots of nodes, edges, positions, groups, and viewport state - **`CanvasSnapshot`** type — portable JSON schema (version 1) with `SerializedNode`, `SerializedEdge`, `SerializedGroup`, viewport, and metadata fields - **`validateSnapshot(data)`** — runtime schema validation with detailed error reporting - **Import options** — `clearExisting` (default true), `offsetPosition` for paste-at-cursor, `remapIds` for UUID regeneration to avoid conflicts - **Headless API integration** — `exportSnapshot()`, `importSnapshot()`, `validateSnapshot()` on `CanvasAPI` - **Built-in commands** — `exportCanvas` (alias: `export`) copies graph JSON to clipboard; `importCanvas` (alias: `import`) reads, validates, and loads from clipboard - **`createWheelHandler(config)`** — extracted wheel zoom logic into a standalone, testable pure function - **`createPinchHandlers(config)`** — extracted pinch zoom logic (simultaneous pan, velocity sampling, inertia) into a standalone, testable pure function ### Changed - **`useCanvasGestures.ts`** — refactored from 518 lines to ~370 lines by delegating wheel and pinch zoom to extracted modules - **`gestures/index.ts`** — exports `createWheelHandler`, `WheelZoomConfig`, `createPinchHandlers`, `PinchZoomConfig` - **`builtins/index.ts`** — registers `serialization-commands` alongside existing built-in command sets ### Tests - **`canvas-serializer.test.ts`** — 18 tests: export (7), import (8), round-trip (1), validation (9) - **`useWheelZoom.test.ts`** — 7 tests: zoom in/out, min/max clamp, guard conditions - **`usePinchZoom.test.ts`** — 5 tests: start/update/end lifecycle, disabled state ## [1.2.0] - 2026-03-10 — "Plugin API" ### Added - **Plugin system** — unified `CanvasPlugin` interface bundles node types, commands, actions, gesture contexts, and edge path calculators into a single declarative manifest - **`plugin-types.ts`** — `CanvasPlugin`, `PluginContext`, `PluginRegistration`, `PluginError` class with typed error codes (`ALREADY_REGISTERED`, `NOT_FOUND`, `MISSING_DEPENDENCY`, `CONFLICT`, `LIFECYCLE_ERROR`) - **`plugin-registry.ts`** — `registerPlugin()` / `unregisterPlugin()` with dependency resolution, cross-plugin conflict detection, atomic registration with rollback, and lifecycle hooks (`onRegister` → cleanup) - **`edge-path-registry.ts`** — extensible custom edge path calculator registry; plugins can register new edge path types beyond the 8 built-ins - **`usePlugin()` / `usePlugins()` hooks** — React integration for plugin lifecycle (register on mount, unregister on unmount, dependency-order cleanup) - **`unregisterNodeType()`** — per-type removal for clean plugin teardown - **2 new test suites** — `plugin-registry` (28 tests), `edge-path-registry` (8 tests) - Registration, duplicate detection, dependency resolution, conflict detection across all subsystems - Atomic rollback on lifecycle error, cleanup verification, query functions - Custom edge path precedence over built-ins, clear/unregister behavior ### Changed - **`Canvas.renderNode` is now optional** — when omitted, the node type registry auto-resolves components by `node.dbData.node_type`, including those registered by plugins. Falls back to `FallbackNodeTypeComponent` for unknown types. - **Barrel exports updated** — plugin types, registry, hooks, and edge path registry all exported from `core/`, `hooks/`, and `utils/` entry points ## [1.1.0] - 2026-03-10 — "Gesture Modular" ### Changed - **Split `useRegisterInputActions.ts`** (528L → 259L): extracted `input-action-helpers.ts` (~370L) with all pure store helper functions (navigation, selection, mutation, escape logic) - **Split `useCanvasGestures.ts`** (667L → 518L): extracted `useGuardContext.ts` (~70L) and `useInertia.ts` (~170L) - **3 new modules** — all exported from `@blinksgg/canvas/gestures`: - `input-action-helpers.ts` — `findNearestNode`, `cycleFocus`, `nudgeSelection`, `deleteSelection`, `escapeInput`, etc. - `useGuardContext.ts` — guard context atom reads + stable ref for gesture callbacks - `useInertia.ts` — pan/zoom inertia engines, velocity samplers, pinch tracking ### Added - **2 new test suites** — `input-action-helpers` (28 tests), `useInertia` (5 tests) - Navigation: `findNearestNode` in all 4 directions, axis preference, fallback behavior - Focus: `cycleFocus` forward/backward, `activateFocusedNode` - Mutations: `nudgeSelection`, `deleteSelection`, `selectAll` - Guards: `isRepeatBlocked`, `getCurrentSubject`, `clearSelectionState` - Zoom: `snapZoom` threshold behavior, constant validation ### Fixed - **WeakMap for module-level caches** — `_prevUiNodes`, `_edgeCache`, `_positionCache` now use `WeakMap` keyed by Graph instance, preventing cross-store contamination in multi-Canvas setups - **`clearAllPendingMutations()` on graph switch** — mutation queue is now cleared when switching graphs, preventing stale `inFlight` flags from blocking new mutations - **`hasPendingMutations()` checks `queuedUiProperties`** — was only checking `queuedPosition`, missing queued UI property updates - **`collectInput` rejects immediately** — was returning a never-resolving Promise, now throws with a descriptive error message - **`startMutation()` count includes descendants** — was only counting selected nodes, not their group descendants, causing imbalanced `completeMutation()` calls - **Timed-state config threading** — `onUp` now receives `config` parameter and uses `config.multiTapWindowMs` instead of hardcoded default - **localStorage key namespaced** — `'canvas-settings'` → `'@blinksgg/canvas/settings'` to prevent cross-application collisions - **Singleton keyboard routing documented** — `GestureProvider.tsx` now has a comment explaining the intentional global `activeOwnerId` pattern --- ## [1.0.0] - 2026-03-10 — "Stable Canvas" First stable release. All APIs are now covered by semver guarantees. ### Added - **API stability document** — `docs/api-stability.md` classifying all exports as Stable, Beta, or Experimental - **Migration guide** — `docs/migration-v1.md` covering v0.x → v1.0 breaking changes - **3 new hook test suites** — `useNodeDrag-atoms`, `useNodeResize-atoms`, `useForceLayout-atoms` - **`peerDependenciesMeta`** — d3-force, @tanstack/react-query, @blocknote/* marked as optional ### Changed - **Version bump** — 0.36.0 → 1.0.0 - **`onAction` callback type** — Now accepts `InputEvent` (pointer + keyboard union) instead of `GestureEvent` (pointer only). Use `event.kind === 'key'` guard to narrow. ### Highlights - 72+ test suites, 219+ exported atoms, zero TODO/FIXME comments - 8 entry points: root, core, hooks, components, commands, gestures, db, utils - React 19 native with React Compiler auto-memoization - Touch-first gesture pipeline with specificity-scored binding resolution - Local-first with optional Supabase sync via storage adapter - Full headless API for programmatic control without React --- ## [0.36.0] - 2026-03-09 — "Gesture Pipeline" ### Changed - **Split `contexts.ts`** (614L → 100L): extracted `keyboard-contexts.ts` (~200L) + `pointer-contexts.ts` (~280L) ### Added - **3 new test suites** — `gesture-specificity` (12), `gesture-mapper` (10), `gesture-dispatcher` (10) - Specificity scoring: type/subject/source/modifier/button/key matching - Mapper: context indexing, priority resolution, consumeInput, guards - Dispatcher: handler registry, phase routing, none/unknown handling --- ## [0.35.0] - 2026-03-09 — "Gesture Polish" ### Added - **3 new test suites** — `gesture-inertia` (12), `timed-state` (10), `gesture-normalize` (4) - VelocitySampler, PanInertia, ZoomInertia fully tested - Timed state machine: tap/double-tap/triple-tap/long-press/cancel - Modifier extraction from pointer events --- ## [0.34.0] - 2026-03-09 — "Layout Hooks" ### Changed - **Split `useNodeDrag.ts`** — extracted `DragMemo` + `UseNodeDragOptions` into `hooks/drag-types.ts` (~32L) ### Added - **2 new test suites** — `gesture-rules-defaults` (8), `keyboard-shortcuts` (5) --- ## [0.33.0] - 2026-03-09 — "Settings & Config" ### Changed - **Split `settings-store.ts`** — extracted presets + utility into `settings-presets.ts` (~85L) ### Added - **1 new test suite** — `settings-presets` (9 tests: preset config, uniqueness, event coverage, utility) --- ## [0.32.0] - 2026-03-09 — "Core Stores" ### Changed - **Split `history-store.ts`** — extracted pure delta functions into `history-actions.ts` (~160L): - `applyDelta`, `invertDelta`, `createSnapshot` now exported standalone ### Added - **2 new test suites** — `history-actions` (14), `group-store-utils` (7) --- ## [0.31.0] - 2026-03-09 — "Command System" ### Changed - **Split `commands/store.ts`** (343L) into 2 modules: - `store-atoms.ts` — core state + derived atoms (~95L) - `store.ts` — action atoms + helpers (~260L) ### Added - **3 new test suites** — `commands-registry` (13), `commands-executor` (3), `commands-store` (15) - Total: 670 tests across 54 files --- ## [0.30.0] - 2026-03-09 — "Gesture Engine" ### Changed - **Split `gesture-rules.ts`** (473L) into 3 focused modules: - `gesture-rules.ts` — scoring, resolver, index (~200L) - `gesture-rules-types.ts` — all type definitions (~117L) - `gesture-rules-defaults.ts` — default rules, labels, merge (~190L) ### Added - **3 new test suites** — `sync-store` (14), `gesture-rule-store` (12), `gesture-configs` (6) - Total: 639 tests across 51 files --- ## [0.29.0] - 2026-03-09 — "Registry Split" ### Changed - **Split `action-registry.ts`** (449L) into 2 modules: - `action-registry.ts` — registry CRUD + utilities (~120L) - `built-in-actions.ts` — all 20+ built-in action definitions (~340L) ### Added - **4 new test suites** — `graph-mutations-edges` (10), `graph-mutations-advanced` (8), `debug` (8), `component-registry` (6) - Total: 607 tests across 48 files --- ## [0.28.0] - 2026-03-09 — "Modular Mutations" ### Changed - **Split `graph-mutations.ts`** (524L) into 3 focused modules: - `graph-mutations.ts` — node CRUD, drag lifecycle, DB sync - `graph-mutations-edges.ts` — edge CRUD, animation, label editing - `graph-mutations-advanced.ts` — split/merge nodes, drop target ### Added - **4 new test suites** — `layout` (11), `hit-test` (4), `action-executor` (8), `mutation-queue` (7) - Total: 575 tests across 44 files --- ## [0.27.0] - 2026-03-09 — "Deep Test" ### Added - **4 new test suites** — `settings-store` (12), `edge-path-calculators` (12), `snap-store` (16), `locked-node-store` (10) - Total: 542 tests across 40 files - **`history-types.ts`** — extracted `HistoryDelta`, `HistoryEntry`, `HistoryState`, `GraphSnapshot` from `history-store.ts` --- ## [0.26.0] - 2026-03-09 — "Improve Core" ### Added - **Minimap edge drawing** — edges render as thin lines between node centers; new `edgeColor` prop - **Fuzzy multi-token search** — split query by whitespace, all tokens must match (order-independent); exported `fuzzyMatch` helper - **Edge search** — search now matches edge labels and types; `searchEdgeResultsAtom`, `searchEdgeResultCountAtom`, `searchTotalResultCountAtom` - **Smart alignment guides** — node-to-node edge/center snapping; `findAlignmentGuides`, `alignmentGuidesAtom`, `AlignmentGuides` component - **2 new test suites** — `alignment-guides.test.ts` (8 tests), 7 fuzzy tests added to `search-store.test.ts` - Total: 489 tests across 36 files ### Changed - **Clipboard cut** — `cutToClipboardAtom` now actually deletes selected nodes (with undo support via `pushHistoryAtom`) - **Edge search dimming** — `EdgeRenderer` keeps matched edges visible during search (checks `searchEdgeResultsAtom`) --- ## [0.25.0] - 2026-03-09 — "Test & Harden" ### Added - **8 new test suites** (65 tests) — `graph-mutations`, `graph-derived`, `graph-position`, `action-registry`, `interaction-store`, `reduced-motion-store`, `external-keyboard-store`, `toast-store` - Total: 474 tests across 35 files ### Changed - **Split `TouchActionButton`** — 559-line monolith → 3 files: `icons.tsx` (7 SVGs), `RadialMenu.tsx` (layout + items), `index.tsx` (orchestrator) --- ## [0.24.0] - 2026-03-09 — "iPad First" ### Added - **`prefers-reduced-motion` support** — new `prefersReducedMotionAtom` + `watchReducedMotionAtom`; pan/zoom inertia, animated layouts, and edge fade animations respect the OS setting - **CSS `prefers-reduced-motion`** — all canvas keyframe animations (search pulse, drop target, edge enter/exit, tap pulse) disabled when active - **Undo/redo in `TouchActionButton`** — primary FAB actions on touch devices (with `UndoIcon` / `RedoIcon`) - **External keyboard detection** — `hasExternalKeyboardAtom`; hides `TouchActionButton` when modifier keys are available - **Cursor hiding on touch** — `@media (pointer: coarse)` resets all cursors to `default` via `data-canvas-root` - **`data-canvas-root`** attribute on `Viewport` for CSS targeting ### Changed - **Safe-area margins** — `ViewportControls` and `TouchActionButton` use `max(16px, env(safe-area-inset-*))` instead of hardcoded `16px` --- ## [0.23.0] - 2026-03-09 — "Clean Barrel" ### Changed - **Slimmed `core/index.ts` barrel** — 458 → 85 lines (replaced 33 named export blocks with `export *`) - **Renamed** `CanvasToast` interface → `CanvasToastData` to resolve name collision with `CanvasToast` component ### Removed - **`findPortAtPosition`** (deprecated in v0.20) — use `hitTestPort` from `utils/hit-test` - **`resolveGestureIntent` / `isBackgroundGesture`** (deprecated in v0.14) — use `resolveGestureIndexed` from `gesture-rules` - **`gesture-resolver.test.ts`** — tests for removed functions - All `@deprecated` markers cleared from production code --- ## [0.22.0] - 2026-03-09 — "Modular Core" ### Changed - **Split `graph-store.ts`** (914 → 68 lines) into 4 focused modules: - `graph-store.ts` — core atoms (`graphAtom`, `graphUpdateVersionAtom`, drag/edge creation state) - `graph-position.ts` — position management (`nodePositionAtomFamily`, `updateNodePositionAtom`, cleanup) - `graph-derived.ts` — read-only UI state (`uiNodesAtom`, `nodeFamilyAtom`, `edgeFamilyAtom`) - `graph-mutations.ts` — write atoms (CRUD, split/merge, edge animations, DB sync) - **Zero public API changes** — all exports still available from `core/index.ts` barrel - Rewrote 51 import sites (25 production, 24 test, 2 dynamic) --- ## [0.21.0] - 2026-03-09 — "Debug" ### Changed - **Unified logging** — migrated all 33 raw `console.*` calls to `createDebug` with `debug` package namespaces - **`createDebug` extended** — now returns `debug()`, `.warn()`, `.error()` sub-loggers - `.warn()` and `.error()` are always enabled (bypass `DEBUG` filter) - `.warn()` routes to `console.warn`, `.error()` to `console.error` - Enable verbose logging: `localStorage.debug = 'canvas:*'` ### Files migrated - `useForceLayout.ts` (9 calls), `db/queries/nodes.ts` (5), `db/queries/edges.ts` (4) - `action-executor.ts` (6), `settings-store.ts` (3), `history-store.ts` (2) - `NodeErrorBoundary.tsx` (1), `NodeTypeCombobox.tsx` (1), `useAnimatedLayout.ts` (1), `NoteNode.tsx` (2) --- ## [0.20.1] - 2026-03-09 ### Added - **`hitTestNode` / `hitTestPort`** — testable hit-test utilities replacing 5 direct `document.elementFromPoint` calls - **`setHitTestProvider`** — swap DOM hit-testing for mocks in tests or SSR ### Changed - **`findPortAtPosition`** deprecated — delegates to `hitTestPort` --- ## [0.20.0] - 2026-03-09 — "Maintain" ### Fixed - **`atomFamily` deprecation** — migrated from deprecated `jotai/utils` to `jotai-family` package (prepares for jotai v3, eliminates deprecation warnings) - **Corrupt `graphology` install** — force-reinstalled pnpm store to fix missing dist files ### Removed - **Dead `CanvasConfig` type** — stale interface with `supabaseUrl`/`supabaseAnonKey` fields (unused, unexported) --- ## [0.17.0] - 2026-03-09 — "Connect" ### Added - **`EdgePreviewLine`** — animated SVG bezier preview during edge creation (dashed line with color states for idle/valid/snapped) - **Drag-from-port** — click a port in `NodePorts` to initiate edge creation (previously only available via `EdgeOverlay` handle) - **Port snap visual** — port dots scale up (1.3x) and glow when a drag is hovering over a compatible port - **`canConnect` validation** — new callback prop on `EdgeOverlay` to reject invalid connections before edge creation ### Changed - **`EdgeOverlayProps`** — added optional `canConnect` callback - **`NodePorts` / `PortBar`** — port elements now have `onPointerDown` for drag initiation --- ## [0.16.0] - 2026-03-09 — "Harden" ### Removed (breaking) - **`resolveGestureIntent`** — removed from public API; use `resolveGestureIndexed` from gesture-rules instead - **`isBackgroundGesture`** — removed from public API - **`nodeGestureConfig` / `viewportGestureConfig`** — removed deprecated constants; use `getNodeGestureConfig(source)` / `getViewportGestureConfig(source)` functions - **`useSupabaseClient` / `supabaseClientAtom`** — removed from public API; use `useStorageAdapter()` instead - **Legacy Supabase props** — `CanvasDbProvider` and `CanvasProvider` no longer accept `supabaseUrl` / `supabaseAnonKey`; pass an `adapter` prop instead ### Changed - **`CanvasProviderProps.adapter`** — now required (was optional) - **`CanvasDbProviderProps`** — simplified to single interface (was discriminated union) - **`utils/index.ts`** — exports `getNodeGestureConfig` / `getViewportGestureConfig` functions instead of deprecated constants - **README.md** — refreshed with v0.14 Gesture System v2 and v0.15 Performance sections; updated architecture diagram and state management table --- ## [0.15.0] - 2026-03-07 — "Perform" ### Added - **Spatial grid index** — `SpatialGrid` class with fixed-cell bucketing for O(visible) viewport culling; replaces O(N) linear scan in `visibleNodeKeysAtom` - **`spatialIndexAtom`** — Jotai atom rebuilding the grid on graph structure and position changes - **Performance instrumentation** — `canvasMark` / `canvasWrap` helpers with `performance.mark`/`performance.measure`; opt-in via `setPerfEnabled(true)` or `window.__canvasPerf?.(true)` - **`perfEnabledAtom`** — Jotai atom for perf toggle; `setPerfEnabled` imperative API - **Gestures subpath export** — `@blinksgg/canvas/gestures` for code-splitting the gesture pipeline - 17 new tests covering `SpatialGrid` (12) and perf instrumentation (5) ### Changed - **`edgeFamilyAtom`** — structural equality cache (`_edgeCache`) prevents re-renders when source/target positions and attributes haven't changed - **`uiNodesAtom`** — structural equality check: returns previous array reference when all entries match by id, position, and isDragging - **`updateNodePositionAtom`** — wrapped with `canvasMark('drag-frame')` for profiling the drag hot path - **`visibleNodeKeysAtom`** — uses `SpatialGrid.query()` + `canvasMark('virtualization-cull')` instead of linear `Array.filter` - **`virtualization-store`** — removed standalone `isNodeVisible` function in favor of spatial grid AABB intersection --- ## [0.14.0] - 2026-03-07 — "Unify" ### Added - **Gesture System v2** — unified pointer + keyboard input pipeline with 4-layer architecture: Normalize → Recognize → Resolve → Dispatch - **Specificity-scored binding resolution** — patterns scored by type (128), key (64), subjectKind (32), modifier (16/8), source (4), button (2); highest score wins across priority-sorted context stack - **Keyboard as parallel input stream** — `KeyInputEvent` type shares resolver and dispatcher with `PointerGestureEvent` via `InputEvent` union; `InputPattern` matches both kinds - **Built-in mapping contexts** — `PALM_REJECTION_CONTEXT` (pri 0), `ACTIVE_INTERACTION_CONTEXT` (pri 15), `SEARCH_CONTEXT` (pri 25), `KEYBOARD_MANIPULATE_CONTEXT` (pri 30), `KEYBOARD_NAVIGATE_CONTEXT` (pri 40), `DEFAULT_CONTEXT` (pri 100) - **Held keys** — `HeldKeysState` tracks pressed keyboard keys; pointer events carry `heldKeys` for patterns like `Space+drag → pan` - **Custom modifiers** — `Modifiers.custom?: Record` for non-keyboard flags (iPad toolbar buttons, stylus buttons); scored identically to standard modifiers - **Phase-aware action handlers** — `PhaseHandler` interface routes `onStart/onMove/onEnd/onInstant/onCancel` for pointer and `onDown/onUp` for keyboard; simple function handlers fire on `start`/`instant`/`down` - **`consumeInput` flag** — bindings can block lower-priority contexts from matching the same event - **`useGestureSystem` hook** — manages context stack, palm rejection toggle, held key state; produces pre-built `MappingIndex` for O(1) event bucketing - **`useCanvasGestures` hook** — wires pointer events from a viewport ref through the full pipeline with inertia support - **`useNodeGestures` hook** — per-node pointer event handlers feeding the pipeline with `{ kind: 'node', nodeId }` subject - **`useRegisterInputActions` hook** — registers all built-in action handlers (selection, viewport, clipboard, history, layout, navigation, search) - **`useInputModeGestureContext` hook** — auto-pushes/removes input mode contexts (pickNode, pickNodes, pickPoint) - **`GestureProvider` / `InputProvider`** — React context provider wrapping the gesture system for tree-wide access - **Keyboard shortcuts in gesture pipeline** — `/` (command line), `Escape`, `Delete`/`Backspace`, `Ctrl/Cmd+C/X/V/D/A/Z/Y/F/M` all routed through binding resolution instead of ad-hoc key handlers - **Keyboard navigate mode** — Arrow keys move focus, Enter enters manipulate mode, Space activates, Tab cycles nodes - **Keyboard manipulate mode** — Arrow keys nudge selected nodes (Shift = large nudge), Escape exits - **Search keyboard bindings** — `Enter`/`Shift+Enter` and `Ctrl/Cmd+G` cycle search results within gesture pipeline - **`SplitNode`, `GroupNodes`, `MergeNodes`** — added to `BuiltInActionId` enum; registered as proper pipeline actions with `ActionHelpers` callbacks - **Pan inertia** — `PanInertia` class with friction-based deceleration after drag release - **Timed state machine** — `TimedStateRunner` for tap counting, long-press detection, double/triple-tap recognition with configurable thresholds - **Interactive demo** — `GestureV2Demo` with Binding Composer (pointer/keyboard), Event Composer (pointer/keyboard), held key simulation, real-time event log with input kind badges - **Architecture docs** — `packages/canvas/docs/arch-gesture-system-v2.md`, `plan-gesture-system-v2.md`, `plan-keyboard-gesture-integration.md` - **5 new test suites** — specificity (16 tests), mapper (12), timed-state (8), palm-rejection (5), inertia (4) ### Changed - **`keyboard.ts`** — shortcut handling simplified; most key bindings moved to gesture pipeline contexts - **`Viewport.tsx`** — gesture intent resolution delegated to v2 pipeline when available - **`Node.tsx`** — pointer events can route through `useNodeGestures` pipeline - **`settings-types.ts`** — `ActionContext` gains optional `selectedNodeIds`; `ActionHelpers` gains optional `splitNode`, `groupNodes`, `mergeNodes` - **`interaction-store.ts`** — added `KeyboardInteractionMode` type (`'navigate' | 'manipulate'`) and `keyboardInteractionModeAtom` - **`selection-path-store.ts`** — exported `isSelectingAtom` for guard context access --- ## [0.13.0] - 2026-03-05 — "Refine" ### Added - **Undo/redo for structural operations** — `splitNodeAtom`, `mergeNodesAtom`, `nestNodesOnDropAtom`, `groupSelectedNodesAtom`, `ungroupNodesAtom` now push history snapshots with descriptive labels; all fully undoable - **Undo/redo toast feedback** — `canvasToastAtom`, `showToastAtom` in `toast-store.ts`; `CanvasToast` overlay component shows "Undo: {label}" / "Redo: {label}" for 2 seconds after Ctrl+Z/Y - **Tree layout** — `useTreeLayout` hook: BFS depth assignment, root detection, top-down/left-right direction; `treeLayout` and `horizontalLayout` commands - **Grid layout** — `useGridLayout` hook: spatial-order-preserving grid with auto column count; `gridLayout` command - **Animated layout transitions** — `useAnimatedLayout` shared hook: RAF loop with cubic easing, history snapshot before animation, persistence callback - **Zoom inertia** — pinch-to-zoom continues with momentum after fingers lift (friction=0.88, velocity tracking) - **Snap-to-100% zoom detent** — zoom snaps to 1.0 when within 0.03 threshold (both wheel and pinch) - **Simultaneous pan during pinch** — tracks finger midpoint movement frame-to-frame for fluid two-finger gestures - **Tap pulse animation** — `canvas-tap-pulse` CSS keyframe on ViewportControls buttons for tactile feedback - 19 new tests (404 total) covering undo for split/merge/nest/group/ungroup, tree layout algorithm, grid layout algorithm ### Changed - **`CommandProvider`** — wires `useTreeLayout` and `useGridLayout` into `CommandContext.layout` - **`useCanvasHistory`** — shows toast with operation label on undo/redo - **`Viewport`** — zoom inertia loop, snap-to-100% in onWheel/onPinch, simultaneous pan tracking in onPinch - **`ViewportControls`** — ControlButton applies `canvas-tap-pulse` class on click - **`CanvasAnimations`** — added `canvas-tap-pulse` keyframe --- ## [0.12.0] - 2026-03-05 — "Reshape" ### Added - **Drag-to-nest** — drag a node over another to nest it as a child; visual drop-target highlight with pulsing blue outline; `dropTargetNodeIdAtom`, `nestNodesOnDropAtom` - **Two-finger split** — two fingers diverging on a node (80px threshold) splits it into two copies with all edges duplicated; `splitNodeAtom`, `useSplitGesture` hook - **Merge nodes** — `Ctrl+M` or command palette merges 2+ selected nodes; edges re-routed to survivor, internal edges discarded; `mergeNodesAtom`, `mergeNodesCommand` - **`split-node` gesture intent** — added to `GestureIntent` type; pinch-on-node now maps to `split-node` instead of `zoom` - **`.canvas-drop-target` CSS** — pulsing blue outline animation for drop target feedback - 18 new tests (385 total) covering nest-on-drop, split-node, merge-nodes ### Changed - **`useNodeDrag`** — detects drop target via `elementFromPoint` during drag; nests nodes on drop end - **`Node`** — integrates `useSplitGesture` for two-finger split; shows drop-target highlight - **`useGlobalKeyboard`** — added `Ctrl+M` shortcut for merge --- ## [0.11.0] - 2026-03-05 — "Interactive" ### Added - **Nested group drag** — dragging a group node now moves all its descendants (children, nested groups) together via `getNodeDescendants` helper - **Edge deletion animations** — `removeEdgeWithAnimationAtom` snapshots edge state before removal and renders a fade-out animation via `departingEdgesAtom`; `DepartingEdgeItem` renders the exit animation - **Edge creation animations** — CSS fade-in animation class `.canvas-edge-enter` for newly created edges - **Edge label editing** — double-click an edge label to open an inline `` overlay; `editingEdgeLabelAtom` + `updateEdgeLabelAtom` manage state; `EdgeLabelEditor` component positioned at label world coords - **`EdgeLabelEditor`** component — HTML input overlay for inline edge label editing, included automatically in `Canvas` - 14 new tests (367 total) covering nested group drag, departing edges, edge label editing ### Changed - **`useNodeDrag`** — on first drag frame, expands `initialPositions` to include all descendants of group nodes being dragged - **`edgeFamilyAtom`** — now depends on `graphUpdateVersionAtom` for non-position attribute reactivity (label, color changes) - **`EdgeRenderer`** — renders departing edges with exit animation; edge labels support double-click to edit - **`CanvasAnimations`** — added `canvas-edge-fade-in`, `canvas-edge-fade-out` keyframes and `.canvas-edge-enter`, `.canvas-edge-exit` CSS classes --- ## [0.10.0] - 2026-03-05 — "Polish" ### Added - **Edge re-routing** — `collapsedEdgeRemapAtom` maps collapsed children to their outermost collapsed ancestor; edges visually re-route to the group node instead of disappearing - **Group auto-resize** — `autoResizeGroupAtom` recomputes group bounding box from children with padding; triggered automatically when drag ends on a child node - **Search keyboard shortcuts** — `Ctrl+F` opens search, `Enter`/`Shift+Enter` cycle results, `Ctrl+G`/`Ctrl+Shift+G` alternative navigation, `Escape` clears search before clearing selection - **Edge search dimming** — edges dim to `opacity: 0.2` when search is active and neither endpoint matches, with `150ms` CSS transition - **Search highlight pulse** — currently highlighted search result gets an animated amber box-shadow pulse (`canvas-search-highlight` CSS class) - **`CanvasAnimations`** component — injects CSS keyframes for search pulse and edge transitions - 11 new tests (353 total) covering edge remap, internal edge hiding, cross-boundary edges, auto-resize, and drag-triggered resize ### Changed - **`visibleEdgeKeysAtom`** — now remap-aware: resolves effective endpoints through `collapsedEdgeRemapAtom`, hides internal edges (both endpoints in same collapsed group) - **`edgeFamilyAtom`** — uses remapped positions when source/target is inside a collapsed group - **`endNodeDragAtom`** — triggers `autoResizeGroupAtom` for the parent group after drag ends - **`useGlobalKeyboard`** — `Escape` now clears search first (before clearing selection) --- ## [0.9.0] - 2026-03-05 — "Full Stack" ### Added - **Abstract persistence** — `CanvasStorageAdapter` interface with `SupabaseStorageAdapter` implementation and `InMemoryStorageAdapter`. `CanvasDbProvider` now accepts an `adapter` prop for any backend - **`storageAdapterAtom`** — Jotai atom for global adapter access; `useStorageAdapter()` hook - **Node grouping** — `parentId` attribute on graph nodes, collapsible container groups with `GroupNode` component - **`group-store`** — `collapsedGroupsAtom`, `toggleGroupCollapseAtom`, `nodeChildrenAtom`, `nodeParentAtom`, `setNodeParentAtom`, `groupSelectedNodesAtom`, `ungroupNodesAtom`, `isNodeCollapsed()` utility - **Group commands** — `groupNodes`, `ungroupNodes`, `collapseGroup`, `expandGroup` registered in command palette - **Search & filter** — `searchQueryAtom`, `searchResultsAtom` (case-insensitive substring match on label/type/id), `highlightedSearchIndexAtom` with next/prev navigation - **Search commands** — `searchNodes` (aliases: `find`, `search`) and `clearSearch` commands - **Node dimming** — non-matching nodes rendered with `opacity: 0.2` and `pointerEvents: none` when search filter is active - **Component tests** — 39 new React component tests (Canvas, Node, Viewport, Minimap, SelectionOverlay, GroupNode) using `@testing-library/react` - 89 new tests (342 total) ### Changed - **`uiNodesAtom`** — filters out nodes whose ancestor group is collapsed (walks parent chain) - **`CanvasDbProvider`** — accepts union props: `{ adapter }` (primary) or `{ supabaseUrl, supabaseAnonKey }` (legacy, deprecated) - **`CanvasProvider`** — accepts optional `adapter` prop alongside deprecated supabase credential props --- ## [0.8.0] - 2026-03-05 — "Full Picture" ### Added - **Minimap** — ``-based overview component showing all nodes with draggable viewport rectangle. Click/drag to pan. Configurable position, size, and colors - **Lasso selection** — pencil drag on background draws freeform lasso path; nodes whose center falls inside the polygon are selected - **Rect selection** — Shift+drag on background draws rectangular selection box; nodes overlapping the rect (AABB intersection) are selected - **`SelectionOverlay`** — SVG component rendering the active lasso/rect path with configurable colors - **`selection-path-store`** — new core store with `startSelectionAtom`, `updateSelectionAtom`, `endSelectionAtom`, `selectionPathAtom`, `selectionRectAtom` - **`pointInPolygon()`** — ray-casting utility for lasso selection, exported from core - **Zoom animations** — `animateZoomToNodeAtom` and `animateFitToBoundsAtom` write atoms for smooth animated viewport transitions with cubic ease-in-out - **`useZoomTransition`** — hook driving requestAnimationFrame-based zoom/pan animation loop - **`zoomAnimationTargetAtom`** — stores current animation target (start/end zoom/pan, duration) - 39 new tests (253 total) covering selection paths, point-in-polygon, and zoom animation atoms ### Changed - **Viewport.tsx** — now resolves gesture intent once at drag start (with modifier key state) and stores it in a ref, fixing Shift+drag rect-select and pencil lasso - **TODO.md** — marked v0.7 completions, updated roadmap for v0.9+ --- ## [0.7.0] - 2026-03-05 — "Solid Ground" ### Added - **Headless Canvas API** — `createCanvasAPI(store)` factory provides all canvas operations (selection, viewport, graph, history, clipboard, snap, virtualization, actions) without React - **`buildActionHelpers(store)`** — pure Jotai store-based ActionHelpers construction, extracted from `useActionExecutor` for headless use - **`fitToBoundsAtom`** — headless write atom for fitting viewport to graph or selection bounds - **`centerOnNodeAtom`** — headless write atom for centering viewport on a specific node - **Arrow key navigation** — spatial keyboard navigation between nodes (Arrow keys = directional focus, Enter/Space = select, Tab = cycle by z-index) - **`focusedNodeIdAtom`** — keyboard focus state, distinct from selection, with visual focus ring - **`useArrowKeyNavigation` hook** — React hook for enabling keyboard navigation - **`useCanvasGraph` hook** — convenience hook for graph structure access (nodeCount, edgeCount, getNode, getEdge) - **214 unit tests** — comprehensive coverage for graph-store, selection-store, history-store, clipboard-store, viewport-store, virtualization-store, and canvas-api (up from 47) ### Changed - **Edge re-render optimization** — `edgeFamilyAtom` no longer depends on `nodePositionUpdateCounterAtom` (global counter). Reactivity flows through `nodePositionAtomFamily` with structural equality caching. Performance: O(connected edges) per drag frame instead of O(all edges) - **`useActionExecutor` simplified** — refactored to use `buildActionHelpers` internally (~30 lines from ~160) - **`useFitToBounds` simplified** — delegates to `fitToBoundsAtom` instead of inline computation - **Position atom caching** — `nodePositionAtomFamily` returns same object reference for unchanged positions via `_positionCache` Map ### Fixed - Position atom cleanup now also clears the position cache to prevent stale references --- ## [0.6.0] - 2026-03-03 ### Changed - **React Compiler integration** — automatic memoization replaces 110 `useCallback`, 25 `useMemo`, 2 `React.memo` - **Context as JSX** — 3 providers modernized (`CanvasStyleProvider`, `CommandProvider`, `CanvasDbProvider`) - **Ref cleanup** — `CanvasStyleProvider` uses ref callback with cleanup for CSS variable lifecycle ### Removed - **Dropped React 18** — peerDependencies require `react@^19.0.0` --- ## [0.5.0] - 2026-03-03 ### Added - `NodeClick` event type — single-click events on nodes now flow through the action executor - `EdgeHover` / `EdgeLeave` observability callbacks on `Canvas` - `React.memo` on `EdgeItem` component for render optimization ### Fixed - `ConnectedNodeRenderer` virtualization — switched from `nodeKeysAtom` to `visibleNodeKeysAtom` - `ConnectedNode` forwards `onClick`, `onHover`, `onLeave` props correctly - `history-store` typed assertion for delta type checking --- ## [0.4.0] - 2026-03-03 ### Added - **Edge events** — `EdgeClick`, `EdgeDoubleClick`, `EdgeRightClick` with callback props on `Canvas` - **Observability callbacks** — `onSelectionChange`, `onViewportChange`, `onDragStart`, `onDragEnd`, `onNodeHover`, `onNodeLeave` - **Strong type contracts** — `UINodeProperties` replaces `Record`, `UINodeState` replaces `any` in event callbacks - `SelectEdge` built-in action in the action registry --- ## [0.3.0] - 2026-03-03 ### Added - All 8 event-action mappings fully wired (`NodeRightClick`, `NodeLongPress`, `BackgroundClick`) - `BackgroundClick` routed through action executor when `onBackgroundClick` prop is provided ### Fixed - `centerOnNode` viewport calculation — correctly reads node position from graph store --- ## [0.2.x] - 2025-01-14 ### Added #### Settings Panel - `CanvasEventType` enum for all canvas events (double-click, triple-click, right-click, long-press) - `ActionRegistry` with `registerAction()` for custom actions - Built-in actions: selection, viewport, node, layout, history (17 total) - `useCanvasSettings()` hook, `useActionExecutor()` hook - `SettingsPanel` headless UI component - Three built-in presets: Default, Minimal, Power User - `localStorage` persistence via `atomWithStorage` #### Layout System - `useFitToBounds()`, `useGetGraphBounds()`, `useSelectionBounds()`, `useForceLayout()` - Layout utilities: `calculateBounds()`, `checkNodesOverlap()`, `getNodeCenter()` #### Command Palette - `@blinksgg/canvas/commands` subpath export - `CommandProvider`, `CommandLine`, `CommandSearch`, `CommandInputCollector`, `CommandFeedbackOverlay` - `registerBuiltinCommands()`, `registerCommand()`, `useCommandLine()`, `useGlobalKeyboard()` - Keyboard shortcuts: `/`, `Cmd+K`, `Escape` #### Clipboard - Local-first copy/cut/paste/duplicate (`Ctrl+C/X/V/D`) #### Virtualization - Viewport culling (+200px buffer) #### Connection Ports - `PortDefinition`, `NodePorts` component, `calculatePortPosition()`, `arePortsCompatible()` #### Input System - `classifyPointer()` for finger/pencil/mouse classification - `resolveGestureIntent()` with palm rejection - Touch/stylus support with device-aware thresholds #### Other - `d3-force` added as peer dependency --- ## [0.2.0] - 2025-01-07 ### Added - **Subscription hooks** for reactive state access: - `useCanvasSelection()`, `useCanvasViewport()`, `useCanvasDrag()` - **New subpath export** `@blinksgg/canvas/hooks` - **Configurable edge paths** with 8 path types: - `bezier` (default), `bezier-vertical`, `bezier-smart` - `straight`, `step`, `step-vertical`, `step-smart`, `smooth-step` - Edge path calculator utilities from `@blinksgg/canvas/utils` ### Changed - `EdgeRenderer` uses configurable path calculators instead of hardcoded bezier curves --- ## [0.1.0] - 2025-01-01 ### Added - Initial release - Core state management with Jotai atoms - Node drag, resize, and selection - Edge creation and management - Viewport pan/zoom with gesture support - Supabase sync integration - Style theming system with CSS variables - Undo/redo history support --- ## Breaking Changes Guide ### Upgrading to 0.6.0 - **React 18 is no longer supported.** Update to React 19. - No API changes — the React Compiler handles memoization automatically. ### Upgrading to 0.2.0 No breaking changes. Subscription hooks are recommended over direct atom imports: ```tsx // Before import { selectedNodeIdsAtom } from '@blinksgg/canvas/core'; const selectedNodeIds = useAtomValue(selectedNodeIdsAtom); // After (recommended) import { useCanvasSelection } from '@blinksgg/canvas/hooks'; const { selectedNodeIds, count, hasSelection } = useCanvasSelection(); ``` --- ## Version Policy - **MAJOR** version: Breaking changes (removed/renamed exports, changed return types) - **MINOR** version: New features (new hooks, components, options) - **PATCH** version: Bug fixes (no API changes)