@tldraw/editor 4.4.0-next.84d68f44c848 → 4.4.0-next.bde73a32273d

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 (126) hide show
  1. package/dist-cjs/index.d.ts +277 -30
  2. package/dist-cjs/index.js +1 -6
  3. package/dist-cjs/index.js.map +3 -3
  4. package/dist-cjs/lib/TldrawEditor.js +20 -8
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/LiveCollaborators.js +14 -24
  7. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  8. package/dist-cjs/lib/components/Shape.js +12 -17
  9. package/dist-cjs/lib/components/Shape.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +226 -0
  11. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +7 -0
  12. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +45 -15
  13. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +3 -1
  15. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js +13 -1
  17. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js.map +2 -2
  18. package/dist-cjs/lib/config/TLUserPreferences.js +9 -3
  19. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  20. package/dist-cjs/lib/config/createTLStore.js.map +1 -1
  21. package/dist-cjs/lib/editor/Editor.js +39 -18
  22. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  23. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +32 -13
  24. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  25. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +2 -3
  26. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +13 -38
  28. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +3 -3
  29. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +378 -89
  30. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
  31. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +8 -3
  32. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  33. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +29 -0
  34. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/editor/tools/RootState.js +0 -13
  36. package/dist-cjs/lib/editor/tools/RootState.js.map +2 -2
  37. package/dist-cjs/lib/hooks/usePeerIds.js +35 -0
  38. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  39. package/dist-cjs/lib/hooks/useShapeCulling.js +75 -0
  40. package/dist-cjs/lib/hooks/useShapeCulling.js.map +7 -0
  41. package/dist-cjs/lib/license/LicenseManager.js +6 -6
  42. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  43. package/dist-cjs/lib/options.js +7 -1
  44. package/dist-cjs/lib/options.js.map +2 -2
  45. package/dist-cjs/lib/utils/collaboratorState.js +42 -0
  46. package/dist-cjs/lib/utils/collaboratorState.js.map +7 -0
  47. package/dist-cjs/version.js +3 -3
  48. package/dist-cjs/version.js.map +1 -1
  49. package/dist-esm/index.d.mts +277 -30
  50. package/dist-esm/index.mjs +1 -6
  51. package/dist-esm/index.mjs.map +2 -2
  52. package/dist-esm/lib/TldrawEditor.mjs +20 -8
  53. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  54. package/dist-esm/lib/components/LiveCollaborators.mjs +17 -24
  55. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  56. package/dist-esm/lib/components/Shape.mjs +12 -17
  57. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  58. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +206 -0
  59. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +7 -0
  60. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +45 -15
  61. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  62. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +3 -1
  63. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  64. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs +13 -1
  65. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs.map +2 -2
  66. package/dist-esm/lib/config/TLUserPreferences.mjs +9 -3
  67. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  68. package/dist-esm/lib/config/createTLStore.mjs.map +1 -1
  69. package/dist-esm/lib/editor/Editor.mjs +39 -18
  70. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  71. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +32 -13
  72. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  73. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +2 -3
  74. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  75. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +14 -39
  76. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
  77. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +378 -89
  78. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
  79. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +8 -3
  80. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  81. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +29 -0
  82. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  83. package/dist-esm/lib/editor/tools/RootState.mjs +0 -13
  84. package/dist-esm/lib/editor/tools/RootState.mjs.map +2 -2
  85. package/dist-esm/lib/hooks/usePeerIds.mjs +39 -1
  86. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  87. package/dist-esm/lib/hooks/useShapeCulling.mjs +55 -0
  88. package/dist-esm/lib/hooks/useShapeCulling.mjs.map +7 -0
  89. package/dist-esm/lib/license/LicenseManager.mjs +6 -6
  90. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  91. package/dist-esm/lib/options.mjs +7 -1
  92. package/dist-esm/lib/options.mjs.map +2 -2
  93. package/dist-esm/lib/utils/collaboratorState.mjs +22 -0
  94. package/dist-esm/lib/utils/collaboratorState.mjs.map +7 -0
  95. package/dist-esm/version.mjs +3 -3
  96. package/dist-esm/version.mjs.map +1 -1
  97. package/editor.css +28 -11
  98. package/package.json +8 -11
  99. package/src/index.ts +2 -5
  100. package/src/lib/TldrawEditor.tsx +35 -13
  101. package/src/lib/components/LiveCollaborators.tsx +26 -37
  102. package/src/lib/components/Shape.tsx +15 -16
  103. package/src/lib/components/default-components/CanvasShapeIndicators.tsx +288 -0
  104. package/src/lib/components/default-components/DefaultCanvas.tsx +39 -3
  105. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +6 -1
  106. package/src/lib/components/default-components/DefaultShapeIndicators.tsx +16 -1
  107. package/src/lib/config/TLUserPreferences.test.ts +1 -0
  108. package/src/lib/config/TLUserPreferences.ts +8 -0
  109. package/src/lib/config/createTLStore.ts +1 -1
  110. package/src/lib/editor/Editor.ts +72 -20
  111. package/src/lib/editor/derivations/notVisibleShapes.ts +39 -17
  112. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +0 -35
  113. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +4 -8
  114. package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +19 -47
  115. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +491 -106
  116. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +24 -0
  117. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +8 -0
  118. package/src/lib/editor/shapes/ShapeUtil.ts +63 -5
  119. package/src/lib/editor/tools/RootState.ts +0 -16
  120. package/src/lib/hooks/usePeerIds.ts +53 -1
  121. package/src/lib/hooks/useShapeCulling.tsx +98 -0
  122. package/src/lib/license/LicenseManager.ts +6 -6
  123. package/src/lib/options.ts +48 -2
  124. package/src/lib/utils/collaboratorState.ts +54 -0
  125. package/src/version.ts +3 -3
  126. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -621
@@ -4,13 +4,53 @@ class ScribbleManager {
4
4
  constructor(editor) {
5
5
  this.editor = editor;
6
6
  }
7
- scribbleItems = /* @__PURE__ */ new Map();
8
- state = "paused";
9
- addScribble(scribble, id = uniqueId()) {
10
- const item = {
7
+ sessions = /* @__PURE__ */ new Map();
8
+ // ==================== SESSION API ====================
9
+ /**
10
+ * Start a new session for grouping scribbles.
11
+ * Returns a session ID that can be used with other session methods.
12
+ *
13
+ * @param options - Session configuration
14
+ * @returns Session ID
15
+ * @public
16
+ */
17
+ startSession(options = {}) {
18
+ const id = options.id ?? uniqueId();
19
+ const session = {
11
20
  id,
21
+ items: [],
22
+ state: "active",
23
+ options: {
24
+ selfConsume: options.selfConsume ?? true,
25
+ idleTimeoutMs: options.idleTimeoutMs ?? 0,
26
+ fadeMode: options.fadeMode ?? "individual",
27
+ fadeEasing: options.fadeEasing ?? (options.fadeMode === "grouped" ? "ease-in" : "linear"),
28
+ fadeDurationMs: options.fadeDurationMs ?? this.editor.options.laserFadeoutMs
29
+ },
30
+ fadeElapsed: 0,
31
+ totalPointsAtFadeStart: 0
32
+ };
33
+ this.sessions.set(id, session);
34
+ if (session.options.idleTimeoutMs > 0) {
35
+ this.resetIdleTimeout(session);
36
+ }
37
+ return id;
38
+ }
39
+ /**
40
+ * Add a scribble to a session.
41
+ *
42
+ * @param sessionId - The session ID
43
+ * @param scribble - Partial scribble properties
44
+ * @param scribbleId - Optional scribble ID
45
+ * @public
46
+ */
47
+ addScribbleToSession(sessionId, scribble, scribbleId = uniqueId()) {
48
+ const session = this.sessions.get(sessionId);
49
+ if (!session) throw Error(`Session ${sessionId} not found`);
50
+ const item = {
51
+ id: scribbleId,
12
52
  scribble: {
13
- id,
53
+ id: scribbleId,
14
54
  size: 20,
15
55
  color: "accent",
16
56
  opacity: 0.8,
@@ -26,43 +66,187 @@ class ScribbleManager {
26
66
  prev: null,
27
67
  next: null
28
68
  };
29
- this.scribbleItems.set(id, item);
69
+ session.items.push(item);
70
+ if (session.options.idleTimeoutMs > 0) {
71
+ this.resetIdleTimeout(session);
72
+ }
30
73
  return item;
31
74
  }
32
- reset() {
33
- this.editor.updateInstanceState({ scribbles: [] });
34
- this.scribbleItems.clear();
35
- }
36
75
  /**
37
- * Start stopping the scribble. The scribble won't be removed until its last point is cleared.
76
+ * Add a point to a scribble in a session.
38
77
  *
78
+ * @param sessionId - The session ID
79
+ * @param scribbleId - The scribble ID
80
+ * @param x - X coordinate
81
+ * @param y - Y coordinate
82
+ * @param z - Z coordinate (pressure)
39
83
  * @public
40
84
  */
41
- stop(id) {
42
- const item = this.scribbleItems.get(id);
43
- if (!item) throw Error(`Scribble with id ${id} not found`);
44
- item.delayRemaining = Math.min(item.delayRemaining, 200);
45
- item.scribble.state = "stopping";
85
+ addPointToSession(sessionId, scribbleId, x, y, z = 0.5) {
86
+ const session = this.sessions.get(sessionId);
87
+ if (!session) throw Error(`Session ${sessionId} not found`);
88
+ const item = session.items.find((i) => i.id === scribbleId);
89
+ if (!item) throw Error(`Scribble ${scribbleId} not found in session ${sessionId}`);
90
+ const point = { x, y, z };
91
+ if (!item.prev || Vec.Dist(item.prev, point) >= 1) {
92
+ item.next = point;
93
+ }
94
+ if (session.options.idleTimeoutMs > 0) {
95
+ this.resetIdleTimeout(session);
96
+ }
46
97
  return item;
47
98
  }
48
99
  /**
49
- * Set the scribble's next point.
100
+ * Extend a session, resetting its idle timeout.
50
101
  *
51
- * @param id - The id of the scribble to add a point to.
52
- * @param x - The x coordinate of the point.
53
- * @param y - The y coordinate of the point.
54
- * @param z - The z coordinate of the point.
102
+ * @param sessionId - The session ID
103
+ * @public
104
+ */
105
+ extendSession(sessionId) {
106
+ const session = this.sessions.get(sessionId);
107
+ if (!session) return;
108
+ if (session.options.idleTimeoutMs > 0) {
109
+ this.resetIdleTimeout(session);
110
+ }
111
+ }
112
+ /**
113
+ * Stop a session, triggering fade-out.
114
+ *
115
+ * @param sessionId - The session ID
116
+ * @public
117
+ */
118
+ stopSession(sessionId) {
119
+ const session = this.sessions.get(sessionId);
120
+ if (!session || session.state !== "active") return;
121
+ this.clearIdleTimeout(session);
122
+ session.state = "stopping";
123
+ if (session.options.fadeMode === "grouped") {
124
+ session.totalPointsAtFadeStart = session.items.reduce(
125
+ (sum, item) => sum + item.scribble.points.length,
126
+ 0
127
+ );
128
+ session.fadeElapsed = 0;
129
+ for (const item of session.items) {
130
+ item.scribble.state = "stopping";
131
+ }
132
+ } else {
133
+ for (const item of session.items) {
134
+ item.delayRemaining = Math.min(item.delayRemaining, 200);
135
+ item.scribble.state = "stopping";
136
+ }
137
+ }
138
+ }
139
+ /**
140
+ * Clear all scribbles in a session immediately.
141
+ *
142
+ * @param sessionId - The session ID
143
+ * @public
144
+ */
145
+ clearSession(sessionId) {
146
+ const session = this.sessions.get(sessionId);
147
+ if (!session) return;
148
+ this.clearIdleTimeout(session);
149
+ for (const item of session.items) {
150
+ item.scribble.points.length = 0;
151
+ }
152
+ session.state = "complete";
153
+ }
154
+ /**
155
+ * Check if a session is active.
156
+ *
157
+ * @param sessionId - The session ID
158
+ * @public
159
+ */
160
+ isSessionActive(sessionId) {
161
+ const session = this.sessions.get(sessionId);
162
+ return session?.state === "active";
163
+ }
164
+ // ==================== SIMPLE API (for eraser, select, etc.) ====================
165
+ /**
166
+ * Add a scribble using the default self-consuming behavior.
167
+ * Creates an implicit session for the scribble.
168
+ *
169
+ * @param scribble - Partial scribble properties
170
+ * @param id - Optional scribble id
171
+ * @returns The created scribble item
172
+ * @public
173
+ */
174
+ addScribble(scribble, id = uniqueId()) {
175
+ const sessionId = this.startSession();
176
+ return this.addScribbleToSession(sessionId, scribble, id);
177
+ }
178
+ /**
179
+ * Add a point to a scribble. Searches all sessions.
180
+ *
181
+ * @param id - The scribble id
182
+ * @param x - X coordinate
183
+ * @param y - Y coordinate
184
+ * @param z - Z coordinate (pressure)
55
185
  * @public
56
186
  */
57
187
  addPoint(id, x, y, z = 0.5) {
58
- const item = this.scribbleItems.get(id);
59
- if (!item) throw Error(`Scribble with id ${id} not found`);
60
- const { prev } = item;
61
- const point = { x, y, z };
62
- if (!prev || Vec.Dist(prev, point) >= 1) {
63
- item.next = point;
188
+ for (const session of this.sessions.values()) {
189
+ const item = session.items.find((i) => i.id === id);
190
+ if (item) {
191
+ const point = { x, y, z };
192
+ if (!item.prev || Vec.Dist(item.prev, point) >= 1) {
193
+ item.next = point;
194
+ }
195
+ if (session.options.idleTimeoutMs > 0) {
196
+ this.resetIdleTimeout(session);
197
+ }
198
+ return item;
199
+ }
64
200
  }
65
- return item;
201
+ throw Error(`Scribble with id ${id} not found`);
202
+ }
203
+ /**
204
+ * Mark a scribble as complete (done being drawn but not yet fading).
205
+ * Searches all sessions.
206
+ *
207
+ * @param id - The scribble id
208
+ * @public
209
+ */
210
+ complete(id) {
211
+ for (const session of this.sessions.values()) {
212
+ const item = session.items.find((i) => i.id === id);
213
+ if (item) {
214
+ if (item.scribble.state === "starting" || item.scribble.state === "active") {
215
+ item.scribble.state = "complete";
216
+ }
217
+ return item;
218
+ }
219
+ }
220
+ throw Error(`Scribble with id ${id} not found`);
221
+ }
222
+ /**
223
+ * Stop a scribble. Searches all sessions.
224
+ *
225
+ * @param id - The scribble id
226
+ * @public
227
+ */
228
+ stop(id) {
229
+ for (const session of this.sessions.values()) {
230
+ const item = session.items.find((i) => i.id === id);
231
+ if (item) {
232
+ item.delayRemaining = Math.min(item.delayRemaining, 200);
233
+ item.scribble.state = "stopping";
234
+ return item;
235
+ }
236
+ }
237
+ throw Error(`Scribble with id ${id} not found`);
238
+ }
239
+ /**
240
+ * Stop and remove all sessions.
241
+ *
242
+ * @public
243
+ */
244
+ reset() {
245
+ for (const session of this.sessions.values()) {
246
+ this.clearIdleTimeout(session);
247
+ }
248
+ this.sessions.clear();
249
+ this.editor.updateInstanceState({ scribbles: [] });
66
250
  }
67
251
  /**
68
252
  * Update on each animation frame.
@@ -71,77 +255,182 @@ class ScribbleManager {
71
255
  * @public
72
256
  */
73
257
  tick(elapsed) {
74
- if (this.scribbleItems.size === 0) return;
258
+ const currentScribbles = this.editor.getInstanceState().scribbles;
259
+ if (this.sessions.size === 0 && currentScribbles.length === 0) return;
75
260
  this.editor.run(() => {
76
- this.scribbleItems.forEach((item) => {
77
- if (item.scribble.state === "starting") {
78
- const { next: next2, prev: prev2 } = item;
79
- if (next2 && next2 !== prev2) {
80
- item.prev = next2;
81
- item.scribble.points.push(next2);
82
- }
83
- if (item.scribble.points.length > 8) {
84
- item.scribble.state = "active";
85
- }
86
- return;
261
+ for (const session of this.sessions.values()) {
262
+ this.tickSession(session, elapsed);
263
+ }
264
+ for (const [id, session] of this.sessions) {
265
+ if (session.state === "complete") {
266
+ this.clearIdleTimeout(session);
267
+ this.sessions.delete(id);
87
268
  }
88
- if (item.delayRemaining > 0) {
89
- item.delayRemaining = Math.max(0, item.delayRemaining - elapsed);
269
+ }
270
+ const scribbles = [];
271
+ for (const session of this.sessions.values()) {
272
+ for (const item of session.items) {
273
+ if (item.scribble.points.length > 0) {
274
+ scribbles.push({
275
+ ...item.scribble,
276
+ points: [...item.scribble.points]
277
+ });
278
+ }
90
279
  }
91
- item.timeoutMs += elapsed;
92
- if (item.timeoutMs >= 16) {
93
- item.timeoutMs = 0;
280
+ }
281
+ this.editor.updateInstanceState({ scribbles });
282
+ });
283
+ }
284
+ // ==================== PRIVATE HELPERS ====================
285
+ resetIdleTimeout(session) {
286
+ this.clearIdleTimeout(session);
287
+ session.idleTimeoutHandle = this.editor.timers.setTimeout(() => {
288
+ this.stopSession(session.id);
289
+ }, session.options.idleTimeoutMs);
290
+ }
291
+ clearIdleTimeout(session) {
292
+ if (session.idleTimeoutHandle !== void 0) {
293
+ clearTimeout(session.idleTimeoutHandle);
294
+ session.idleTimeoutHandle = void 0;
295
+ }
296
+ }
297
+ tickSession(session, elapsed) {
298
+ if (session.state === "complete") return;
299
+ if (session.state === "stopping" && session.options.fadeMode === "grouped") {
300
+ this.tickGroupedFade(session, elapsed);
301
+ } else {
302
+ this.tickSessionItems(session, elapsed);
303
+ }
304
+ const hasContent = session.items.some((item) => item.scribble.points.length > 0);
305
+ if (!hasContent && (session.state === "stopping" || session.items.length === 0)) {
306
+ session.state = "complete";
307
+ }
308
+ }
309
+ tickSessionItems(session, elapsed) {
310
+ for (const item of session.items) {
311
+ const shouldSelfConsume = session.options.selfConsume || session.state === "stopping" || item.scribble.state === "stopping";
312
+ if (shouldSelfConsume) {
313
+ this.tickSelfConsumingItem(item, elapsed);
314
+ } else {
315
+ this.tickPersistentItem(item);
316
+ }
317
+ }
318
+ if (session.options.fadeMode === "individual") {
319
+ for (let i = session.items.length - 1; i >= 0; i--) {
320
+ if (session.items[i].scribble.points.length === 0) {
321
+ session.items.splice(i, 1);
94
322
  }
95
- const { delayRemaining, timeoutMs, prev, next, scribble } = item;
96
- switch (scribble.state) {
97
- case "active": {
98
- if (next && next !== prev) {
99
- item.prev = next;
100
- scribble.points.push(next);
101
- if (delayRemaining === 0) {
102
- if (scribble.points.length > 8) {
103
- scribble.points.shift();
104
- }
105
- }
323
+ }
324
+ }
325
+ }
326
+ tickPersistentItem(item) {
327
+ const { scribble } = item;
328
+ if (scribble.state === "starting") {
329
+ const { next, prev } = item;
330
+ if (next && next !== prev) {
331
+ item.prev = next;
332
+ scribble.points.push(next);
333
+ }
334
+ if (scribble.points.length > 8) {
335
+ scribble.state = "active";
336
+ }
337
+ return;
338
+ }
339
+ if (scribble.state === "active") {
340
+ const { next, prev } = item;
341
+ if (next && next !== prev) {
342
+ item.prev = next;
343
+ scribble.points.push(next);
344
+ }
345
+ }
346
+ }
347
+ tickSelfConsumingItem(item, elapsed) {
348
+ const { scribble } = item;
349
+ if (scribble.state === "starting") {
350
+ const { next: next2, prev: prev2 } = item;
351
+ if (next2 && next2 !== prev2) {
352
+ item.prev = next2;
353
+ scribble.points.push(next2);
354
+ }
355
+ if (scribble.points.length > 8) {
356
+ scribble.state = "active";
357
+ }
358
+ return;
359
+ }
360
+ if (item.delayRemaining > 0) {
361
+ item.delayRemaining = Math.max(0, item.delayRemaining - elapsed);
362
+ }
363
+ item.timeoutMs += elapsed;
364
+ if (item.timeoutMs >= 16) {
365
+ item.timeoutMs = 0;
366
+ }
367
+ const { delayRemaining, timeoutMs, prev, next } = item;
368
+ switch (scribble.state) {
369
+ case "active": {
370
+ if (next && next !== prev) {
371
+ item.prev = next;
372
+ scribble.points.push(next);
373
+ if (delayRemaining === 0 && scribble.points.length > 8) {
374
+ scribble.points.shift();
375
+ }
376
+ } else {
377
+ if (timeoutMs === 0) {
378
+ if (scribble.points.length > 1) {
379
+ scribble.points.shift();
106
380
  } else {
107
- if (timeoutMs === 0) {
108
- if (scribble.points.length > 1) {
109
- scribble.points.shift();
110
- } else {
111
- item.delayRemaining = scribble.delay;
112
- }
113
- }
381
+ item.delayRemaining = scribble.delay;
114
382
  }
115
- break;
116
383
  }
117
- case "stopping": {
118
- if (item.delayRemaining === 0) {
119
- if (timeoutMs === 0) {
120
- if (scribble.points.length === 1) {
121
- this.scribbleItems.delete(item.id);
122
- return;
123
- }
124
- if (scribble.shrink) {
125
- scribble.size = Math.max(1, scribble.size * (1 - scribble.shrink));
126
- }
127
- scribble.points.shift();
128
- }
129
- }
130
- break;
384
+ }
385
+ break;
386
+ }
387
+ case "stopping": {
388
+ if (delayRemaining === 0 && timeoutMs === 0) {
389
+ if (scribble.points.length <= 1) {
390
+ scribble.points.length = 0;
391
+ return;
131
392
  }
132
- case "paused": {
133
- break;
393
+ if (scribble.shrink) {
394
+ scribble.size = Math.max(1, scribble.size * (1 - scribble.shrink));
134
395
  }
396
+ scribble.points.shift();
135
397
  }
136
- });
137
- this.editor.updateInstanceState({
138
- scribbles: Array.from(this.scribbleItems.values()).map(({ scribble }) => ({
139
- ...scribble,
140
- points: [...scribble.points]
141
- })).slice(-5)
142
- // limit to three as a minor sanity check
143
- });
144
- });
398
+ break;
399
+ }
400
+ case "paused": {
401
+ break;
402
+ }
403
+ }
404
+ }
405
+ tickGroupedFade(session, elapsed) {
406
+ session.fadeElapsed += elapsed;
407
+ let remainingPoints = 0;
408
+ for (const item of session.items) {
409
+ remainingPoints += item.scribble.points.length;
410
+ }
411
+ if (remainingPoints === 0) return;
412
+ if (session.fadeElapsed >= session.options.fadeDurationMs) {
413
+ for (const item of session.items) {
414
+ item.scribble.points.length = 0;
415
+ }
416
+ return;
417
+ }
418
+ const progress = session.fadeElapsed / session.options.fadeDurationMs;
419
+ const easedProgress = session.options.fadeEasing === "ease-in" ? progress * progress : progress;
420
+ const targetRemoved = Math.floor(easedProgress * session.totalPointsAtFadeStart);
421
+ const actuallyRemoved = session.totalPointsAtFadeStart - remainingPoints;
422
+ const pointsToRemove = Math.max(1, targetRemoved - actuallyRemoved);
423
+ let removed = 0;
424
+ let itemIndex = 0;
425
+ while (removed < pointsToRemove && itemIndex < session.items.length) {
426
+ const item = session.items[itemIndex];
427
+ if (item.scribble.points.length > 0) {
428
+ item.scribble.points.shift();
429
+ removed++;
430
+ } else {
431
+ itemIndex++;
432
+ }
433
+ }
145
434
  }
146
435
  }
147
436
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/ScribbleManager/ScribbleManager.ts"],
4
- "sourcesContent": ["import { TLScribble, VecModel } from '@tldraw/tlschema'\nimport { uniqueId } from '@tldraw/utils'\nimport { Vec } from '../../../primitives/Vec'\nimport { Editor } from '../../Editor'\n\n/** @public */\nexport interface ScribbleItem {\n\tid: string\n\tscribble: TLScribble\n\ttimeoutMs: number\n\tdelayRemaining: number\n\tprev: null | VecModel\n\tnext: null | VecModel\n}\n\n/** @public */\nexport class ScribbleManager {\n\tscribbleItems = new Map<string, ScribbleItem>()\n\tstate = 'paused' as 'paused' | 'running'\n\n\tconstructor(private editor: Editor) {}\n\n\taddScribble(scribble: Partial<TLScribble>, id = uniqueId()) {\n\t\tconst item: ScribbleItem = {\n\t\t\tid,\n\t\t\tscribble: {\n\t\t\t\tid,\n\t\t\t\tsize: 20,\n\t\t\t\tcolor: 'accent',\n\t\t\t\topacity: 0.8,\n\t\t\t\tdelay: 0,\n\t\t\t\tpoints: [],\n\t\t\t\tshrink: 0.1,\n\t\t\t\ttaper: true,\n\t\t\t\t...scribble,\n\t\t\t\tstate: 'starting',\n\t\t\t},\n\t\t\ttimeoutMs: 0,\n\t\t\tdelayRemaining: scribble.delay ?? 0,\n\t\t\tprev: null,\n\t\t\tnext: null,\n\t\t}\n\t\tthis.scribbleItems.set(id, item)\n\t\treturn item\n\t}\n\n\treset() {\n\t\tthis.editor.updateInstanceState({ scribbles: [] })\n\t\tthis.scribbleItems.clear()\n\t}\n\n\t/**\n\t * Start stopping the scribble. The scribble won't be removed until its last point is cleared.\n\t *\n\t * @public\n\t */\n\tstop(id: ScribbleItem['id']) {\n\t\tconst item = this.scribbleItems.get(id)\n\t\tif (!item) throw Error(`Scribble with id ${id} not found`)\n\t\titem.delayRemaining = Math.min(item.delayRemaining, 200)\n\t\titem.scribble.state = 'stopping'\n\t\treturn item\n\t}\n\n\t/**\n\t * Set the scribble's next point.\n\t *\n\t * @param id - The id of the scribble to add a point to.\n\t * @param x - The x coordinate of the point.\n\t * @param y - The y coordinate of the point.\n\t * @param z - The z coordinate of the point.\n\t * @public\n\t */\n\taddPoint(id: ScribbleItem['id'], x: number, y: number, z = 0.5) {\n\t\tconst item = this.scribbleItems.get(id)\n\t\tif (!item) throw Error(`Scribble with id ${id} not found`)\n\t\tconst { prev } = item\n\t\tconst point = { x, y, z }\n\t\tif (!prev || Vec.Dist(prev, point) >= 1) {\n\t\t\titem.next = point\n\t\t}\n\t\treturn item\n\t}\n\n\t/**\n\t * Update on each animation frame.\n\t *\n\t * @param elapsed - The number of milliseconds since the last tick.\n\t * @public\n\t */\n\ttick(elapsed: number) {\n\t\tif (this.scribbleItems.size === 0) return\n\t\tthis.editor.run(() => {\n\t\t\tthis.scribbleItems.forEach((item) => {\n\t\t\t\t// let the item get at least eight points before\n\t\t\t\t// switching from starting to active\n\t\t\t\tif (item.scribble.state === 'starting') {\n\t\t\t\t\tconst { next, prev } = item\n\t\t\t\t\tif (next && next !== prev) {\n\t\t\t\t\t\titem.prev = next\n\t\t\t\t\t\titem.scribble.points.push(next)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.scribble.points.length > 8) {\n\t\t\t\t\t\titem.scribble.state = 'active'\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (item.delayRemaining > 0) {\n\t\t\t\t\titem.delayRemaining = Math.max(0, item.delayRemaining - elapsed)\n\t\t\t\t}\n\n\t\t\t\titem.timeoutMs += elapsed\n\t\t\t\tif (item.timeoutMs >= 16) {\n\t\t\t\t\titem.timeoutMs = 0\n\t\t\t\t}\n\n\t\t\t\tconst { delayRemaining, timeoutMs, prev, next, scribble } = item\n\n\t\t\t\tswitch (scribble.state) {\n\t\t\t\t\tcase 'active': {\n\t\t\t\t\t\tif (next && next !== prev) {\n\t\t\t\t\t\t\titem.prev = next\n\t\t\t\t\t\t\tscribble.points.push(next)\n\n\t\t\t\t\t\t\t// If we've run out of delay, then shrink the scribble from the start\n\t\t\t\t\t\t\tif (delayRemaining === 0) {\n\t\t\t\t\t\t\t\tif (scribble.points.length > 8) {\n\t\t\t\t\t\t\t\t\tscribble.points.shift()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// While not moving, shrink the scribble from the start\n\t\t\t\t\t\t\tif (timeoutMs === 0) {\n\t\t\t\t\t\t\t\tif (scribble.points.length > 1) {\n\t\t\t\t\t\t\t\t\tscribble.points.shift()\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Reset the item's delay\n\t\t\t\t\t\t\t\t\titem.delayRemaining = scribble.delay\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'stopping': {\n\t\t\t\t\t\tif (item.delayRemaining === 0) {\n\t\t\t\t\t\t\tif (timeoutMs === 0) {\n\t\t\t\t\t\t\t\t// If the scribble is down to one point, we're done!\n\t\t\t\t\t\t\t\tif (scribble.points.length === 1) {\n\t\t\t\t\t\t\t\t\tthis.scribbleItems.delete(item.id) // Remove the scribble\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (scribble.shrink) {\n\t\t\t\t\t\t\t\t\t// Drop the scribble's size as it shrinks\n\t\t\t\t\t\t\t\t\tscribble.size = Math.max(1, scribble.size * (1 - scribble.shrink))\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Drop the scribble's first point (its tail)\n\t\t\t\t\t\t\t\tscribble.points.shift()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'paused': {\n\t\t\t\t\t\t// Nothing to do while paused.\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// The object here will get frozen into the record, so we need to\n\t\t\t// create a copies of the parts that what we'll be mutating later.\n\t\t\tthis.editor.updateInstanceState({\n\t\t\t\tscribbles: Array.from(this.scribbleItems.values())\n\t\t\t\t\t.map(({ scribble }) => ({\n\t\t\t\t\t\t...scribble,\n\t\t\t\t\t\tpoints: [...scribble.points],\n\t\t\t\t\t}))\n\t\t\t\t\t.slice(-5), // limit to three as a minor sanity check\n\t\t\t})\n\t\t})\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { TLScribble, VecModel } from '@tldraw/tlschema'\nimport { uniqueId } from '@tldraw/utils'\nimport { Vec } from '../../../primitives/Vec'\nimport { Editor } from '../../Editor'\n\n/** @public */\nexport interface ScribbleItem {\n\tid: string\n\tscribble: TLScribble\n\ttimeoutMs: number\n\tdelayRemaining: number\n\tprev: null | VecModel\n\tnext: null | VecModel\n}\n\n/** @public */\nexport interface ScribbleSessionOptions {\n\t/** Session id. Auto-generated if not provided. */\n\tid?: string\n\t/**\n\t * Whether scribbles self-consume (shrink from start) while drawing.\n\t * - true: scribbles eat their own tail as you draw (default, used for eraser/select)\n\t * - false: scribbles persist until session stops (used for laser)\n\t */\n\tselfConsume?: boolean\n\t/**\n\t * How long to wait after last activity before auto-stopping the session.\n\t * Only applies when selfConsume is false.\n\t */\n\tidleTimeoutMs?: number\n\t/**\n\t * How scribbles fade when stopping.\n\t * - 'individual': each scribble fades on its own (default)\n\t * - 'grouped': all scribbles fade together as one sequence\n\t */\n\tfadeMode?: 'individual' | 'grouped'\n\t/**\n\t * Easing for grouped fade.\n\t */\n\tfadeEasing?: 'linear' | 'ease-in'\n\t/**\n\t * Duration of the fade in milliseconds.\n\t */\n\tfadeDurationMs?: number\n}\n\n// Internal session state (not exported)\ninterface Session {\n\tid: string\n\titems: ScribbleItem[]\n\tstate: 'active' | 'stopping' | 'complete'\n\toptions: Required<Omit<ScribbleSessionOptions, 'id'>>\n\tidleTimeoutHandle?: number\n\tfadeElapsed: number\n\ttotalPointsAtFadeStart: number\n}\n\n/** @public */\nexport class ScribbleManager {\n\tprivate sessions = new Map<string, Session>()\n\n\tconstructor(private editor: Editor) {}\n\n\t// ==================== SESSION API ====================\n\n\t/**\n\t * Start a new session for grouping scribbles.\n\t * Returns a session ID that can be used with other session methods.\n\t *\n\t * @param options - Session configuration\n\t * @returns Session ID\n\t * @public\n\t */\n\tstartSession(options: ScribbleSessionOptions = {}): string {\n\t\tconst id = options.id ?? uniqueId()\n\t\tconst session: Session = {\n\t\t\tid,\n\t\t\titems: [],\n\t\t\tstate: 'active',\n\t\t\toptions: {\n\t\t\t\tselfConsume: options.selfConsume ?? true,\n\t\t\t\tidleTimeoutMs: options.idleTimeoutMs ?? 0,\n\t\t\t\tfadeMode: options.fadeMode ?? 'individual',\n\t\t\t\tfadeEasing: options.fadeEasing ?? (options.fadeMode === 'grouped' ? 'ease-in' : 'linear'),\n\t\t\t\tfadeDurationMs: options.fadeDurationMs ?? this.editor.options.laserFadeoutMs,\n\t\t\t},\n\t\t\tfadeElapsed: 0,\n\t\t\ttotalPointsAtFadeStart: 0,\n\t\t}\n\n\t\tthis.sessions.set(id, session)\n\n\t\t// Set up idle timeout if configured\n\t\tif (session.options.idleTimeoutMs > 0) {\n\t\t\tthis.resetIdleTimeout(session)\n\t\t}\n\n\t\treturn id\n\t}\n\n\t/**\n\t * Add a scribble to a session.\n\t *\n\t * @param sessionId - The session ID\n\t * @param scribble - Partial scribble properties\n\t * @param scribbleId - Optional scribble ID\n\t * @public\n\t */\n\taddScribbleToSession(\n\t\tsessionId: string,\n\t\tscribble: Partial<TLScribble>,\n\t\tscribbleId = uniqueId()\n\t): ScribbleItem {\n\t\tconst session = this.sessions.get(sessionId)\n\t\tif (!session) throw Error(`Session ${sessionId} not found`)\n\n\t\tconst item: ScribbleItem = {\n\t\t\tid: scribbleId,\n\t\t\tscribble: {\n\t\t\t\tid: scribbleId,\n\t\t\t\tsize: 20,\n\t\t\t\tcolor: 'accent',\n\t\t\t\topacity: 0.8,\n\t\t\t\tdelay: 0,\n\t\t\t\tpoints: [],\n\t\t\t\tshrink: 0.1,\n\t\t\t\ttaper: true,\n\t\t\t\t...scribble,\n\t\t\t\tstate: 'starting',\n\t\t\t},\n\t\t\ttimeoutMs: 0,\n\t\t\tdelayRemaining: scribble.delay ?? 0,\n\t\t\tprev: null,\n\t\t\tnext: null,\n\t\t}\n\n\t\tsession.items.push(item)\n\n\t\t// Reset idle timeout on activity\n\t\tif (session.options.idleTimeoutMs > 0) {\n\t\t\tthis.resetIdleTimeout(session)\n\t\t}\n\n\t\treturn item\n\t}\n\n\t/**\n\t * Add a point to a scribble in a session.\n\t *\n\t * @param sessionId - The session ID\n\t * @param scribbleId - The scribble ID\n\t * @param x - X coordinate\n\t * @param y - Y coordinate\n\t * @param z - Z coordinate (pressure)\n\t * @public\n\t */\n\taddPointToSession(\n\t\tsessionId: string,\n\t\tscribbleId: string,\n\t\tx: number,\n\t\ty: number,\n\t\tz = 0.5\n\t): ScribbleItem {\n\t\tconst session = this.sessions.get(sessionId)\n\t\tif (!session) throw Error(`Session ${sessionId} not found`)\n\n\t\tconst item = session.items.find((i) => i.id === scribbleId)\n\t\tif (!item) throw Error(`Scribble ${scribbleId} not found in session ${sessionId}`)\n\n\t\tconst point = { x, y, z }\n\t\tif (!item.prev || Vec.Dist(item.prev, point) >= 1) {\n\t\t\titem.next = point\n\t\t}\n\n\t\t// Reset idle timeout on activity\n\t\tif (session.options.idleTimeoutMs > 0) {\n\t\t\tthis.resetIdleTimeout(session)\n\t\t}\n\n\t\treturn item\n\t}\n\n\t/**\n\t * Extend a session, resetting its idle timeout.\n\t *\n\t * @param sessionId - The session ID\n\t * @public\n\t */\n\textendSession(sessionId: string): void {\n\t\tconst session = this.sessions.get(sessionId)\n\t\tif (!session) return\n\n\t\tif (session.options.idleTimeoutMs > 0) {\n\t\t\tthis.resetIdleTimeout(session)\n\t\t}\n\t}\n\n\t/**\n\t * Stop a session, triggering fade-out.\n\t *\n\t * @param sessionId - The session ID\n\t * @public\n\t */\n\tstopSession(sessionId: string): void {\n\t\tconst session = this.sessions.get(sessionId)\n\t\tif (!session || session.state !== 'active') return\n\n\t\tthis.clearIdleTimeout(session)\n\t\tsession.state = 'stopping'\n\n\t\tif (session.options.fadeMode === 'grouped') {\n\t\t\tsession.totalPointsAtFadeStart = session.items.reduce(\n\t\t\t\t(sum, item) => sum + item.scribble.points.length,\n\t\t\t\t0\n\t\t\t)\n\t\t\tsession.fadeElapsed = 0\n\t\t\tfor (const item of session.items) {\n\t\t\t\titem.scribble.state = 'stopping'\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const item of session.items) {\n\t\t\t\titem.delayRemaining = Math.min(item.delayRemaining, 200)\n\t\t\t\titem.scribble.state = 'stopping'\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clear all scribbles in a session immediately.\n\t *\n\t * @param sessionId - The session ID\n\t * @public\n\t */\n\tclearSession(sessionId: string): void {\n\t\tconst session = this.sessions.get(sessionId)\n\t\tif (!session) return\n\n\t\tthis.clearIdleTimeout(session)\n\t\tfor (const item of session.items) {\n\t\t\titem.scribble.points.length = 0\n\t\t}\n\t\tsession.state = 'complete'\n\t}\n\n\t/**\n\t * Check if a session is active.\n\t *\n\t * @param sessionId - The session ID\n\t * @public\n\t */\n\tisSessionActive(sessionId: string): boolean {\n\t\tconst session = this.sessions.get(sessionId)\n\t\treturn session?.state === 'active'\n\t}\n\n\t// ==================== SIMPLE API (for eraser, select, etc.) ====================\n\n\t/**\n\t * Add a scribble using the default self-consuming behavior.\n\t * Creates an implicit session for the scribble.\n\t *\n\t * @param scribble - Partial scribble properties\n\t * @param id - Optional scribble id\n\t * @returns The created scribble item\n\t * @public\n\t */\n\taddScribble(scribble: Partial<TLScribble>, id = uniqueId()): ScribbleItem {\n\t\tconst sessionId = this.startSession()\n\t\treturn this.addScribbleToSession(sessionId, scribble, id)\n\t}\n\n\t/**\n\t * Add a point to a scribble. Searches all sessions.\n\t *\n\t * @param id - The scribble id\n\t * @param x - X coordinate\n\t * @param y - Y coordinate\n\t * @param z - Z coordinate (pressure)\n\t * @public\n\t */\n\taddPoint(id: string, x: number, y: number, z = 0.5): ScribbleItem {\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tconst item = session.items.find((i) => i.id === id)\n\t\t\tif (item) {\n\t\t\t\tconst point = { x, y, z }\n\t\t\t\tif (!item.prev || Vec.Dist(item.prev, point) >= 1) {\n\t\t\t\t\titem.next = point\n\t\t\t\t}\n\t\t\t\tif (session.options.idleTimeoutMs > 0) {\n\t\t\t\t\tthis.resetIdleTimeout(session)\n\t\t\t\t}\n\t\t\t\treturn item\n\t\t\t}\n\t\t}\n\t\tthrow Error(`Scribble with id ${id} not found`)\n\t}\n\n\t/**\n\t * Mark a scribble as complete (done being drawn but not yet fading).\n\t * Searches all sessions.\n\t *\n\t * @param id - The scribble id\n\t * @public\n\t */\n\tcomplete(id: string): ScribbleItem {\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tconst item = session.items.find((i) => i.id === id)\n\t\t\tif (item) {\n\t\t\t\tif (item.scribble.state === 'starting' || item.scribble.state === 'active') {\n\t\t\t\t\titem.scribble.state = 'complete'\n\t\t\t\t}\n\t\t\t\treturn item\n\t\t\t}\n\t\t}\n\t\tthrow Error(`Scribble with id ${id} not found`)\n\t}\n\n\t/**\n\t * Stop a scribble. Searches all sessions.\n\t *\n\t * @param id - The scribble id\n\t * @public\n\t */\n\tstop(id: string): ScribbleItem {\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tconst item = session.items.find((i) => i.id === id)\n\t\t\tif (item) {\n\t\t\t\titem.delayRemaining = Math.min(item.delayRemaining, 200)\n\t\t\t\titem.scribble.state = 'stopping'\n\t\t\t\treturn item\n\t\t\t}\n\t\t}\n\t\tthrow Error(`Scribble with id ${id} not found`)\n\t}\n\n\t/**\n\t * Stop and remove all sessions.\n\t *\n\t * @public\n\t */\n\treset(): void {\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tthis.clearIdleTimeout(session)\n\t\t}\n\t\tthis.sessions.clear()\n\t\tthis.editor.updateInstanceState({ scribbles: [] })\n\t}\n\n\t/**\n\t * Update on each animation frame.\n\t *\n\t * @param elapsed - The number of milliseconds since the last tick.\n\t * @public\n\t */\n\ttick(elapsed: number): void {\n\t\tconst currentScribbles = this.editor.getInstanceState().scribbles\n\t\tif (this.sessions.size === 0 && currentScribbles.length === 0) return\n\n\t\tthis.editor.run(() => {\n\t\t\t// Tick all sessions\n\t\t\tfor (const session of this.sessions.values()) {\n\t\t\t\tthis.tickSession(session, elapsed)\n\t\t\t}\n\n\t\t\t// Remove completed sessions\n\t\t\tfor (const [id, session] of this.sessions) {\n\t\t\t\tif (session.state === 'complete') {\n\t\t\t\t\tthis.clearIdleTimeout(session)\n\t\t\t\t\tthis.sessions.delete(id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Collect scribbles from all sessions\n\t\t\tconst scribbles: TLScribble[] = []\n\t\t\tfor (const session of this.sessions.values()) {\n\t\t\t\tfor (const item of session.items) {\n\t\t\t\t\tif (item.scribble.points.length > 0) {\n\t\t\t\t\t\tscribbles.push({\n\t\t\t\t\t\t\t...item.scribble,\n\t\t\t\t\t\t\tpoints: [...item.scribble.points],\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.editor.updateInstanceState({ scribbles })\n\t\t})\n\t}\n\n\t// ==================== PRIVATE HELPERS ====================\n\n\tprivate resetIdleTimeout(session: Session): void {\n\t\tthis.clearIdleTimeout(session)\n\t\tsession.idleTimeoutHandle = this.editor.timers.setTimeout(() => {\n\t\t\tthis.stopSession(session.id)\n\t\t}, session.options.idleTimeoutMs)\n\t}\n\n\tprivate clearIdleTimeout(session: Session): void {\n\t\tif (session.idleTimeoutHandle !== undefined) {\n\t\t\tclearTimeout(session.idleTimeoutHandle)\n\t\t\tsession.idleTimeoutHandle = undefined\n\t\t}\n\t}\n\n\tprivate tickSession(session: Session, elapsed: number): void {\n\t\tif (session.state === 'complete') return\n\n\t\tif (session.state === 'stopping' && session.options.fadeMode === 'grouped') {\n\t\t\tthis.tickGroupedFade(session, elapsed)\n\t\t} else {\n\t\t\tthis.tickSessionItems(session, elapsed)\n\t\t}\n\n\t\t// Check if session is complete\n\t\tconst hasContent = session.items.some((item) => item.scribble.points.length > 0)\n\t\tif (!hasContent && (session.state === 'stopping' || session.items.length === 0)) {\n\t\t\tsession.state = 'complete'\n\t\t}\n\t}\n\n\tprivate tickSessionItems(session: Session, elapsed: number): void {\n\t\tfor (const item of session.items) {\n\t\t\tconst shouldSelfConsume =\n\t\t\t\tsession.options.selfConsume ||\n\t\t\t\tsession.state === 'stopping' ||\n\t\t\t\titem.scribble.state === 'stopping'\n\n\t\t\tif (shouldSelfConsume) {\n\t\t\t\tthis.tickSelfConsumingItem(item, elapsed)\n\t\t\t} else {\n\t\t\t\tthis.tickPersistentItem(item)\n\t\t\t}\n\t\t}\n\n\t\t// Remove completed items in individual fade mode\n\t\tif (session.options.fadeMode === 'individual') {\n\t\t\tfor (let i = session.items.length - 1; i >= 0; i--) {\n\t\t\t\tif (session.items[i].scribble.points.length === 0) {\n\t\t\t\t\tsession.items.splice(i, 1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate tickPersistentItem(item: ScribbleItem): void {\n\t\tconst { scribble } = item\n\n\t\tif (scribble.state === 'starting') {\n\t\t\tconst { next, prev } = item\n\t\t\tif (next && next !== prev) {\n\t\t\t\titem.prev = next\n\t\t\t\tscribble.points.push(next)\n\t\t\t}\n\t\t\tif (scribble.points.length > 8) {\n\t\t\t\tscribble.state = 'active'\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif (scribble.state === 'active') {\n\t\t\tconst { next, prev } = item\n\t\t\tif (next && next !== prev) {\n\t\t\t\titem.prev = next\n\t\t\t\tscribble.points.push(next)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate tickSelfConsumingItem(item: ScribbleItem, elapsed: number): void {\n\t\tconst { scribble } = item\n\n\t\tif (scribble.state === 'starting') {\n\t\t\tconst { next, prev } = item\n\t\t\tif (next && next !== prev) {\n\t\t\t\titem.prev = next\n\t\t\t\tscribble.points.push(next)\n\t\t\t}\n\t\t\tif (scribble.points.length > 8) {\n\t\t\t\tscribble.state = 'active'\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif (item.delayRemaining > 0) {\n\t\t\titem.delayRemaining = Math.max(0, item.delayRemaining - elapsed)\n\t\t}\n\n\t\titem.timeoutMs += elapsed\n\t\tif (item.timeoutMs >= 16) {\n\t\t\titem.timeoutMs = 0\n\t\t}\n\n\t\tconst { delayRemaining, timeoutMs, prev, next } = item\n\n\t\tswitch (scribble.state) {\n\t\t\tcase 'active': {\n\t\t\t\tif (next && next !== prev) {\n\t\t\t\t\titem.prev = next\n\t\t\t\t\tscribble.points.push(next)\n\t\t\t\t\tif (delayRemaining === 0 && scribble.points.length > 8) {\n\t\t\t\t\t\tscribble.points.shift()\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (timeoutMs === 0) {\n\t\t\t\t\t\tif (scribble.points.length > 1) {\n\t\t\t\t\t\t\tscribble.points.shift()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\titem.delayRemaining = scribble.delay\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'stopping': {\n\t\t\t\tif (delayRemaining === 0 && timeoutMs === 0) {\n\t\t\t\t\tif (scribble.points.length <= 1) {\n\t\t\t\t\t\tscribble.points.length = 0\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif (scribble.shrink) {\n\t\t\t\t\t\tscribble.size = Math.max(1, scribble.size * (1 - scribble.shrink))\n\t\t\t\t\t}\n\t\t\t\t\tscribble.points.shift()\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'paused': {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate tickGroupedFade(session: Session, elapsed: number): void {\n\t\tsession.fadeElapsed += elapsed\n\n\t\tlet remainingPoints = 0\n\t\tfor (const item of session.items) {\n\t\t\tremainingPoints += item.scribble.points.length\n\t\t}\n\n\t\tif (remainingPoints === 0) return\n\n\t\tif (session.fadeElapsed >= session.options.fadeDurationMs) {\n\t\t\tfor (const item of session.items) {\n\t\t\t\titem.scribble.points.length = 0\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tconst progress = session.fadeElapsed / session.options.fadeDurationMs\n\t\tconst easedProgress = session.options.fadeEasing === 'ease-in' ? progress * progress : progress\n\n\t\tconst targetRemoved = Math.floor(easedProgress * session.totalPointsAtFadeStart)\n\t\tconst actuallyRemoved = session.totalPointsAtFadeStart - remainingPoints\n\t\tconst pointsToRemove = Math.max(1, targetRemoved - actuallyRemoved)\n\n\t\tlet removed = 0\n\t\tlet itemIndex = 0\n\t\twhile (removed < pointsToRemove && itemIndex < session.items.length) {\n\t\t\tconst item = session.items[itemIndex]\n\t\t\tif (item.scribble.points.length > 0) {\n\t\t\t\titem.scribble.points.shift()\n\t\t\t\tremoved++\n\t\t\t} else {\n\t\t\t\titemIndex++\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["next", "prev"]
7
7
  }
@@ -46,10 +46,10 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
46
46
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
47
47
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
48
48
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
49
- var _getInputMode_dec, _getEnhancedA11yMode_dec, _getIsPasteAtCursorMode_dec, _getIsDynamicResizeMode_dec, _getIsWrapMode_dec, _getIsSnapMode_dec, _getColor_dec, _getLocale_dec, _getName_dec, _getId_dec, _getAreKeyboardShortcutsEnabled_dec, _getAnimationSpeed_dec, _getEdgeScrollSpeed_dec, _getIsDarkMode_dec, _getUserPreferences_dec, _init;
49
+ var _getIsZoomDirectionInverted_dec, _getInputMode_dec, _getEnhancedA11yMode_dec, _getIsPasteAtCursorMode_dec, _getIsDynamicResizeMode_dec, _getIsWrapMode_dec, _getIsSnapMode_dec, _getColor_dec, _getLocale_dec, _getName_dec, _getId_dec, _getAreKeyboardShortcutsEnabled_dec, _getAnimationSpeed_dec, _getEdgeScrollSpeed_dec, _getIsDarkMode_dec, _getUserPreferences_dec, _init;
50
50
  import { atom, computed } from "@tldraw/state";
51
51
  import { defaultUserPreferences } from "../../../config/TLUserPreferences.mjs";
52
- _getUserPreferences_dec = [computed], _getIsDarkMode_dec = [computed], _getEdgeScrollSpeed_dec = [computed], _getAnimationSpeed_dec = [computed], _getAreKeyboardShortcutsEnabled_dec = [computed], _getId_dec = [computed], _getName_dec = [computed], _getLocale_dec = [computed], _getColor_dec = [computed], _getIsSnapMode_dec = [computed], _getIsWrapMode_dec = [computed], _getIsDynamicResizeMode_dec = [computed], _getIsPasteAtCursorMode_dec = [computed], _getEnhancedA11yMode_dec = [computed], _getInputMode_dec = [computed];
52
+ _getUserPreferences_dec = [computed], _getIsDarkMode_dec = [computed], _getEdgeScrollSpeed_dec = [computed], _getAnimationSpeed_dec = [computed], _getAreKeyboardShortcutsEnabled_dec = [computed], _getId_dec = [computed], _getName_dec = [computed], _getLocale_dec = [computed], _getColor_dec = [computed], _getIsSnapMode_dec = [computed], _getIsWrapMode_dec = [computed], _getIsDynamicResizeMode_dec = [computed], _getIsPasteAtCursorMode_dec = [computed], _getEnhancedA11yMode_dec = [computed], _getInputMode_dec = [computed], _getIsZoomDirectionInverted_dec = [computed];
53
53
  class UserPreferencesManager {
54
54
  constructor(user, inferDarkMode) {
55
55
  this.user = user;
@@ -95,7 +95,8 @@ class UserPreferencesManager {
95
95
  isWrapMode: this.getIsWrapMode(),
96
96
  isDynamicResizeMode: this.getIsDynamicResizeMode(),
97
97
  enhancedA11yMode: this.getEnhancedA11yMode(),
98
- inputMode: this.getInputMode()
98
+ inputMode: this.getInputMode(),
99
+ isZoomDirectionInverted: this.getIsZoomDirectionInverted()
99
100
  };
100
101
  }
101
102
  getIsDarkMode() {
@@ -149,6 +150,9 @@ class UserPreferencesManager {
149
150
  getInputMode() {
150
151
  return this.user.userPreferences.get().inputMode ?? defaultUserPreferences.inputMode;
151
152
  }
153
+ getIsZoomDirectionInverted() {
154
+ return this.user.userPreferences.get().isZoomDirectionInverted ?? defaultUserPreferences.isZoomDirectionInverted;
155
+ }
152
156
  }
153
157
  _init = __decoratorStart(null);
154
158
  __decorateElement(_init, 1, "getUserPreferences", _getUserPreferences_dec, UserPreferencesManager);
@@ -166,6 +170,7 @@ __decorateElement(_init, 1, "getIsDynamicResizeMode", _getIsDynamicResizeMode_de
166
170
  __decorateElement(_init, 1, "getIsPasteAtCursorMode", _getIsPasteAtCursorMode_dec, UserPreferencesManager);
167
171
  __decorateElement(_init, 1, "getEnhancedA11yMode", _getEnhancedA11yMode_dec, UserPreferencesManager);
168
172
  __decorateElement(_init, 1, "getInputMode", _getInputMode_dec, UserPreferencesManager);
173
+ __decorateElement(_init, 1, "getIsZoomDirectionInverted", _getIsZoomDirectionInverted_dec, UserPreferencesManager);
169
174
  __decoratorMetadata(_init, UserPreferencesManager);
170
175
  export {
171
176
  UserPreferencesManager