Skip to content

Commit 5c0db60

Browse files
fix(editor): reset camera state on dispose to prevent stuck 'moving' state (tldraw#8396)
Fixes tldraw#8386 When using deep links with React strict mode (or any scenario where the editor is disposed mid-camera-transition), the camera state can get permanently stuck at `'moving'`. This keeps the `tl-hit-test-blocker` div covering the entire canvas with `pointer-events: all`, blocking all shape interactions (resize, rotate, select, etc.). **Root cause:** When the editor is disposed, `TickManager.dispose()` stops the tick loop, but the camera decay callback (subscribed to the `'tick'` event) never fires, leaving `cameraState` stuck at `'moving'` in the shared store. The next editor instance inherits this stale state. **Fix:** Add a disposable that resets `cameraState` to `'idle'` and unsubscribes the decay callback when the editor is torn down. This follows the React cleanup pattern — each editor instance properly cleans up its transient state on dispose. ### Change type - [x] `bugfix` ### Test plan 1. In `apps/examples/src/index.tsx`, set `ENABLE_STRICT_MODE = true` 2. Run `yarn dev`, go to `http://localhost:5420/configuration/deep-links/full` 3. Draw a shape → verify resize/rotate handles work (cursor changes on hover, can drag) 4. Copy a deep link, move camera, paste the deep link URL 5. Verify handles still work after navigation 6. Verify `editor.getCameraState()` returns `'idle'` after navigation completes To reproduce the bug on `main`: 1. Set `ENABLE_STRICT_MODE = true` in `apps/examples/src/index.tsx` 2. Run `yarn dev`, go to `http://localhost:5420/configuration/deep-links/full` 3. Draw a shape → resize/rotate handles will NOT work (cursor doesn't change, can't drag) 4. `editor.getCameraState()` returns `'moving'` and never transitions to `'idle'` - [ ] Unit tests - [ ] End to end tests ### Release notes - Fixed camera state getting stuck at 'moving' when using deep links with React strict mode, which blocked all pointer interactions on the canvas. ### Code changes | Section | LOC change | | ---------- | ---------- | | Core code | +6 / -0 |
1 parent b30af3a commit 5c0db60

1 file changed

Lines changed: 6 additions & 0 deletions

File tree

packages/editor/src/lib/editor/Editor.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ export class Editor extends EventEmitter<TLEventMap> {
361361
this.fonts = new FontManager(this, fontAssetUrls)
362362

363363
this._tickManager = new TickManager(this)
364+
this.disposables.add(() => {
365+
// Reset camera state to 'idle' so the store isn't left stuck at 'moving'
366+
// when tick events stop (e.g. React strict mode disposes while camera is moving)
367+
this.off('tick', this._decayCameraStateTimeout)
368+
this._setCameraState('idle')
369+
})
364370

365371
this.inputs = new InputsManager(this)
366372

0 commit comments

Comments
 (0)