Commit 84b3964
refactor(editor): add CollaboratorsManager; split collaborator indicators (tldraw#8648)
In order to clean up a few rough edges from the OverlayUtil system
landed in tldraw#8633, this PR (a) encapsulates collaborator state in a new
manager, (b) gets remote-collaborator rendering out of the
local-indicator overlay util, and (c) makes `setCursor` cheap to call
from pointer-move hot paths.
### Concepts
| Term | Type | Meaning |
| ---- | ---- | ------- |
| `CollaboratorsManager` | class | Lives on `editor.collaborators`; owns
the visibility clock and exposes `getCollaborators*` queries |
| `CollaboratorShapeIndicatorOverlayUtil` | class | tldraw-package
overlay util that draws remote collaborators' selection indicators
(split out of `ShapeIndicatorOverlayUtil`) |
| `strokeShapeIndicators` | function | Shared canvas helper exported
from `@tldraw/editor`, used by both local and collaborator indicator
overlays |
### What changed
**`CollaboratorsManager`.** The visibility clock atom and the
collaborator-presence queries used to live directly on `Editor`. They
now live on a small dedicated manager, in the same style as
`ClickManager` / `EdgeScrollManager` / `FocusManager` etc. The
previously `@internal` `Editor._collaboratorVisibilityClock` is now a
private field on the manager. The four `Editor.getCollaborators*()`
methods stay as thin convenience wrappers that delegate to
`editor.collaborators.*` so existing callers don't break.
**Collaborator indicators split out.** `ShapeIndicatorOverlayUtil` (in
`@tldraw/editor`) used to also render remote collaborators' selected
shapes via a third `collaboratorIndicators` prop on its overlay. That
mixed two concerns: a generic editor-package util shouldn't know about
presence records. Remote-indicator rendering is now its own
`CollaboratorShapeIndicatorOverlayUtil` in `@tldraw/tldraw`, registered
ahead of the local indicator util in `defaultOverlayUtils` and given a
lower `zIndex` so local selection always paints on top. To avoid
duplicating the canvas drawing code, the stroke logic was extracted into
a public `strokeShapeIndicators(editor, ctx, shapeIds)` helper.
**`setCursor` equality guard.** `updateHoveredOverlayId` (and various
tool states) call `editor.setCursor(...)` from pointer-move handlers.
Without a guard, every pointer move builds a fresh cursor object, enters
a history batch, spreads instance state, and walks the validator before
the store deduplicates the no-op write. The guard short-circuits when
the requested `type`/`rotation` already match `instanceState.cursor`,
skipping all of that on the hot path. The store-level
`validateUsingKnownGoodVersion` dedup is still the source of truth —
this is purely a perf shortcut.
### Editor API
| Method / property | Description |
| ----------------- | ----------- |
| `editor.collaborators` | The `CollaboratorsManager` instance |
| `editor.collaborators.getCollaborators()` /
`getCollaboratorsOnCurrentPage()` / `getVisibleCollaborators()` /
`getVisibleCollaboratorsOnCurrentPage()` | Same shape as the existing
`Editor.getCollaborators*()` methods (which now delegate here) |
| `strokeShapeIndicators(editor, ctx, shapeIds)` | Strokes the indicator
path for each shape into the given 2D context. Uses the current stroke
style — set color/`lineWidth`/`globalAlpha` on the context first |
| `CollaboratorShapeIndicatorOverlayUtil` | New default overlay util in
`@tldraw/tldraw`. Subclass to customize remote-indicator rendering
independently of local selection |
### Change type
- [x] `api`
### Test plan
1. Run `yarn dev` and confirm local selection / hover / hint indicators
still render identically to before.
2. Open a multiplayer room (e.g. `yarn dev-app`); remote collaborators'
selections should render below your own selection if you both select the
same shape.
3. Move the pointer continuously over and off overlays — cursor should
still update correctly, with no visible regression in responsiveness.
4. Confirm `editor.collaborators.getVisibleCollaborators()` and
`editor.getVisibleCollaborators()` return identical results.
- [ ] Unit tests
- [ ] End to end tests
### Release notes
- Add `editor.collaborators` (`CollaboratorsManager`) owning the
collaborator visibility clock and presence queries.
`Editor.getCollaborators()` / `getCollaboratorsOnCurrentPage()` /
`getVisibleCollaborators()` / `getVisibleCollaboratorsOnCurrentPage()`
are unchanged and now delegate to it.
- Add `CollaboratorShapeIndicatorOverlayUtil` in `@tldraw/tldraw` for
remote-collaborator selection indicators (split out of
`ShapeIndicatorOverlayUtil`); add `strokeShapeIndicators` helper from
`@tldraw/editor` for sharing indicator rendering between overlay utils.
- `Editor.setCursor` now skips redundant writes when the requested
cursor type and rotation already match the current cursor.
### API changes
- Added `CollaboratorsManager` and `Editor.collaborators`.
- Added `strokeShapeIndicators(editor, ctx, shapeIds)`.
- Added `CollaboratorShapeIndicatorOverlayUtil` and
`TLCollaboratorShapeIndicatorOverlay` to `@tldraw/tldraw`; added it to
`defaultOverlayUtils` (registered before `ShapeIndicatorOverlayUtil`).
- Removed the `@internal` `Editor._collaboratorVisibilityClock` atom
(now private to `CollaboratorsManager`).
- Removed the `collaboratorIndicators` prop from
`TLShapeIndicatorOverlay`; remote-collaborator rendering moved to
`CollaboratorShapeIndicatorOverlayUtil`.
### Code changes
| Section | LOC change |
| --------------- | ---------- |
| Core code | +295 / -137 |
| Automated files | +43 / -7 |
Relates to tldraw#8633.
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent f6b4d01 commit 84b3964
9 files changed
Lines changed: 340 additions & 144 deletions
File tree
- packages
- editor
- src
- lib/editor
- managers/CollaboratorsManager
- overlays
- tldraw
- src
- lib
- overlays
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
534 | 534 | | |
535 | 535 | | |
536 | 536 | | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
537 | 546 | | |
538 | 547 | | |
539 | 548 | | |
| |||
911 | 920 | | |
912 | 921 | | |
913 | 922 | | |
914 | | - | |
915 | | - | |
| 923 | + | |
916 | 924 | | |
917 | 925 | | |
918 | 926 | | |
| |||
3261 | 3269 | | |
3262 | 3270 | | |
3263 | 3271 | | |
| 3272 | + | |
| 3273 | + | |
| 3274 | + | |
3264 | 3275 | | |
3265 | 3276 | | |
3266 | 3277 | | |
| |||
4600 | 4611 | | |
4601 | 4612 | | |
4602 | 4613 | | |
4603 | | - | |
4604 | | - | |
4605 | | - | |
4606 | | - | |
4607 | 4614 | | |
4608 | 4615 | | |
4609 | 4616 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
| 139 | + | |
139 | 140 | | |
140 | 141 | | |
141 | 142 | | |
| |||
182 | 183 | | |
183 | 184 | | |
184 | 185 | | |
| 186 | + | |
185 | 187 | | |
186 | 188 | | |
187 | 189 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | | - | |
93 | 92 | | |
94 | 93 | | |
95 | 94 | | |
| |||
132 | 131 | | |
133 | 132 | | |
134 | 133 | | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | 134 | | |
140 | 135 | | |
141 | 136 | | |
| |||
156 | 151 | | |
157 | 152 | | |
158 | 153 | | |
| 154 | + | |
159 | 155 | | |
160 | 156 | | |
161 | 157 | | |
| |||
422 | 418 | | |
423 | 419 | | |
424 | 420 | | |
425 | | - | |
426 | | - | |
427 | | - | |
| 421 | + | |
428 | 422 | | |
429 | 423 | | |
430 | 424 | | |
| |||
1057 | 1051 | | |
1058 | 1052 | | |
1059 | 1053 | | |
1060 | | - | |
1061 | | - | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
1062 | 1060 | | |
1063 | 1061 | | |
1064 | 1062 | | |
| |||
1946 | 1944 | | |
1947 | 1945 | | |
1948 | 1946 | | |
| 1947 | + | |
| 1948 | + | |
| 1949 | + | |
| 1950 | + | |
| 1951 | + | |
1949 | 1952 | | |
1950 | 1953 | | |
1951 | 1954 | | |
1952 | 1955 | | |
1953 | | - | |
| 1956 | + | |
| 1957 | + | |
| 1958 | + | |
| 1959 | + | |
| 1960 | + | |
| 1961 | + | |
| 1962 | + | |
| 1963 | + | |
1954 | 1964 | | |
1955 | 1965 | | |
1956 | 1966 | | |
| |||
4256 | 4266 | | |
4257 | 4267 | | |
4258 | 4268 | | |
4259 | | - | |
4260 | | - | |
4261 | | - | |
4262 | | - | |
4263 | | - | |
4264 | | - | |
4265 | | - | |
4266 | 4269 | | |
4267 | 4270 | | |
4268 | 4271 | | |
4269 | 4272 | | |
| 4273 | + | |
| 4274 | + | |
4270 | 4275 | | |
4271 | 4276 | | |
4272 | | - | |
4273 | 4277 | | |
4274 | | - | |
4275 | | - | |
4276 | | - | |
4277 | | - | |
4278 | | - | |
4279 | | - | |
4280 | | - | |
4281 | | - | |
4282 | | - | |
4283 | | - | |
| 4278 | + | |
4284 | 4279 | | |
4285 | 4280 | | |
4286 | 4281 | | |
4287 | 4282 | | |
4288 | 4283 | | |
4289 | 4284 | | |
| 4285 | + | |
| 4286 | + | |
4290 | 4287 | | |
4291 | 4288 | | |
4292 | | - | |
4293 | 4289 | | |
4294 | | - | |
4295 | | - | |
| 4290 | + | |
4296 | 4291 | | |
4297 | 4292 | | |
4298 | 4293 | | |
| |||
4302 | 4297 | | |
4303 | 4298 | | |
4304 | 4299 | | |
| 4300 | + | |
| 4301 | + | |
4305 | 4302 | | |
4306 | 4303 | | |
4307 | | - | |
4308 | 4304 | | |
4309 | | - | |
4310 | | - | |
4311 | | - | |
4312 | | - | |
4313 | | - | |
4314 | | - | |
4315 | | - | |
| 4305 | + | |
4316 | 4306 | | |
4317 | 4307 | | |
4318 | 4308 | | |
4319 | 4309 | | |
4320 | 4310 | | |
4321 | 4311 | | |
| 4312 | + | |
| 4313 | + | |
4322 | 4314 | | |
4323 | 4315 | | |
4324 | | - | |
4325 | 4316 | | |
4326 | | - | |
4327 | | - | |
| 4317 | + | |
4328 | 4318 | | |
4329 | 4319 | | |
4330 | 4320 | | |
| |||
Lines changed: 98 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
0 commit comments