snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-23 16:12:52 +05:00
commit a774ac673a
4541 changed files with 770499 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
---
title: Input handling
created_at: 12/20/2025
updated_at: 12/20/2025
keywords:
- input
- pointer
- keyboard
- InputsManager
- events
reviewed_by: steveruizok
status: published
date: 12/20/2025
readability: 9
voice: 8
completeness: 9
accuracy: 9
notes: 'Passive constructions ("is calculated", "are added") and formulaic transition ("For each space"). Add API links like [InputsManager](?).'
---
The [InputsManager](?) class tracks pointer and keyboard state for the editor. It stores pointer positions in both screen space and page space, tracks pressed keys and buttons, detects device types (mouse, touch, pen), and calculates pointer velocity. Access it through `editor.inputs`.
All input state is reactive. The manager stores values as atoms from `@tldraw/state`, so components that read input state automatically update when those values change. The manager updates on every input event, converting coordinates between screen space and page space.
## Pointer position tracking
The manager tracks pointer positions in two coordinate spaces. Screen space is pixels relative to the canvas container's origin. Page space is the position on the infinite canvas, adjusted for camera position and zoom.
In each space, the manager keeps three positions: current, previous, and origin.
### Current and previous positions
```typescript
editor.inputs.getCurrentScreenPoint() // Current position in screen space
editor.inputs.getCurrentPagePoint() // Current position in page space
editor.inputs.getPreviousScreenPoint() // Previous position in screen space
editor.inputs.getPreviousPagePoint() // Previous position in page space
```
The current position updates on every pointer event. The previous position stores where the pointer was before the most recent update. You can use these together to calculate deltas for dragging and panning:
```typescript
const delta = Vec.Sub(editor.inputs.getCurrentPagePoint(), editor.inputs.getPreviousPagePoint())
```
### Origin positions
The origin position captures where the most recent `pointer_down` event occurred:
```typescript
editor.inputs.getOriginScreenPoint() // Where pointer_down occurred in screen space
editor.inputs.getOriginPagePoint() // Where pointer_down occurred in page space
```
Tools use the origin to calculate drag distances and determine whether an interaction has moved far enough to trigger behaviors like dragging. The origin resets on every `pointer_down` event and continuously while a pinch is in progress.
### Coordinate space conversion
The manager converts screen coordinates to page coordinates using the camera's position and zoom:
```typescript
// Screen to page conversion
const pageX = screenX / camera.z - camera.x
const pageY = screenY / camera.z - camera.y
```
## Pointer velocity
The manager tracks pointer velocity for gesture detection:
```typescript
editor.inputs.getPointerVelocity() // Vec with x/y velocity in pixels per millisecond
```
The manager listens to the editor's `frame` event and recomputes velocity once per frame (not on each pointer event) from the screen-space distance traveled since the previous frame. It smooths the result against the previous value and clamps components below 0.01 to zero to prevent jitter.
Tools use velocity to distinguish between slow, precise interactions and fast flick gestures. Velocity resets to zero on `pointer_down` events and continuously while a pinch is in progress.
## Input device detection
The manager tracks whether the most recent pointer event came from a pen (`pointerType === 'pen'`):
```typescript
editor.inputs.getIsPen() // true for stylus input
```
Pen mode ignores non-pen input to prevent accidental touch interactions while using a stylus. The editor only turns pen mode on automatically for direct-display pens (Apple Pencil, Surface Pen), flagged as `isPenDirect` on the pointer event; desktop graphics tablets still draw as pens without enabling it.
## Modifier keys and button states
The manager tracks modifier key states:
```typescript
editor.inputs.getShiftKey()
editor.inputs.getAltKey()
editor.inputs.getCtrlKey()
editor.inputs.getMetaKey()
editor.inputs.getAccelKey() // Cmd on Mac, Ctrl elsewhere
```
The `getAccelKey()` method returns true for Command on macOS and Control on other platforms. Use this for cross-platform shortcuts.
### Button tracking
The manager tracks currently pressed pointer buttons in a reactive set:
```typescript
editor.inputs.buttons.has(0) // Primary button (left click)
editor.inputs.buttons.has(1) // Middle button
editor.inputs.buttons.has(2) // Secondary button (right click)
```
Buttons are added on `pointer_down` events and removed on `pointer_up` events.
### Keyboard key tracking
The manager tracks pressed keyboard keys in a reactive set:
```typescript
editor.inputs.keys.has('Space')
editor.inputs.keys.has('ShiftLeft')
```
The editor adds keys on `key_down` and removes them on `key_up`. Tools can use this to detect held keys during pointer operations. For example, the editor checks `keys.has('Space')` on pointer up to decide whether to keep spacebar panning active.
## Interaction state flags
The manager tracks the current interaction state:
```typescript
editor.inputs.getIsPointing() // Pointer button is down
editor.inputs.getIsRightPointing() // Right button is down, before the drag threshold
editor.inputs.getIsDragging() // Pointer moved beyond drag threshold while pointing
editor.inputs.getIsPinching() // Two-finger pinch gesture active
editor.inputs.getIsEditing() // Editing text or other content
editor.inputs.getIsPanning() // Panning the canvas
editor.inputs.getIsSpacebarPanning() // Panning via spacebar (vs. other panning modes)
```
The editor sets these flags during event processing. For example, `isPointing` becomes true on `pointer_down` and false on `pointer_up`. The `isDragging` flag becomes true when the pointer moves beyond the drag distance threshold (`dragDistanceSquared` or `coarseDragDistanceSquared` in the editor's [options](/sdk-features/options)) while pointing.
## Event processing flow
When an input event occurs, the editor processes it through these stages:
1. The browser fires a native DOM event
2. The canvas event handlers transform it into a typed event info object and call [Editor#dispatch](?)
3. `pointer_move`, `wheel`, and `pinch` events are queued and flushed once per frame; other events flush immediately
4. The editor emits `before-event`, then updates modifier key state
5. For pointer, pinch, and wheel events, the editor calls `updateFromEvent()` on the `InputsManager`
6. For pointer events, the editor updates buttons and interaction flags, then hands the event to the `ClickManager` for double-click detection
7. The editor sends the event to the state machine via `root.handleEvent()`, which propagates it through active tool states, then emits `event`
See [Events](/sdk-features/events) for subscribing to `before-event` and `event`.
The typed event info objects are:
| Event type | Info type |
| --------------- | --------------------- |
| Pointer events | `TLPointerEventInfo` |
| Click events | `TLClickEventInfo` |
| Keyboard events | `TLKeyboardEventInfo` |
| Wheel events | `TLWheelEventInfo` |
| Pinch events | `TLPinchEventInfo` |
In collaborative sessions, `updateFromEvent()` also updates the user's pointer presence record in the store, broadcasting pointer position to other users.
## Input normalization
The editor listens to Pointer Events, so mouse, touch, and pen input arrive through the same handlers, and the manager tracks the device type through the `isPen` flag.
Pointer positions include a `z` coordinate carrying the pointer event's pressure; synthetic events without a `z` default to 0.5. The manager subtracts the container's screen bounds from client coordinates, so the editor works correctly in nested layouts and scrolled containers.
## State serialization
The manager provides a `toJson()` method for debugging:
```typescript
const state = editor.inputs.toJson()
// Returns all position vectors, modifier key states, interaction flags,
// device type, and the contents of the keys and buttons sets
```
We use this serialized form when generating crash reports.
## Related examples
- [Reactive inputs](/examples/editor-api/reactive-inputs) - Display pointer positions, velocity, and other input state reactively
- [Canvas events](/examples/events/canvas-events) - Log pointer, keyboard, and wheel events to see the event flow