947 lines
52 KiB
Markdown
947 lines
52 KiB
Markdown
# 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<object, ...>` 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<string, boolean>` 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 `<input>` 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** — `<canvas>`-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<string, unknown>`, `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)
|