canvas/PRINCIPLES.md

74 lines
2.9 KiB
Markdown
Raw Normal View History

2026-03-11 18:42:08 -07:00
# @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.