@tldraw/editor 5.1.0-next.d7c83ba698ae → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist-cjs/index.d.ts +19 -2
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/editor/Editor.js +3 -5
  4. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  5. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +23 -21
  6. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  7. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +27 -8
  8. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  9. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +4 -2
  10. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  11. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +2 -2
  12. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
  13. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +8 -0
  14. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  15. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +7 -1
  16. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  17. package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js +13 -15
  18. package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js.map +2 -2
  19. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +43 -24
  20. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/overlays/OverlayManager.js +5 -0
  22. package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
  23. package/dist-cjs/lib/editor/overlays/OverlayUtil.js +5 -0
  24. package/dist-cjs/lib/editor/overlays/OverlayUtil.js.map +2 -2
  25. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
  26. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  27. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  28. package/dist-cjs/lib/options.js +1 -0
  29. package/dist-cjs/lib/options.js.map +2 -2
  30. package/dist-cjs/lib/utils/dom.js +1 -0
  31. package/dist-cjs/lib/utils/dom.js.map +2 -2
  32. package/dist-cjs/version.js +3 -3
  33. package/dist-cjs/version.js.map +1 -1
  34. package/dist-esm/index.d.mts +19 -2
  35. package/dist-esm/index.mjs +1 -1
  36. package/dist-esm/lib/editor/Editor.mjs +3 -5
  37. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  38. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +23 -21
  39. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  40. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +27 -11
  41. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  42. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +4 -2
  43. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  44. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +2 -2
  45. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
  46. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +8 -0
  47. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  48. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +7 -1
  49. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  50. package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs +13 -15
  51. package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs.map +2 -2
  52. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +43 -24
  53. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +2 -2
  54. package/dist-esm/lib/editor/overlays/OverlayManager.mjs +5 -0
  55. package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
  56. package/dist-esm/lib/editor/overlays/OverlayUtil.mjs +5 -0
  57. package/dist-esm/lib/editor/overlays/OverlayUtil.mjs.map +2 -2
  58. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
  59. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  60. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  61. package/dist-esm/lib/options.mjs +1 -0
  62. package/dist-esm/lib/options.mjs.map +2 -2
  63. package/dist-esm/lib/utils/dom.mjs +1 -0
  64. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  65. package/dist-esm/version.mjs +3 -3
  66. package/dist-esm/version.mjs.map +1 -1
  67. package/package.json +7 -7
  68. package/src/lib/editor/Editor.ts +3 -5
  69. package/src/lib/editor/derivations/notVisibleShapes.ts +34 -26
  70. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +132 -0
  71. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +40 -12
  72. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +12 -2
  73. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +7 -0
  74. package/src/lib/editor/managers/FocusManager/FocusManager.ts +2 -2
  75. package/src/lib/editor/managers/FontManager/FontManager.test.ts +33 -2
  76. package/src/lib/editor/managers/FontManager/FontManager.ts +20 -2
  77. package/src/lib/editor/managers/InputsManager/InputsManager.ts +11 -2
  78. package/src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts +13 -14
  79. package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +67 -40
  80. package/src/lib/editor/overlays/OverlayManager.ts +6 -0
  81. package/src/lib/editor/overlays/OverlayUtil.ts +5 -0
  82. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
  83. package/src/lib/hooks/usePeerIds.ts +1 -0
  84. package/src/lib/options.ts +8 -0
  85. package/src/lib/utils/dom.ts +1 -0
  86. package/src/version.ts +3 -3
  87. package/dist-cjs/lib/utils/collaboratorState.js +0 -42
  88. package/dist-cjs/lib/utils/collaboratorState.js.map +0 -7
  89. package/dist-esm/lib/utils/collaboratorState.mjs +0 -22
  90. package/dist-esm/lib/utils/collaboratorState.mjs.map +0 -7
  91. package/src/lib/utils/collaboratorState.ts +0 -54
@@ -28,51 +28,56 @@ export class SpatialIndexManager {
28
28
  private spatialIndexComputed: Computed<number>
29
29
  private lastPageId: TLPageId | null = null
30
30
 
31
+ // Bumps only when the rbush content may have changed. Consumers subscribe
32
+ // via the computed; a stable epoch lets prop-only diffs skip downstream
33
+ // invalidations.
34
+ private _boundsEpoch = 0
35
+
31
36
  constructor(public readonly editor: Editor) {
32
37
  this.rbush = new RBushIndex()
33
38
  this.spatialIndexComputed = this.createSpatialIndexComputed()
34
39
  }
35
40
 
41
+ private rebuildAndBumpEpoch(): number {
42
+ this.buildFromScratch()
43
+ this._boundsEpoch++
44
+ return this._boundsEpoch
45
+ }
46
+
36
47
  private createSpatialIndexComputed() {
37
48
  const shapeHistory = this.editor.store.query.filterHistory('shape')
38
49
 
39
50
  return computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {
40
51
  if (isUninitialized(_prevValue)) {
41
- return this.buildFromScratch(lastComputedEpoch)
52
+ return this.rebuildAndBumpEpoch()
42
53
  }
43
54
 
44
55
  const shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)
45
56
 
46
57
  if (shapeDiff === RESET_VALUE) {
47
- return this.buildFromScratch(lastComputedEpoch)
58
+ return this.rebuildAndBumpEpoch()
48
59
  }
49
60
 
50
61
  const currentPageId = this.editor.getCurrentPageId()
51
62
  if (this.lastPageId !== currentPageId) {
52
- return this.buildFromScratch(lastComputedEpoch)
63
+ return this.rebuildAndBumpEpoch()
53
64
  }
54
65
 
55
- // No shape changes - index is already up to date
56
- if (shapeDiff.length === 0) {
57
- return lastComputedEpoch
58
- }
59
-
60
- // Process incremental updates
61
- this.processIncrementalUpdate(shapeDiff)
66
+ if (shapeDiff.length === 0) return this._boundsEpoch
62
67
 
63
- return lastComputedEpoch
68
+ if (this.processIncrementalUpdate(shapeDiff)) {
69
+ this._boundsEpoch++
70
+ }
71
+ return this._boundsEpoch
64
72
  })
65
73
  }
66
74
 
67
- private buildFromScratch(epoch: number): number {
75
+ private buildFromScratch(): void {
68
76
  this.rbush.clear()
69
77
  this.lastPageId = this.editor.getCurrentPageId()
70
78
 
71
- const shapes = this.editor.getCurrentPageShapes()
72
79
  const elements: SpatialElement[] = []
73
-
74
- // Collect all shape elements for bulk loading
75
- for (const shape of shapes) {
80
+ for (const shape of this.editor.getCurrentPageShapes()) {
76
81
  const bounds = this.editor.getShapePageBounds(shape.id)
77
82
  if (bounds && bounds.isValid()) {
78
83
  elements.push({
@@ -87,36 +92,38 @@ export class SpatialIndexManager {
87
92
 
88
93
  // Bulk load for efficiency
89
94
  this.rbush.bulkLoad(elements)
90
-
91
- return epoch
92
95
  }
93
96
 
94
- private processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): void {
95
- // Track shapes we've already processed from the diff
97
+ private processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): boolean {
96
98
  const processedShapeIds = new Set<TLShapeId>()
99
+ let changed = false
97
100
 
98
- // 1. Process shape additions, removals, and updates from diff
101
+ // Step 1: apply diff entries directly. `changed` flips only on real
102
+ // rbush mutations, so prop-only updates and no-op removes (e.g. shapes
103
+ // from other pages, or never-indexed shapes with invalid bounds) don't
104
+ // bump the epoch.
99
105
  for (const changes of shapeDiff) {
100
- // Handle additions (only for shapes on current page)
101
106
  for (const shape of objectMapValues(changes.added) as TLShape[]) {
102
107
  if (isShape(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {
103
108
  const bounds = this.editor.getShapePageBounds(shape.id)
104
109
  if (bounds && bounds.isValid()) {
105
110
  this.rbush.upsert(shape.id, bounds)
111
+ changed = true
106
112
  }
107
113
  processedShapeIds.add(shape.id)
108
114
  }
109
115
  }
110
116
 
111
- // Handle removals
112
117
  for (const shape of objectMapValues(changes.removed) as TLShape[]) {
113
118
  if (isShape(shape)) {
114
- this.rbush.remove(shape.id)
119
+ if (this.rbush.has(shape.id)) {
120
+ this.rbush.remove(shape.id)
121
+ changed = true
122
+ }
115
123
  processedShapeIds.add(shape.id)
116
124
  }
117
125
  }
118
126
 
119
- // Handle updated shapes: page changes and bounds updates
120
127
  for (const [, to] of objectMapValues(changes.updated) as [TLShape, TLShape][]) {
121
128
  if (!isShape(to)) continue
122
129
  processedShapeIds.add(to.id)
@@ -126,35 +133,55 @@ export class SpatialIndexManager {
126
133
  if (isOnPage) {
127
134
  const bounds = this.editor.getShapePageBounds(to.id)
128
135
  if (bounds && bounds.isValid()) {
129
- this.rbush.upsert(to.id, bounds)
136
+ const indexedElement = this.rbush.getElement(to.id)
137
+ if (!this.areBoundsEqualToSpatialElement(bounds, indexedElement)) {
138
+ this.rbush.upsert(to.id, bounds)
139
+ changed = true
140
+ }
141
+ } else if (this.rbush.has(to.id)) {
142
+ this.rbush.remove(to.id)
143
+ changed = true
130
144
  }
131
- } else {
145
+ } else if (this.rbush.has(to.id)) {
132
146
  this.rbush.remove(to.id)
147
+ changed = true
133
148
  }
134
149
  }
135
150
  }
136
151
 
137
- // 2. Check remaining shapes in index for bounds changes
138
- // This handles shapes with computed bounds (arrows bound to moved shapes, groups with moved children, etc.)
139
- const allShapeIds = this.rbush.getAllShapeIds()
140
-
141
- for (const shapeId of allShapeIds) {
152
+ // Step 2: must always run. Diff entries can dirty derived bounds
153
+ // arrows bound to moved shapes, groups with moved children — without
154
+ // touching any record visited in step 1. Also catches outline-only
155
+ // changes (e.g. geo rectangle→ellipse at the same w/h) that shift a
156
+ // bound arrow's intersection points: step 1 sees the geo's
157
+ // axis-aligned bounds unchanged and skips, but the dependent arrow's
158
+ // bounds have moved.
159
+ //
160
+ // Iterating the rbush's element map directly avoids allocating a
161
+ // shape-id array per pointer move. Mutation here is limited to
162
+ // upserts of existing keys and deletions, both safe during Map
163
+ // iteration.
164
+ for (const [shapeId, indexedElement] of this.rbush.entries()) {
142
165
  if (processedShapeIds.has(shapeId)) continue
143
166
 
144
167
  const currentBounds = this.editor.getShapePageBounds(shapeId)
145
- const indexedBounds = this.rbush.getBounds(shapeId)
168
+ if (this.areBoundsEqualToSpatialElement(currentBounds, indexedElement)) continue
146
169
 
147
- if (!this.areBoundsEqual(currentBounds, indexedBounds)) {
148
- if (currentBounds && currentBounds.isValid()) {
149
- this.rbush.upsert(shapeId, currentBounds)
150
- } else {
151
- this.rbush.remove(shapeId)
152
- }
170
+ if (currentBounds && currentBounds.isValid()) {
171
+ this.rbush.upsert(shapeId, currentBounds)
172
+ } else {
173
+ this.rbush.remove(shapeId)
153
174
  }
175
+ changed = true
154
176
  }
177
+
178
+ return changed
155
179
  }
156
180
 
157
- private areBoundsEqual(a: Box | undefined, b: Box | undefined): boolean {
181
+ private areBoundsEqualToSpatialElement(
182
+ a: Box | undefined,
183
+ b: SpatialElement | undefined
184
+ ): boolean {
158
185
  if (!a && !b) return true
159
186
  if (!a || !b) return false
160
187
  return a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY
@@ -38,6 +38,12 @@ export class OverlayManager {
38
38
  this._overlayUtils.set(type, util)
39
39
  }
40
40
 
41
+ dispose() {
42
+ for (const util of this._overlayUtils.values()) {
43
+ util.dispose()
44
+ }
45
+ }
46
+
41
47
  /**
42
48
  * Get an overlay util by type string, overlay instance, or by passing
43
49
  * a util class as a generic parameter for type-safe lookup.
@@ -140,4 +140,9 @@ export abstract class OverlayUtil<T extends TLOverlay = TLOverlay> {
140
140
  * at minimap scale (e.g. brushes, collaborator cursors) should opt in.
141
141
  */
142
142
  renderMinimap(_ctx: CanvasRenderingContext2D, _overlays: T[], _zoom: number): void {}
143
+
144
+ /**
145
+ * Clean up any resources held by this overlay util.
146
+ */
147
+ dispose(): void {}
143
148
  }
@@ -133,7 +133,7 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
133
133
  if (this.editor.getCurrentPageState().focusedGroupId === group.id) {
134
134
  this.editor.popFocusedGroupId()
135
135
  }
136
- this.editor.reparentShapes(children, group.parentId)
136
+ this.editor.reparentShapes(children, group.parentId, group.index)
137
137
  this.editor.deleteShapes([group.id])
138
138
  return
139
139
  }
@@ -4,6 +4,7 @@ import { useEditor } from './useEditor'
4
4
 
5
5
  /**
6
6
  * Reactive list of peer user IDs for collaborators currently shown in the UI.
7
+ * This list includes user ids who are not on the user's current page.
7
8
  * Mirrors {@link Editor.getVisibleCollaborators} — peers fade out as they
8
9
  * transition to idle/inactive, without requiring a manual re-render.
9
10
  *
@@ -171,6 +171,13 @@ export interface TldrawOptions {
171
171
  * The distance (in screen pixels) at which shapes snap to guides and other shapes.
172
172
  */
173
173
  readonly snapThreshold: number
174
+ /**
175
+ * Whether locked shapes can be selected with a left click. When false (default), left-clicking
176
+ * a locked shape is treated as a click on the canvas — only right-click selects it. When true,
177
+ * locked shapes can be selected via left click and included in brush and scribble selections,
178
+ * but the editor's lock guards still prevent moving, resizing, editing, or deleting them.
179
+ */
180
+ readonly selectLockedShapes: boolean
174
181
  /**
175
182
  * Options for the editor's camera. These are the initial camera options.
176
183
  * Use {@link Editor.setCameraOptions} to update camera options at runtime.
@@ -335,6 +342,7 @@ export const defaultTldrawOptions = {
335
342
  rightClickPanning: true,
336
343
  zoomToFitPadding: 128,
337
344
  snapThreshold: 8,
345
+ selectLockedShapes: false,
338
346
  camera: DEFAULT_CAMERA_OPTIONS,
339
347
  text: {},
340
348
  deepLinks: undefined,
@@ -35,6 +35,7 @@ export function loopToHtmlElement(elm: Element): HTMLElement {
35
35
  * @public
36
36
  */
37
37
  export function preventDefault(event: React.BaseSyntheticEvent | Event) {
38
+ if ('cancelable' in event && !event.cancelable) return
38
39
  event.preventDefault()
39
40
  if (debugFlags.logPreventDefaults.get()) {
40
41
  console.warn('preventDefault called on event:', event)
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '5.1.0-next.d7c83ba698ae'
4
+ export const version = '5.1.0'
5
5
  export const publishDates = {
6
6
  major: '2026-05-06T16:28:18.473Z',
7
- minor: '2026-05-13T18:49:11.769Z',
8
- patch: '2026-05-13T18:49:11.769Z',
7
+ minor: '2026-06-03T10:26:13.606Z',
8
+ patch: '2026-06-03T10:26:13.606Z',
9
9
  }
@@ -1,42 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var collaboratorState_exports = {};
20
- __export(collaboratorState_exports, {
21
- getCollaboratorStateFromElapsedTime: () => getCollaboratorStateFromElapsedTime,
22
- shouldShowCollaborator: () => shouldShowCollaborator
23
- });
24
- module.exports = __toCommonJS(collaboratorState_exports);
25
- function getCollaboratorStateFromElapsedTime(editor, elapsed) {
26
- return elapsed > editor.options.collaboratorInactiveTimeoutMs ? "inactive" : elapsed > editor.options.collaboratorIdleTimeoutMs ? "idle" : "active";
27
- }
28
- function shouldShowCollaborator(editor, presence, state) {
29
- const { followingUserId, highlightedUserIds } = editor.getInstanceState();
30
- switch (state) {
31
- case "inactive":
32
- return followingUserId === presence.userId || highlightedUserIds.includes(presence.userId);
33
- case "idle":
34
- if (presence.followingUserId === editor.user.getId()) {
35
- return !!(presence.chatMessage || highlightedUserIds.includes(presence.userId));
36
- }
37
- return true;
38
- case "active":
39
- return true;
40
- }
41
- }
42
- //# sourceMappingURL=collaboratorState.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/utils/collaboratorState.ts"],
4
- "sourcesContent": ["import { TLInstancePresence } from '@tldraw/tlschema'\nimport { Editor } from '../editor/Editor'\n\n/** The activity state of a collaborator */\nexport type CollaboratorState = 'active' | 'idle' | 'inactive'\n\n/**\n * Get the activity state of a collaborator based on elapsed time since their last activity.\n *\n * @param editor - The editor instance\n * @param elapsed - Time in milliseconds since the collaborator's last activity\n * @returns The collaborator's activity state\n */\nexport function getCollaboratorStateFromElapsedTime(\n\teditor: Editor,\n\telapsed: number\n): CollaboratorState {\n\treturn elapsed > editor.options.collaboratorInactiveTimeoutMs\n\t\t? 'inactive'\n\t\t: elapsed > editor.options.collaboratorIdleTimeoutMs\n\t\t\t? 'idle'\n\t\t\t: 'active'\n}\n\n/**\n * Determine whether a collaborator should be shown based on their activity state\n * and the current instance state (following, highlighted users, etc.).\n *\n * @param editor - The editor instance\n * @param presence - The collaborator's presence data\n * @param state - The collaborator's activity state\n * @returns Whether the collaborator should be shown\n */\nexport function shouldShowCollaborator(\n\teditor: Editor,\n\tpresence: TLInstancePresence,\n\tstate: CollaboratorState\n): boolean {\n\tconst { followingUserId, highlightedUserIds } = editor.getInstanceState()\n\n\tswitch (state) {\n\t\tcase 'inactive':\n\t\t\t// If they're inactive, only show if we're following them or they're highlighted\n\t\t\treturn followingUserId === presence.userId || highlightedUserIds.includes(presence.userId)\n\t\tcase 'idle':\n\t\t\t// If they're idle and following us, hide them unless they have a chat message or are highlighted\n\t\t\tif (presence.followingUserId === editor.user.getId()) {\n\t\t\t\treturn !!(presence.chatMessage || highlightedUserIds.includes(presence.userId))\n\t\t\t}\n\t\t\treturn true\n\t\tcase 'active':\n\t\t\treturn true\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaO,SAAS,oCACf,QACA,SACoB;AACpB,SAAO,UAAU,OAAO,QAAQ,gCAC7B,aACA,UAAU,OAAO,QAAQ,4BACxB,SACA;AACL;AAWO,SAAS,uBACf,QACA,UACA,OACU;AACV,QAAM,EAAE,iBAAiB,mBAAmB,IAAI,OAAO,iBAAiB;AAExE,UAAQ,OAAO;AAAA,IACd,KAAK;AAEJ,aAAO,oBAAoB,SAAS,UAAU,mBAAmB,SAAS,SAAS,MAAM;AAAA,IAC1F,KAAK;AAEJ,UAAI,SAAS,oBAAoB,OAAO,KAAK,MAAM,GAAG;AACrD,eAAO,CAAC,EAAE,SAAS,eAAe,mBAAmB,SAAS,SAAS,MAAM;AAAA,MAC9E;AACA,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,EACT;AACD;",
6
- "names": []
7
- }
@@ -1,22 +0,0 @@
1
- function getCollaboratorStateFromElapsedTime(editor, elapsed) {
2
- return elapsed > editor.options.collaboratorInactiveTimeoutMs ? "inactive" : elapsed > editor.options.collaboratorIdleTimeoutMs ? "idle" : "active";
3
- }
4
- function shouldShowCollaborator(editor, presence, state) {
5
- const { followingUserId, highlightedUserIds } = editor.getInstanceState();
6
- switch (state) {
7
- case "inactive":
8
- return followingUserId === presence.userId || highlightedUserIds.includes(presence.userId);
9
- case "idle":
10
- if (presence.followingUserId === editor.user.getId()) {
11
- return !!(presence.chatMessage || highlightedUserIds.includes(presence.userId));
12
- }
13
- return true;
14
- case "active":
15
- return true;
16
- }
17
- }
18
- export {
19
- getCollaboratorStateFromElapsedTime,
20
- shouldShowCollaborator
21
- };
22
- //# sourceMappingURL=collaboratorState.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/utils/collaboratorState.ts"],
4
- "sourcesContent": ["import { TLInstancePresence } from '@tldraw/tlschema'\nimport { Editor } from '../editor/Editor'\n\n/** The activity state of a collaborator */\nexport type CollaboratorState = 'active' | 'idle' | 'inactive'\n\n/**\n * Get the activity state of a collaborator based on elapsed time since their last activity.\n *\n * @param editor - The editor instance\n * @param elapsed - Time in milliseconds since the collaborator's last activity\n * @returns The collaborator's activity state\n */\nexport function getCollaboratorStateFromElapsedTime(\n\teditor: Editor,\n\telapsed: number\n): CollaboratorState {\n\treturn elapsed > editor.options.collaboratorInactiveTimeoutMs\n\t\t? 'inactive'\n\t\t: elapsed > editor.options.collaboratorIdleTimeoutMs\n\t\t\t? 'idle'\n\t\t\t: 'active'\n}\n\n/**\n * Determine whether a collaborator should be shown based on their activity state\n * and the current instance state (following, highlighted users, etc.).\n *\n * @param editor - The editor instance\n * @param presence - The collaborator's presence data\n * @param state - The collaborator's activity state\n * @returns Whether the collaborator should be shown\n */\nexport function shouldShowCollaborator(\n\teditor: Editor,\n\tpresence: TLInstancePresence,\n\tstate: CollaboratorState\n): boolean {\n\tconst { followingUserId, highlightedUserIds } = editor.getInstanceState()\n\n\tswitch (state) {\n\t\tcase 'inactive':\n\t\t\t// If they're inactive, only show if we're following them or they're highlighted\n\t\t\treturn followingUserId === presence.userId || highlightedUserIds.includes(presence.userId)\n\t\tcase 'idle':\n\t\t\t// If they're idle and following us, hide them unless they have a chat message or are highlighted\n\t\t\tif (presence.followingUserId === editor.user.getId()) {\n\t\t\t\treturn !!(presence.chatMessage || highlightedUserIds.includes(presence.userId))\n\t\t\t}\n\t\t\treturn true\n\t\tcase 'active':\n\t\t\treturn true\n\t}\n}\n"],
5
- "mappings": "AAaO,SAAS,oCACf,QACA,SACoB;AACpB,SAAO,UAAU,OAAO,QAAQ,gCAC7B,aACA,UAAU,OAAO,QAAQ,4BACxB,SACA;AACL;AAWO,SAAS,uBACf,QACA,UACA,OACU;AACV,QAAM,EAAE,iBAAiB,mBAAmB,IAAI,OAAO,iBAAiB;AAExE,UAAQ,OAAO;AAAA,IACd,KAAK;AAEJ,aAAO,oBAAoB,SAAS,UAAU,mBAAmB,SAAS,SAAS,MAAM;AAAA,IAC1F,KAAK;AAEJ,UAAI,SAAS,oBAAoB,OAAO,KAAK,MAAM,GAAG;AACrD,eAAO,CAAC,EAAE,SAAS,eAAe,mBAAmB,SAAS,SAAS,MAAM;AAAA,MAC9E;AACA,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,EACT;AACD;",
6
- "names": []
7
- }
@@ -1,54 +0,0 @@
1
- import { TLInstancePresence } from '@tldraw/tlschema'
2
- import { Editor } from '../editor/Editor'
3
-
4
- /** The activity state of a collaborator */
5
- export type CollaboratorState = 'active' | 'idle' | 'inactive'
6
-
7
- /**
8
- * Get the activity state of a collaborator based on elapsed time since their last activity.
9
- *
10
- * @param editor - The editor instance
11
- * @param elapsed - Time in milliseconds since the collaborator's last activity
12
- * @returns The collaborator's activity state
13
- */
14
- export function getCollaboratorStateFromElapsedTime(
15
- editor: Editor,
16
- elapsed: number
17
- ): CollaboratorState {
18
- return elapsed > editor.options.collaboratorInactiveTimeoutMs
19
- ? 'inactive'
20
- : elapsed > editor.options.collaboratorIdleTimeoutMs
21
- ? 'idle'
22
- : 'active'
23
- }
24
-
25
- /**
26
- * Determine whether a collaborator should be shown based on their activity state
27
- * and the current instance state (following, highlighted users, etc.).
28
- *
29
- * @param editor - The editor instance
30
- * @param presence - The collaborator's presence data
31
- * @param state - The collaborator's activity state
32
- * @returns Whether the collaborator should be shown
33
- */
34
- export function shouldShowCollaborator(
35
- editor: Editor,
36
- presence: TLInstancePresence,
37
- state: CollaboratorState
38
- ): boolean {
39
- const { followingUserId, highlightedUserIds } = editor.getInstanceState()
40
-
41
- switch (state) {
42
- case 'inactive':
43
- // If they're inactive, only show if we're following them or they're highlighted
44
- return followingUserId === presence.userId || highlightedUserIds.includes(presence.userId)
45
- case 'idle':
46
- // If they're idle and following us, hide them unless they have a chat message or are highlighted
47
- if (presence.followingUserId === editor.user.getId()) {
48
- return !!(presence.chatMessage || highlightedUserIds.includes(presence.userId))
49
- }
50
- return true
51
- case 'active':
52
- return true
53
- }
54
- }