52 KiB
52 KiB
Changelog
All notable changes to @blinksgg/canvas will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[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/useMemowrappers — React Compiler auto-optimizes
Changed
- Integrated React Compiler (
babel-plugin-react-compiler) into Vitest pipeline - Added
@vitejs/plugin-reactfor Babel transform support - Removed
useCallbackfromuseGestureSystem.ts(4),GestureProvider.tsx(1),useGestureResolver.ts(1) - Removed
useMemofromuseGestureSystem.ts(1),GestureProvider.tsx(1),keyboard.ts(1) - Replaced
useMemo(() => nextOwnerId++, [])withuseState(() => nextOwnerId++)inGestureProvider.tsx
Tests
- All 761 passing tests remain green — zero regressions
[2.5.0] - 2026-03-11 — "Hook Integration"
Changed
- Exported
easeInOutCubicfromuseAnimatedLayout.tsfor independent testing
Added
animated-layout.test.ts— 9 tests: easeInOutCubic boundary values, monotonicity, rangeuseActionExecutor-hook.test.ts— 6 tests: renderHook integration, return shapeusePlugin-hook.test.ts— 5 tests: register/unregister lifecycle, re-render stabilityuseLayout-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, isDragPreventedaction-types-enums.test.ts— 9 tests: ActionCategory enum, BuiltInActionId uniquenessevent-types-enums.test.ts— 10 tests: CanvasEventType, EVENT_TYPE_INFO completenessgesture-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 verificationactions-viewport.test.ts— 6 tests: viewport + history action registrationmodifier-helpers.test.ts— 9 tests: isRepeatBlocked, getSelectedNodeIds, resolveFocusableNodeId, getCurrentSubjectpointer-bindings-v2.test.ts— 6 tests: array integrity, unique IDs, pattern typesplugin-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) intoactions-node.tsandactions-viewport.ts; original slimmed to 44L barrel
Added
keyboard-bindings.test.ts— 6 tests: array integrity, unique IDs, standard shortcutsgesture-classification.test.ts— 3 tests: findNearestNode contract, empty graphviewport-commands.test.ts— 7 tests: command shapes, registration, descriptionsserialization-commands.test.ts— 5 tests: export/import shapes, registrationstore-atoms.test.ts— 13 tests: default values, isCommandActive, currentInput, commandProgresscommand-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 statestorage-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
addEdgeToLocalGraphAtomandremoveEdgeFromLocalGraphAtomfromgraph-mutations.ts— import fromgraph-mutations-edgesinstead - Renamed
gesturesV2→gesturesnamespace export in main barrel
Changed
CANVAS_VERSIONbumped 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, createActionContextFromTouchEventsettings-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 actionclipboard-commands.test.ts— 10 tests covering copy/cut/paste/duplicate/deleteSelected definitions and registrationgroup-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, arePortsCompatiblecommand-registry.test.ts— 11 tests covering register/unregister, get/has, search, aliases, sortingkeyboard-contexts.test.ts— 7 tests covering unique IDs, priorities, arrow key and nudge bindingscanvas-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 lookupaction-types.ts— ActionCategory enum, BuiltInActionId, ActionContext, ActionHelpers, ActionDefinitionsettings-state-types.ts— EventActionMapping, SettingsPreset, CanvasSettingsState, DEFAULT_MAPPINGSgraph-derived-atoms.test.ts— 8 tests covering highestZIndex, nodeKeys, edgeKeys, reactivitysettings-types.test.ts— 10 tests covering enum integrity, EVENT_TYPE_INFO coverage, action ID uniquenesscanvas-serializer-validation.test.ts— 7 tests covering missing fields, wrong version, invalid nodesviewport-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, buildGuardContextgesture-provider-utils.test.ts— 13 tests covering DOM detection, held-key immutability, key delta applicationtimed-state-runner.test.ts— 8 tests covering state transitions, tap, double-tap, long-press, cancel, destroycontexts.test.ts— 5 tests covering PALM_REJECTION and ACTIVE_INTERACTION context priorities and integritycommand-store.test.ts— 5 tests covering open/close command line, phase transitions
Changed
GestureProvider.tsx— delegates togesture-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_CONTEXTpointer-bindings.ts— 22 pointer/touch gesture bindings extracted from DEFAULT_CONTEXTminimap-utils.ts— computeGraphBounds, getMinimapTransform, minimapToWorldpointer-contexts.test.ts— 12 tests covering binding integrity, no duplicate IDs, pick mode contextsminimap-utils.test.ts— 8 tests covering bounds calculation, transform, world conversioncanvas-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 tominimap-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, getNodeAttributesuseCanvasSelection.test.ts— 8 tests covering select, deselect, toggle, add, remove, edge selectionuseCanvasViewport.test.ts— 7 tests covering zoom, pan, viewportRect, constantsuseCanvasHistory.test.ts— 6 tests covering recordSnapshot, undo, redo, clear, labelsuseVirtualization.test.ts— 5 tests covering metrics, enable/disable/toggleuseCanvasSettings.test.ts— 8 tests covering mappings, presets, panel toggle, resetplugin-registry.test.ts— 12 tests covering register/unregister, dependencies, conflicts, lifecycleuseDragStateMachine.ts— buildDragPositions, computeDragUpdates, isDragPreventeduseDragConstraints.ts— snapToGrid, clampToBounds, applyDragConstraintsmodifier-helpers.ts— isRepeatBlocked, getSelectedNodeIds, getCurrentSubject, updateKeySubjectgesture-classification.ts— findNearestNode, cycleFocus, navigateFocus, activateFocusedNode
Changed
useNodeDrag.ts— delegates touseDragStateMachinehelpers (367→~310L)input-action-helpers.ts— delegates tomodifier-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 lifecycleclipboard-store.test.ts— 10 tests covering copy, cut, paste, duplicate, edge remapping, guardsgroup-store.test.ts— 12 tests covering collapse/expand, parent/child, circular guard, moveToGrouphistory-store.test.ts— 10 tests covering push, undo, redo, clear, labels, edge casesbuilt-in-actions.test.ts— 14 tests covering registration, all 18 handler delegations, guardsContextMenuAction— standalone touch-friendly action row componentContextMenuDivider— section header/separator componentComboboxOption— option row with icon, label, description, highlightComboboxSearch— search input with ARIA combobox roleEdgePath— drag handle visual indicator (44px touch target)EdgeLabel— inline edge label positioning component
Changed
NodeContextMenu.tsx— delegates toContextMenuAction+ContextMenuDivider(367→~310L)NodeTypeCombobox.tsx— delegates toComboboxOption+ComboboxSearch(374→~310L)EdgeOverlay.tsx— delegates handle toEdgePath(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 anyCanvasStorageAdapterimplementation against the full CRUD + batch contract (15 tests) - Batch operations — optional
createNodes,deleteNodes,createEdges,deleteEdgesonCanvasStorageAdapterwith sequential fallback inInMemoryStorageAdapter PortHandle— standalone port handle component with drag, hover, and compatibility highlightingSettingsPresets— preset management component (apply, save-as, delete, reset)SettingsEventMap— event-to-action mapping table componentCollectorInputPhase— progress/prompt display for command input collectionCollectorSelectInput/CollectorBooleanInput— extracted select option and boolean input components with sharedShortcutButton
Changed
@blinksgg/canvas/db— promoted from Beta → StableNodePorts.tsx— refactored from 406L → ~110L, delegates toPortBar+PortHandleCommandInputCollector.tsx— refactored from 397L → ~165L, delegates toCollectorInputPhase+CollectorSelectInputSettingsPanel.tsx— refactored from 392L → ~125L, delegates toSettingsPresets+SettingsEventMapstorage-adapter.test.ts— refactored to use conformance harness; retains only atom-level tests
Tests
adapter-conformance.test.ts— 14 CRUD + 2 batch tests againstInMemoryStorageAdapterstorage-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 CanvasSnapshottype — portable JSON schema (version 1) withSerializedNode,SerializedEdge,SerializedGroup, viewport, and metadata fieldsvalidateSnapshot(data)— runtime schema validation with detailed error reporting- Import options —
clearExisting(default true),offsetPositionfor paste-at-cursor,remapIdsfor UUID regeneration to avoid conflicts - Headless API integration —
exportSnapshot(),importSnapshot(),validateSnapshot()onCanvasAPI - 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 functioncreatePinchHandlers(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 modulesgestures/index.ts— exportscreateWheelHandler,WheelZoomConfig,createPinchHandlers,PinchZoomConfigbuiltins/index.ts— registersserialization-commandsalongside 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 conditionsusePinchZoom.test.ts— 5 tests: start/update/end lifecycle, disabled state
[1.2.0] - 2026-03-10 — "Plugin API"
Added
- Plugin system — unified
CanvasPlugininterface bundles node types, commands, actions, gesture contexts, and edge path calculators into a single declarative manifest plugin-types.ts—CanvasPlugin,PluginContext,PluginRegistration,PluginErrorclass 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-insusePlugin()/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.renderNodeis now optional — when omitted, the node type registry auto-resolves components bynode.dbData.node_type, including those registered by plugins. Falls back toFallbackNodeTypeComponentfor unknown types.- Barrel exports updated — plugin types, registry, hooks, and edge path registry all exported from
core/,hooks/, andutils/entry points
[1.1.0] - 2026-03-10 — "Gesture Modular"
Changed
- Split
useRegisterInputActions.ts(528L → 259L): extractedinput-action-helpers.ts(~370L) with all pure store helper functions (navigation, selection, mutation, escape logic) - Split
useCanvasGestures.ts(667L → 518L): extracteduseGuardContext.ts(~70L) anduseInertia.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 callbacksuseInertia.ts— pan/zoom inertia engines, velocity samplers, pinch tracking
Added
- 2 new test suites —
input-action-helpers(28 tests),useInertia(5 tests)- Navigation:
findNearestNodein all 4 directions, axis preference, fallback behavior - Focus:
cycleFocusforward/backward,activateFocusedNode - Mutations:
nudgeSelection,deleteSelection,selectAll - Guards:
isRepeatBlocked,getCurrentSubject,clearSelectionState - Zoom:
snapZoomthreshold behavior, constant validation
- Navigation:
Fixed
- WeakMap for module-level caches —
_prevUiNodes,_edgeCache,_positionCachenow useWeakMap<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 staleinFlightflags from blocking new mutationshasPendingMutations()checksqueuedUiProperties— was only checkingqueuedPosition, missing queued UI property updatescollectInputrejects immediately — was returning a never-resolving Promise, now throws with a descriptive error messagestartMutation()count includes descendants — was only counting selected nodes, not their group descendants, causing imbalancedcompleteMutation()calls- Timed-state config threading —
onUpnow receivesconfigparameter and usesconfig.multiTapWindowMsinstead of hardcoded default - localStorage key namespaced —
'canvas-settings'→'@blinksgg/canvas/settings'to prevent cross-application collisions - Singleton keyboard routing documented —
GestureProvider.tsxnow has a comment explaining the intentional globalactiveOwnerIdpattern
[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.mdclassifying all exports as Stable, Beta, or Experimental - Migration guide —
docs/migration-v1.mdcovering 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
onActioncallback type — Now acceptsInputEvent(pointer + keyboard union) instead ofGestureEvent(pointer only). Useevent.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): extractedkeyboard-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— extractedDragMemo+UseNodeDragOptionsintohooks/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 intosettings-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 intohistory-actions.ts(~160L):applyDelta,invertDelta,createSnapshotnow 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 syncgraph-mutations-edges.ts— edge CRUD, animation, label editinggraph-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— extractedHistoryDelta,HistoryEntry,HistoryState,GraphSnapshotfromhistory-store.ts
[0.26.0] - 2026-03-09 — "Improve Core"
Added
- Minimap edge drawing — edges render as thin lines between node centers; new
edgeColorprop - Fuzzy multi-token search — split query by whitespace, all tokens must match (order-independent); exported
fuzzyMatchhelper - Edge search — search now matches edge labels and types;
searchEdgeResultsAtom,searchEdgeResultCountAtom,searchTotalResultCountAtom - Smart alignment guides — node-to-node edge/center snapping;
findAlignmentGuides,alignmentGuidesAtom,AlignmentGuidescomponent - 2 new test suites —
alignment-guides.test.ts(8 tests), 7 fuzzy tests added tosearch-store.test.ts- Total: 489 tests across 36 files
Changed
- Clipboard cut —
cutToClipboardAtomnow actually deletes selected nodes (with undo support viapushHistoryAtom) - Edge search dimming —
EdgeRendererkeeps matched edges visible during search (checkssearchEdgeResultsAtom)
[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-motionsupport — newprefersReducedMotionAtom+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 (withUndoIcon/RedoIcon) - External keyboard detection —
hasExternalKeyboardAtom; hidesTouchActionButtonwhen modifier keys are available - Cursor hiding on touch —
@media (pointer: coarse)resets all cursors todefaultviadata-canvas-root data-canvas-rootattribute onViewportfor CSS targeting
Changed
- Safe-area margins —
ViewportControlsandTouchActionButtonusemax(16px, env(safe-area-inset-*))instead of hardcoded16px
[0.23.0] - 2026-03-09 — "Clean Barrel"
Changed
- Slimmed
core/index.tsbarrel — 458 → 85 lines (replaced 33 named export blocks withexport *) - Renamed
CanvasToastinterface →CanvasToastDatato resolve name collision withCanvasToastcomponent
Removed
findPortAtPosition(deprecated in v0.20) — usehitTestPortfromutils/hit-testresolveGestureIntent/isBackgroundGesture(deprecated in v0.14) — useresolveGestureIndexedfromgesture-rulesgesture-resolver.test.ts— tests for removed functions- All
@deprecatedmarkers 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.tsbarrel - 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 tocreateDebugwithdebugpackage namespaces createDebugextended — now returnsdebug(),.warn(),.error()sub-loggers.warn()and.error()are always enabled (bypassDEBUGfilter).warn()routes toconsole.warn,.error()toconsole.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 directdocument.elementFromPointcallssetHitTestProvider— swap DOM hit-testing for mocks in tests or SSR
Changed
findPortAtPositiondeprecated — delegates tohitTestPort
[0.20.0] - 2026-03-09 — "Maintain"
Fixed
atomFamilydeprecation — migrated from deprecatedjotai/utilstojotai-familypackage (prepares for jotai v3, eliminates deprecation warnings)- Corrupt
graphologyinstall — force-reinstalled pnpm store to fix missing dist files
Removed
- Dead
CanvasConfigtype — stale interface withsupabaseUrl/supabaseAnonKeyfields (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
NodePortsto initiate edge creation (previously only available viaEdgeOverlayhandle) - Port snap visual — port dots scale up (1.3x) and glow when a drag is hovering over a compatible port
canConnectvalidation — new callback prop onEdgeOverlayto reject invalid connections before edge creation
Changed
EdgeOverlayProps— added optionalcanConnectcallbackNodePorts/PortBar— port elements now haveonPointerDownfor drag initiation
[0.16.0] - 2026-03-09 — "Harden"
Removed (breaking)
resolveGestureIntent— removed from public API; useresolveGestureIndexedfrom gesture-rules insteadisBackgroundGesture— removed from public APInodeGestureConfig/viewportGestureConfig— removed deprecated constants; usegetNodeGestureConfig(source)/getViewportGestureConfig(source)functionsuseSupabaseClient/supabaseClientAtom— removed from public API; useuseStorageAdapter()instead- Legacy Supabase props —
CanvasDbProviderandCanvasProviderno longer acceptsupabaseUrl/supabaseAnonKey; pass anadapterprop instead
Changed
CanvasProviderProps.adapter— now required (was optional)CanvasDbProviderProps— simplified to single interface (was discriminated union)utils/index.ts— exportsgetNodeGestureConfig/getViewportGestureConfigfunctions 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 —
SpatialGridclass with fixed-cell bucketing for O(visible) viewport culling; replaces O(N) linear scan invisibleNodeKeysAtom spatialIndexAtom— Jotai atom rebuilding the grid on graph structure and position changes- Performance instrumentation —
canvasMark/canvasWraphelpers withperformance.mark/performance.measure; opt-in viasetPerfEnabled(true)orwindow.__canvasPerf?.(true) perfEnabledAtom— Jotai atom for perf toggle;setPerfEnabledimperative API- Gestures subpath export —
@blinksgg/canvas/gesturesfor 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 changeduiNodesAtom— structural equality check: returns previous array reference when all entries match by id, position, and isDraggingupdateNodePositionAtom— wrapped withcanvasMark('drag-frame')for profiling the drag hot pathvisibleNodeKeysAtom— usesSpatialGrid.query()+canvasMark('virtualization-cull')instead of linearArray.filtervirtualization-store— removed standaloneisNodeVisiblefunction 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 —
KeyInputEventtype shares resolver and dispatcher withPointerGestureEventviaInputEventunion;InputPatternmatches 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 —
HeldKeysStatetracks pressed keyboard keys; pointer events carryheldKeysfor patterns likeSpace+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 —
PhaseHandlerinterface routesonStart/onMove/onEnd/onInstant/onCancelfor pointer andonDown/onUpfor keyboard; simple function handlers fire onstart/instant/down consumeInputflag — bindings can block lower-priority contexts from matching the same eventuseGestureSystemhook — manages context stack, palm rejection toggle, held key state; produces pre-builtMappingIndexfor O(1) event bucketinguseCanvasGestureshook — wires pointer events from a viewport ref through the full pipeline with inertia supportuseNodeGestureshook — per-node pointer event handlers feeding the pipeline with{ kind: 'node', nodeId }subjectuseRegisterInputActionshook — registers all built-in action handlers (selection, viewport, clipboard, history, layout, navigation, search)useInputModeGestureContexthook — 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/Mall 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+EnterandCtrl/Cmd+Gcycle search results within gesture pipeline SplitNode,GroupNodes,MergeNodes— added toBuiltInActionIdenum; registered as proper pipeline actions withActionHelperscallbacks- Pan inertia —
PanInertiaclass with friction-based deceleration after drag release - Timed state machine —
TimedStateRunnerfor tap counting, long-press detection, double/triple-tap recognition with configurable thresholds - Interactive demo —
GestureV2Demowith 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 contextsViewport.tsx— gesture intent resolution delegated to v2 pipeline when availableNode.tsx— pointer events can route throughuseNodeGesturespipelinesettings-types.ts—ActionContextgains optionalselectedNodeIds;ActionHelpersgains optionalsplitNode,groupNodes,mergeNodesinteraction-store.ts— addedKeyboardInteractionModetype ('navigate' | 'manipulate') andkeyboardInteractionModeAtomselection-path-store.ts— exportedisSelectingAtomfor guard context access
[0.13.0] - 2026-03-05 — "Refine"
Added
- Undo/redo for structural operations —
splitNodeAtom,mergeNodesAtom,nestNodesOnDropAtom,groupSelectedNodesAtom,ungroupNodesAtomnow push history snapshots with descriptive labels; all fully undoable - Undo/redo toast feedback —
canvasToastAtom,showToastAtomintoast-store.ts;CanvasToastoverlay component shows "Undo: {label}" / "Redo: {label}" for 2 seconds after Ctrl+Z/Y - Tree layout —
useTreeLayouthook: BFS depth assignment, root detection, top-down/left-right direction;treeLayoutandhorizontalLayoutcommands - Grid layout —
useGridLayouthook: spatial-order-preserving grid with auto column count;gridLayoutcommand - Animated layout transitions —
useAnimatedLayoutshared 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-pulseCSS 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— wiresuseTreeLayoutanduseGridLayoutintoCommandContext.layoutuseCanvasHistory— shows toast with operation label on undo/redoViewport— zoom inertia loop, snap-to-100% in onWheel/onPinch, simultaneous pan tracking in onPinchViewportControls— ControlButton appliescanvas-tap-pulseclass on clickCanvasAnimations— addedcanvas-tap-pulsekeyframe
[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,useSplitGesturehook - Merge nodes —
Ctrl+Mor command palette merges 2+ selected nodes; edges re-routed to survivor, internal edges discarded;mergeNodesAtom,mergeNodesCommand split-nodegesture intent — added toGestureIntenttype; pinch-on-node now maps tosplit-nodeinstead ofzoom.canvas-drop-targetCSS — 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 viaelementFromPointduring drag; nests nodes on drop endNode— integratesuseSplitGesturefor two-finger split; shows drop-target highlightuseGlobalKeyboard— addedCtrl+Mshortcut 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
getNodeDescendantshelper - Edge deletion animations —
removeEdgeWithAnimationAtomsnapshots edge state before removal and renders a fade-out animation viadepartingEdgesAtom;DepartingEdgeItemrenders the exit animation - Edge creation animations — CSS fade-in animation class
.canvas-edge-enterfor newly created edges - Edge label editing — double-click an edge label to open an inline
<input>overlay;editingEdgeLabelAtom+updateEdgeLabelAtommanage state;EdgeLabelEditorcomponent positioned at label world coords EdgeLabelEditorcomponent — HTML input overlay for inline edge label editing, included automatically inCanvas- 14 new tests (367 total) covering nested group drag, departing edges, edge label editing
Changed
useNodeDrag— on first drag frame, expandsinitialPositionsto include all descendants of group nodes being draggededgeFamilyAtom— now depends ongraphUpdateVersionAtomfor non-position attribute reactivity (label, color changes)EdgeRenderer— renders departing edges with exit animation; edge labels support double-click to editCanvasAnimations— addedcanvas-edge-fade-in,canvas-edge-fade-outkeyframes and.canvas-edge-enter,.canvas-edge-exitCSS classes
[0.10.0] - 2026-03-05 — "Polish"
Added
- Edge re-routing —
collapsedEdgeRemapAtommaps collapsed children to their outermost collapsed ancestor; edges visually re-route to the group node instead of disappearing - Group auto-resize —
autoResizeGroupAtomrecomputes group bounding box from children with padding; triggered automatically when drag ends on a child node - Search keyboard shortcuts —
Ctrl+Fopens search,Enter/Shift+Entercycle results,Ctrl+G/Ctrl+Shift+Galternative navigation,Escapeclears search before clearing selection - Edge search dimming — edges dim to
opacity: 0.2when search is active and neither endpoint matches, with150msCSS transition - Search highlight pulse — currently highlighted search result gets an animated amber box-shadow pulse (
canvas-search-highlightCSS class) CanvasAnimationscomponent — 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 throughcollapsedEdgeRemapAtom, hides internal edges (both endpoints in same collapsed group)edgeFamilyAtom— uses remapped positions when source/target is inside a collapsed groupendNodeDragAtom— triggersautoResizeGroupAtomfor the parent group after drag endsuseGlobalKeyboard—Escapenow clears search first (before clearing selection)
[0.9.0] - 2026-03-05 — "Full Stack"
Added
- Abstract persistence —
CanvasStorageAdapterinterface withSupabaseStorageAdapterimplementation andInMemoryStorageAdapter.CanvasDbProvidernow accepts anadapterprop for any backend storageAdapterAtom— Jotai atom for global adapter access;useStorageAdapter()hook- Node grouping —
parentIdattribute on graph nodes, collapsible container groups withGroupNodecomponent group-store—collapsedGroupsAtom,toggleGroupCollapseAtom,nodeChildrenAtom,nodeParentAtom,setNodeParentAtom,groupSelectedNodesAtom,ungroupNodesAtom,isNodeCollapsed()utility- Group commands —
groupNodes,ungroupNodes,collapseGroup,expandGroupregistered in command palette - Search & filter —
searchQueryAtom,searchResultsAtom(case-insensitive substring match on label/type/id),highlightedSearchIndexAtomwith next/prev navigation - Search commands —
searchNodes(aliases:find,search) andclearSearchcommands - Node dimming — non-matching nodes rendered with
opacity: 0.2andpointerEvents: nonewhen 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 optionaladapterprop 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 colorsselection-path-store— new core store withstartSelectionAtom,updateSelectionAtom,endSelectionAtom,selectionPathAtom,selectionRectAtompointInPolygon()— ray-casting utility for lasso selection, exported from core- Zoom animations —
animateZoomToNodeAtomandanimateFitToBoundsAtomwrite atoms for smooth animated viewport transitions with cubic ease-in-out useZoomTransition— hook driving requestAnimationFrame-based zoom/pan animation loopzoomAnimationTargetAtom— 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 fromuseActionExecutorfor headless usefitToBoundsAtom— headless write atom for fitting viewport to graph or selection boundscenterOnNodeAtom— 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 ringuseArrowKeyNavigationhook — React hook for enabling keyboard navigationuseCanvasGraphhook — 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 —
edgeFamilyAtomno longer depends onnodePositionUpdateCounterAtom(global counter). Reactivity flows throughnodePositionAtomFamilywith structural equality caching. Performance: O(connected edges) per drag frame instead of O(all edges) useActionExecutorsimplified — refactored to usebuildActionHelpersinternally (~30 lines from ~160)useFitToBoundssimplified — delegates tofitToBoundsAtominstead of inline computation- Position atom caching —
nodePositionAtomFamilyreturns same object reference for unchanged positions via_positionCacheMap
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, 25useMemo, 2React.memo - Context as JSX — 3 providers modernized (
CanvasStyleProvider,CommandProvider,CanvasDbProvider) - Ref cleanup —
CanvasStyleProvideruses 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
NodeClickevent type — single-click events on nodes now flow through the action executorEdgeHover/EdgeLeaveobservability callbacks onCanvasReact.memoonEdgeItemcomponent for render optimization
Fixed
ConnectedNodeRenderervirtualization — switched fromnodeKeysAtomtovisibleNodeKeysAtomConnectedNodeforwardsonClick,onHover,onLeaveprops correctlyhistory-storetyped assertion for delta type checking
[0.4.0] - 2026-03-03
Added
- Edge events —
EdgeClick,EdgeDoubleClick,EdgeRightClickwith callback props onCanvas - Observability callbacks —
onSelectionChange,onViewportChange,onDragStart,onDragEnd,onNodeHover,onNodeLeave - Strong type contracts —
UINodePropertiesreplacesRecord<string, unknown>,UINodeStatereplacesanyin event callbacks SelectEdgebuilt-in action in the action registry
[0.3.0] - 2026-03-03
Added
- All 8 event-action mappings fully wired (
NodeRightClick,NodeLongPress,BackgroundClick) BackgroundClickrouted through action executor whenonBackgroundClickprop is provided
Fixed
centerOnNodeviewport calculation — correctly reads node position from graph store
[0.2.x] - 2025-01-14
Added
Settings Panel
CanvasEventTypeenum for all canvas events (double-click, triple-click, right-click, long-press)ActionRegistrywithregisterAction()for custom actions- Built-in actions: selection, viewport, node, layout, history (17 total)
useCanvasSettings()hook,useActionExecutor()hookSettingsPanelheadless UI component- Three built-in presets: Default, Minimal, Power User
localStoragepersistence viaatomWithStorage
Layout System
useFitToBounds(),useGetGraphBounds(),useSelectionBounds(),useForceLayout()- Layout utilities:
calculateBounds(),checkNodesOverlap(),getNodeCenter()
Command Palette
@blinksgg/canvas/commandssubpath exportCommandProvider,CommandLine,CommandSearch,CommandInputCollector,CommandFeedbackOverlayregisterBuiltinCommands(),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,NodePortscomponent,calculatePortPosition(),arePortsCompatible()
Input System
classifyPointer()for finger/pencil/mouse classificationresolveGestureIntent()with palm rejection- Touch/stylus support with device-aware thresholds
Other
d3-forceadded 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-smartstraight,step,step-vertical,step-smart,smooth-step
- Edge path calculator utilities from
@blinksgg/canvas/utils
Changed
EdgeRendereruses 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:
// 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)