# @blinksgg/canvas — Core Principles These principles guide every design decision in the canvas library. When in doubt, refer here. When principles conflict, earlier ones win. ## 1. Touch-first, mouse-compatible Design for fingers and stylus first, then ensure mouse works well. Every interaction has device-aware thresholds — 44px hit targets for fingers, 3px drag thresholds for pencil, palm rejection when stylus is active. Mouse is not the default; it is one of three equal input sources. > "Pencil draws, fingers navigate, mouse does both." > — input-classifier.ts ## 2. Headless core, optional UI All state lives in Jotai atoms with zero React dependency. The `core/` layer is a pure state machine: atoms in, atoms out. Components in `components/` are one possible UI — consumers can build entirely custom UIs by importing only `@blinksgg/canvas/core` and `@blinksgg/canvas/hooks`. ## 3. Local-first, sync-optional Every operation (drag, resize, copy, paste, undo, redo) works without a network connection. The clipboard is in-memory. Undo/redo uses delta-based snapshots in local state. The database layer (`db/`) is an optional add-on with an adapter interface — not a requirement. ## 4. Events are data, actions are configurable Canvas interactions produce typed events (NodeDoubleClick, BackgroundLongPress, etc.). What *happens* in response is a mapping in a settings store, not hardcoded logic. Users remap actions via presets or the SettingsPanel. This separates "what happened" from "what to do about it." ## 5. Atoms over context Prefer Jotai atoms over React context for shared state. Atoms are composable, testable without React, and don't cause provider waterfall re-renders. Use React context only for non-reactive configuration (style themes, callback refs). ## 6. Fully controllable headless API Every canvas operation must be executable from the `core/` layer without React. If a hook can do it, an atom or pure function in `core/` can do it too. The React hooks are convenience wrappers, not the only path. External tools, tests, and non-React integrations should have full control via `store.set(atom, value)`. ## 7. Performance by default Virtualization is on by default (only render visible nodes). The React Compiler handles memoization automatically. Edge paths only recompute when their connected nodes move. Delta-based history is O(1) for moves instead of O(N) full-graph snapshots. ## 8. Composition over configuration Provide small, focused hooks (`useNodeDrag`, `useNodeResize`, `useTapGesture`) that compose into complex behavior. The `Canvas` component is a convenient composition — not the only way to use the library. Advanced users compose hooks directly. ## 9. Backend-agnostic persistence The `CanvasStorageAdapter` interface defines CRUD + subscriptions. Implement it for any backend. The simplest path is callback props (`onNodePersist`) with no adapter at all.