@tldraw/editor 5.1.1 → 5.2.0-canary.05c017c18b15
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.
- package/README.md +7 -1
- package/dist-cjs/index.d.ts +50 -50
- package/dist-cjs/index.js +4 -4
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +4 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +3 -3
- package/dist-cjs/lib/components/default-components/DefaultLoadingScreen.js +2 -2
- package/dist-cjs/lib/components/default-components/DefaultLoadingScreen.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeErrorFallback.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeErrorFallback.js.map +3 -3
- package/dist-cjs/lib/components/default-components/DefaultSvgDefs.js +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSvgDefs.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +119 -54
- package/dist-cjs/lib/editor/Editor.js.map +3 -3
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js +2 -2
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js +2 -2
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +8 -58
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +3 -3
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +1 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +24 -2
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +4 -2
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +7 -3
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +15 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +79 -0
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +7 -0
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -0
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/event-types.js +0 -2
- package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
- package/dist-cjs/lib/hooks/usePresence.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +3 -1
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +2 -2
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +5 -3
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +50 -50
- package/dist-esm/index.mjs +5 -7
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +4 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/DefaultLoadingScreen.mjs +2 -2
- package/dist-esm/lib/components/default-components/DefaultLoadingScreen.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeErrorFallback.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultShapeErrorFallback.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/DefaultSvgDefs.mjs +2 -2
- package/dist-esm/lib/components/default-components/DefaultSvgDefs.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +119 -54
- package/dist-esm/lib/editor/Editor.mjs.map +3 -3
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +2 -2
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +2 -2
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +8 -58
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +3 -3
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +1 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +24 -2
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +4 -2
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -3
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +15 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +59 -0
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +7 -0
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -0
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/editor/types/event-types.mjs +0 -2
- package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePresence.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +3 -1
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +2 -2
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +5 -3
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +2 -0
- package/package.json +8 -8
- package/src/index.ts +2 -5
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +4 -1
- package/src/lib/components/default-components/DefaultLoadingScreen.tsx +1 -1
- package/src/lib/components/default-components/DefaultShapeErrorFallback.tsx +4 -3
- package/src/lib/components/default-components/DefaultSvgDefs.tsx +1 -1
- package/src/lib/editor/Editor.ts +166 -69
- package/src/lib/editor/derivations/bindingsIndex.ts +1 -1
- package/src/lib/editor/derivations/parentsToChildren.ts +1 -1
- package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +1 -1
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +54 -74
- package/src/lib/editor/managers/ClickManager/ClickManager.ts +15 -65
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +43 -16
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +8 -5
- package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +4 -4
- package/src/lib/editor/managers/FocusManager/FocusManager.ts +1 -2
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +13 -9
- package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +32 -0
- package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +34 -4
- package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +9 -2
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +16 -14
- package/src/lib/editor/managers/TextManager/TextManager.ts +17 -2
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +12 -2
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +27 -2
- package/src/lib/editor/overlays/strokeShapeIndicators.ts +86 -0
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -0
- package/src/lib/editor/tools/StateNode.ts +0 -2
- package/src/lib/editor/types/event-types.ts +2 -6
- package/src/lib/hooks/usePresence.ts +2 -2
- package/src/lib/license/LicenseProvider.tsx +3 -1
- package/src/lib/primitives/utils.ts +1 -1
- package/src/lib/utils/dom.ts +5 -3
- package/src/version.ts +3 -3
- package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +0 -161
- package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +0 -7
- package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +0 -141
- package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +0 -7
- package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +0 -216
|
@@ -48,7 +48,11 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
48
48
|
switch (this.state) {
|
|
49
49
|
case HistoryRecorderState.Recording:
|
|
50
50
|
this.pendingDiff.apply(entry.changes)
|
|
51
|
-
this
|
|
51
|
+
// this interceptor runs for every store change, so skip the update when the
|
|
52
|
+
// redo stack is already empty
|
|
53
|
+
if (this.stacks.get().redos.length > 0) {
|
|
54
|
+
this.stacks.update(({ undos }) => ({ undos, redos: stack() }))
|
|
55
|
+
}
|
|
52
56
|
break
|
|
53
57
|
case HistoryRecorderState.RecordingPreserveRedoStack:
|
|
54
58
|
this.pendingDiff.apply(entry.changes)
|
|
@@ -79,6 +83,14 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
79
83
|
return this.stacks.get().redos.length
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
/** @internal */
|
|
87
|
+
private _isReplaying = false
|
|
88
|
+
|
|
89
|
+
/** @internal */
|
|
90
|
+
isReplaying() {
|
|
91
|
+
return this._isReplaying
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
/** @internal */
|
|
83
95
|
_isInBatch = false
|
|
84
96
|
|
|
@@ -115,7 +127,9 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
115
127
|
// History
|
|
116
128
|
_undo({ pushToRedoStack, toMark = undefined }: { pushToRedoStack: boolean; toMark?: string }) {
|
|
117
129
|
const previousState = this.state
|
|
130
|
+
const previousIsReplaying = this._isReplaying
|
|
118
131
|
this.state = HistoryRecorderState.Paused
|
|
132
|
+
this._isReplaying = true
|
|
119
133
|
try {
|
|
120
134
|
let { undos, redos } = this.stacks.get()
|
|
121
135
|
|
|
@@ -176,11 +190,11 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
176
190
|
this.pendingDiff.restore(pendingDiff)
|
|
177
191
|
return this
|
|
178
192
|
}
|
|
179
|
-
|
|
180
193
|
this.store.applyDiff(diffToUndo, { ignoreEphemeralKeys: true })
|
|
181
194
|
this.store.ensureStoreIsUsable()
|
|
182
195
|
this.stacks.set({ undos, redos })
|
|
183
196
|
} finally {
|
|
197
|
+
this._isReplaying = previousIsReplaying
|
|
184
198
|
this.state = previousState
|
|
185
199
|
}
|
|
186
200
|
|
|
@@ -195,7 +209,9 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
195
209
|
|
|
196
210
|
redo() {
|
|
197
211
|
const previousState = this.state
|
|
212
|
+
const previousIsReplaying = this._isReplaying
|
|
198
213
|
this.state = HistoryRecorderState.Paused
|
|
214
|
+
this._isReplaying = true
|
|
199
215
|
try {
|
|
200
216
|
this.flushPendingDiff()
|
|
201
217
|
|
|
@@ -224,11 +240,11 @@ export class HistoryManager<R extends UnknownRecord> {
|
|
|
224
240
|
break
|
|
225
241
|
}
|
|
226
242
|
}
|
|
227
|
-
|
|
228
243
|
this.store.applyDiff(diffToRedo, { ignoreEphemeralKeys: true })
|
|
229
244
|
this.store.ensureStoreIsUsable()
|
|
230
245
|
this.stacks.set({ undos, redos })
|
|
231
246
|
} finally {
|
|
247
|
+
this._isReplaying = previousIsReplaying
|
|
232
248
|
this.state = previousState
|
|
233
249
|
}
|
|
234
250
|
|
|
@@ -349,7 +365,16 @@ class PendingDiff<R extends UnknownRecord> {
|
|
|
349
365
|
|
|
350
366
|
apply(diff: RecordsDiff<R>) {
|
|
351
367
|
squashRecordDiffsMutable(this.diff, [diff])
|
|
352
|
-
|
|
368
|
+
// Recomputing emptiness from the accumulated diff is O(N) (for-in over a
|
|
369
|
+
// dictionary-mode object pays an O(N) key-collection prologue), and this runs on
|
|
370
|
+
// every history interceptor call — e.g. every input tick while resizing many
|
|
371
|
+
// shapes. Updates can never cancel out existing entries during a squash, so the
|
|
372
|
+
// full recompute is only needed when the incoming diff adds or removes records.
|
|
373
|
+
if (hasAnyKey(diff.added) || hasAnyKey(diff.removed)) {
|
|
374
|
+
this.isEmptyAtom.set(isRecordsDiffEmpty(this.diff))
|
|
375
|
+
} else if (this.isEmptyAtom.__unsafe__getWithoutCapture()) {
|
|
376
|
+
this.isEmptyAtom.set(!hasAnyKey(diff.updated))
|
|
377
|
+
}
|
|
353
378
|
}
|
|
354
379
|
|
|
355
380
|
debug() {
|
|
@@ -357,6 +382,11 @@ class PendingDiff<R extends UnknownRecord> {
|
|
|
357
382
|
}
|
|
358
383
|
}
|
|
359
384
|
|
|
385
|
+
function hasAnyKey(obj: object) {
|
|
386
|
+
for (const _ in obj) return true
|
|
387
|
+
return false
|
|
388
|
+
}
|
|
389
|
+
|
|
360
390
|
type Stack<T> = StackItem<T> | EmptyStackItem<T>
|
|
361
391
|
|
|
362
392
|
function stack<T>(): Stack<T> {
|
|
@@ -46,6 +46,10 @@ export class SpatialIndexManager {
|
|
|
46
46
|
|
|
47
47
|
private createSpatialIndexComputed() {
|
|
48
48
|
const shapeHistory = this.editor.store.query.filterHistory('shape')
|
|
49
|
+
// Binding changes can move a shape's derived bounds (e.g. creating or
|
|
50
|
+
// deleting an arrow binding relocates the arrow's body) without
|
|
51
|
+
// touching any shape record, so they must also invalidate the index.
|
|
52
|
+
const bindingHistory = this.editor.store.query.filterHistory('binding')
|
|
49
53
|
|
|
50
54
|
return computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {
|
|
51
55
|
if (isUninitialized(_prevValue)) {
|
|
@@ -53,8 +57,9 @@ export class SpatialIndexManager {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
const shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)
|
|
60
|
+
const bindingDiff = bindingHistory.getDiffSince(lastComputedEpoch)
|
|
56
61
|
|
|
57
|
-
if (shapeDiff === RESET_VALUE) {
|
|
62
|
+
if (shapeDiff === RESET_VALUE || bindingDiff === RESET_VALUE) {
|
|
58
63
|
return this.rebuildAndBumpEpoch()
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -63,8 +68,10 @@ export class SpatialIndexManager {
|
|
|
63
68
|
return this.rebuildAndBumpEpoch()
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
if (shapeDiff.length === 0) return this._boundsEpoch
|
|
71
|
+
if (shapeDiff.length === 0 && bindingDiff.length === 0) return this._boundsEpoch
|
|
67
72
|
|
|
73
|
+
// A binding-only diff passes an empty shape diff: step 1 is a no-op
|
|
74
|
+
// and the step-2 sweep re-checks the indexed bounds of every shape.
|
|
68
75
|
if (this.processIncrementalUpdate(shapeDiff)) {
|
|
69
76
|
this._boundsEpoch++
|
|
70
77
|
}
|
|
@@ -82,20 +82,22 @@ const mockEditor = {
|
|
|
82
82
|
getContainerDocument: vi.fn(() => mockDocument),
|
|
83
83
|
} as unknown as Editor
|
|
84
84
|
|
|
85
|
-
global.Range = vi.fn(()
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
85
|
+
global.Range = vi.fn(function () {
|
|
86
|
+
return {
|
|
87
|
+
setStart: vi.fn(),
|
|
88
|
+
setEnd: vi.fn(),
|
|
89
|
+
getClientRects: vi.fn(() => [
|
|
90
|
+
{
|
|
91
|
+
width: 10,
|
|
92
|
+
height: 16,
|
|
93
|
+
left: 0,
|
|
94
|
+
top: 0,
|
|
95
|
+
right: 10,
|
|
96
|
+
bottom: 16,
|
|
97
|
+
},
|
|
98
|
+
]),
|
|
99
|
+
}
|
|
100
|
+
}) as any
|
|
99
101
|
|
|
100
102
|
describe('TextManager', () => {
|
|
101
103
|
let textManager: TextManager
|
|
@@ -2,6 +2,21 @@ import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'
|
|
|
2
2
|
import { objectMapKeys } from '@tldraw/utils'
|
|
3
3
|
import type { Editor } from '../../Editor'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The whole-pixel line-height for a given font size and tldraw's unitless line-height
|
|
7
|
+
* multiplier. tldraw's theme stores line-height as a multiplier (e.g. 1.35); resolving it
|
|
8
|
+
* to a whole pixel keeps line spacing identical across rendering engines, which otherwise
|
|
9
|
+
* disagree on fractional line boxes (WebKit snaps them to whole pixels, Blink keeps the
|
|
10
|
+
* fraction) and let multi-line text drift apart. Apply it everywhere line-height is used —
|
|
11
|
+
* measurement, on-canvas render, and export — so geometry and rendering agree.
|
|
12
|
+
* See https://github.com/tldraw/tldraw/issues/8970.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export function resolveLineHeightPx(fontSize: number, lineHeight: number): number {
|
|
17
|
+
return Math.round(fontSize * lineHeight)
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
const fixNewLines = /\r?\n|\r/g
|
|
6
21
|
|
|
7
22
|
function normalizeTextForDom(text: string) {
|
|
@@ -154,7 +169,7 @@ export class TextManager {
|
|
|
154
169
|
'font-style': opts.fontStyle,
|
|
155
170
|
'font-weight': opts.fontWeight,
|
|
156
171
|
'font-size': opts.fontSize + 'px',
|
|
157
|
-
'line-height': opts.lineHeight
|
|
172
|
+
'line-height': `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,
|
|
158
173
|
padding: opts.padding,
|
|
159
174
|
'max-width': opts.maxWidth ? opts.maxWidth + 'px' : undefined,
|
|
160
175
|
'min-width': opts.minWidth ? opts.minWidth + 'px' : undefined,
|
|
@@ -382,7 +397,7 @@ export class TextManager {
|
|
|
382
397
|
'font-style': opts.fontStyle,
|
|
383
398
|
'font-weight': opts.fontWeight,
|
|
384
399
|
'font-size': opts.fontSize + 'px',
|
|
385
|
-
'line-height': opts.lineHeight
|
|
400
|
+
'line-height': `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,
|
|
386
401
|
width: `${elementWidth}px`,
|
|
387
402
|
height: 'min-content',
|
|
388
403
|
'text-align': textAlignmentsForLtr[opts.textAlign],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { atom } from '@tldraw/state'
|
|
2
|
+
import { createUserId } from '@tldraw/tlschema'
|
|
2
3
|
import { Mocked, vi } from 'vitest'
|
|
3
4
|
import { TLCurrentUser } from '../../../config/createTLCurrentUser'
|
|
4
5
|
import { TLUserPreferences, defaultUserPreferences } from '../../../config/TLUserPreferences'
|
|
@@ -296,8 +297,17 @@ describe('UserPreferencesManager', () => {
|
|
|
296
297
|
userPreferencesManager = new UserPreferencesManager(mockUser, 'light')
|
|
297
298
|
})
|
|
298
299
|
|
|
299
|
-
describe('
|
|
300
|
-
it('should return user id', () => {
|
|
300
|
+
describe('getExternalId / getRecordId', () => {
|
|
301
|
+
it('should return the raw external user id', () => {
|
|
302
|
+
expect(userPreferencesManager.getExternalId()).toBe(mockUserPreferences.id)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should return the prefixed record id', () => {
|
|
306
|
+
expect(userPreferencesManager.getRecordId()).toBe(createUserId(mockUserPreferences.id))
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('getId() still returns the external id', () => {
|
|
310
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
301
311
|
expect(userPreferencesManager.getId()).toBe(mockUserPreferences.id)
|
|
302
312
|
})
|
|
303
313
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { atom, computed } from '@tldraw/state'
|
|
2
|
+
import { createUserId, TLUserId } from '@tldraw/tlschema'
|
|
2
3
|
import { TLCurrentUser } from '../../../config/createTLCurrentUser'
|
|
3
4
|
import { TLUserPreferences, defaultUserPreferences } from '../../../config/TLUserPreferences'
|
|
4
5
|
import { getGlobalWindow } from '../../../utils/dom'
|
|
@@ -39,7 +40,7 @@ export class UserPreferencesManager {
|
|
|
39
40
|
}
|
|
40
41
|
@computed getUserPreferences() {
|
|
41
42
|
return {
|
|
42
|
-
id: this.
|
|
43
|
+
id: this.getExternalId(),
|
|
43
44
|
name: this.getName(),
|
|
44
45
|
locale: this.getLocale(),
|
|
45
46
|
color: this.getColor(),
|
|
@@ -89,10 +90,34 @@ export class UserPreferencesManager {
|
|
|
89
90
|
)
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The current user's raw, app-provided id — the value set in the user's
|
|
95
|
+
* {@link @tldraw/editor#TLUserPreferences}. Use this when you need the id your application
|
|
96
|
+
* assigned to the user. To compare against or look up store records, use
|
|
97
|
+
* {@link UserPreferencesManager.getRecordId} instead.
|
|
98
|
+
*/
|
|
99
|
+
@computed getExternalId(): string {
|
|
93
100
|
return this.user.userPreferences.get().id
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
/**
|
|
104
|
+
* @deprecated Use {@link UserPreferencesManager.getExternalId} for the raw app-provided id, or
|
|
105
|
+
* {@link UserPreferencesManager.getRecordId} for the prefixed `TLUserId` record id.
|
|
106
|
+
*/
|
|
107
|
+
@computed getId() {
|
|
108
|
+
return this.getExternalId()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The current user's id as a tldraw {@link @tldraw/tlschema#TLUserId} record id (prefixed
|
|
113
|
+
* with `user:`). Use this when comparing against or looking up store records, such as a
|
|
114
|
+
* presence record's `userId` or `followingUserId`. For the raw, app-provided id, use
|
|
115
|
+
* {@link UserPreferencesManager.getExternalId}.
|
|
116
|
+
*/
|
|
117
|
+
@computed getRecordId(): TLUserId {
|
|
118
|
+
return createUserId(this.getExternalId())
|
|
119
|
+
}
|
|
120
|
+
|
|
96
121
|
@computed getName() {
|
|
97
122
|
return this.user.userPreferences.get().name?.trim() ?? defaultUserPreferences.name
|
|
98
123
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createComputedCache } from '@tldraw/store'
|
|
2
|
+
import { TLShape, TLShapeId } from '@tldraw/tlschema'
|
|
3
|
+
import type { Editor } from '../Editor'
|
|
4
|
+
|
|
5
|
+
const indicatorPathCache = createComputedCache(
|
|
6
|
+
'shapeIndicatorPath',
|
|
7
|
+
(editor: Editor, shape: TLShape) => {
|
|
8
|
+
const util = editor.getShapeUtil(shape)
|
|
9
|
+
return util.getIndicatorPath(shape)
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
areRecordsEqual(a, b) {
|
|
13
|
+
return a.props === b.props
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Combine every batchable shape indicator into a single page-space `Path2D` and
|
|
20
|
+
* emit one stroke call. Shapes whose indicator needs an evenodd clip (e.g.
|
|
21
|
+
* arrows with labels or complex arrowheads) can't be batched — they still
|
|
22
|
+
* stroke individually inside a save/restore with `ctx.clip` applied.
|
|
23
|
+
*
|
|
24
|
+
* Shared by any overlay util that paints shape indicators (e.g. collaborator
|
|
25
|
+
* selections).
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
export function strokeShapeIndicators(
|
|
30
|
+
editor: Editor,
|
|
31
|
+
ctx: CanvasRenderingContext2D,
|
|
32
|
+
shapeIds: TLShapeId[]
|
|
33
|
+
): void {
|
|
34
|
+
if (shapeIds.length === 0) return
|
|
35
|
+
|
|
36
|
+
const batched = new Path2D()
|
|
37
|
+
|
|
38
|
+
for (const shapeId of shapeIds) {
|
|
39
|
+
const shape = editor.getShape(shapeId)
|
|
40
|
+
if (!shape || shape.isLocked) continue
|
|
41
|
+
|
|
42
|
+
const pageTransform = editor.getShapePageTransform(shape)
|
|
43
|
+
if (!pageTransform) continue
|
|
44
|
+
|
|
45
|
+
const indicatorPath = indicatorPathCache.get(editor, shape.id)
|
|
46
|
+
if (!indicatorPath) continue
|
|
47
|
+
|
|
48
|
+
if (indicatorPath instanceof Path2D) {
|
|
49
|
+
batched.addPath(indicatorPath, pageTransform)
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { path, clipPath, additionalPaths } = indicatorPath
|
|
54
|
+
|
|
55
|
+
if (!clipPath) {
|
|
56
|
+
batched.addPath(path, pageTransform)
|
|
57
|
+
if (additionalPaths) {
|
|
58
|
+
for (const p of additionalPaths) batched.addPath(p, pageTransform)
|
|
59
|
+
}
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Clipped case: fall back to an individual stroke. Rare (arrows with
|
|
64
|
+
// labels / complex arrowheads), so the extra save/restore/stroke
|
|
65
|
+
// pair per such shape isn't worth batching away.
|
|
66
|
+
ctx.save()
|
|
67
|
+
ctx.transform(
|
|
68
|
+
pageTransform.a,
|
|
69
|
+
pageTransform.b,
|
|
70
|
+
pageTransform.c,
|
|
71
|
+
pageTransform.d,
|
|
72
|
+
pageTransform.e,
|
|
73
|
+
pageTransform.f
|
|
74
|
+
)
|
|
75
|
+
ctx.save()
|
|
76
|
+
ctx.clip(clipPath, 'evenodd')
|
|
77
|
+
ctx.stroke(path)
|
|
78
|
+
ctx.restore()
|
|
79
|
+
if (additionalPaths) {
|
|
80
|
+
for (const p of additionalPaths) ctx.stroke(p)
|
|
81
|
+
}
|
|
82
|
+
ctx.restore()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
ctx.stroke(batched)
|
|
86
|
+
}
|
|
@@ -77,6 +77,10 @@ export class Pointing extends StateNode {
|
|
|
77
77
|
this.cancel()
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
override onLongPress() {
|
|
81
|
+
if (this.editor.getInstanceState().isCoarsePointer) this.cancel()
|
|
82
|
+
}
|
|
83
|
+
|
|
80
84
|
complete() {
|
|
81
85
|
const originPagePoint = this.editor.inputs.getOriginPagePoint()
|
|
82
86
|
|
|
@@ -268,8 +268,6 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
|
|
268
268
|
onLongPress?(info: TLPointerEventInfo): void
|
|
269
269
|
onPointerUp?(info: TLPointerEventInfo): void
|
|
270
270
|
onDoubleClick?(info: TLClickEventInfo): void
|
|
271
|
-
onTripleClick?(info: TLClickEventInfo): void
|
|
272
|
-
onQuadrupleClick?(info: TLClickEventInfo): void
|
|
273
271
|
onRightClick?(info: TLPointerEventInfo): void
|
|
274
272
|
onMiddleClick?(info: TLPointerEventInfo): void
|
|
275
273
|
onKeyDown?(info: TLKeyboardEventInfo): void
|
|
@@ -24,7 +24,7 @@ export type TLPointerEventName =
|
|
|
24
24
|
| 'middle_click'
|
|
25
25
|
|
|
26
26
|
/** @public */
|
|
27
|
-
export type TLCLickEventName = 'double_click'
|
|
27
|
+
export type TLCLickEventName = 'double_click'
|
|
28
28
|
|
|
29
29
|
/** @public */
|
|
30
30
|
export type TLPinchEventName = 'pinch_start' | 'pinch' | 'pinch_end'
|
|
@@ -72,7 +72,7 @@ export type TLClickEventInfo = TLBaseEventInfo & {
|
|
|
72
72
|
point: VecLike
|
|
73
73
|
pointerId: number
|
|
74
74
|
button: number
|
|
75
|
-
phase: 'down' | 'up' | 'settle'
|
|
75
|
+
phase: 'down' | 'up' | 'settle-down' | 'settle-up'
|
|
76
76
|
} & TLPointerEventTarget
|
|
77
77
|
|
|
78
78
|
/** @public */
|
|
@@ -173,8 +173,6 @@ export interface TLEventHandlers {
|
|
|
173
173
|
onLongPress: TLPointerEvent
|
|
174
174
|
onRightClick: TLPointerEvent
|
|
175
175
|
onDoubleClick: TLClickEvent
|
|
176
|
-
onTripleClick: TLClickEvent
|
|
177
|
-
onQuadrupleClick: TLClickEvent
|
|
178
176
|
onMiddleClick: TLPointerEvent
|
|
179
177
|
onPointerUp: TLPointerEvent
|
|
180
178
|
onKeyDown: TLKeyboardEvent
|
|
@@ -206,7 +204,5 @@ export const EVENT_NAME_MAP: Record<
|
|
|
206
204
|
complete: 'onComplete',
|
|
207
205
|
interrupt: 'onInterrupt',
|
|
208
206
|
double_click: 'onDoubleClick',
|
|
209
|
-
triple_click: 'onTripleClick',
|
|
210
|
-
quadruple_click: 'onQuadrupleClick',
|
|
211
207
|
tick: 'onTick',
|
|
212
208
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useValue } from '@tldraw/state-react'
|
|
2
|
-
import { TLInstancePresence } from '@tldraw/tlschema'
|
|
2
|
+
import { TLInstancePresence, TLUserId } from '@tldraw/tlschema'
|
|
3
3
|
import { useEditor } from './useEditor'
|
|
4
4
|
|
|
5
5
|
// TODO: maybe move this to a computed property on the App class?
|
|
@@ -7,7 +7,7 @@ import { useEditor } from './useEditor'
|
|
|
7
7
|
* @returns The latest presence of the user matching userId
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
|
-
export function usePresence(userId:
|
|
10
|
+
export function usePresence(userId: TLUserId): TLInstancePresence | null {
|
|
11
11
|
const editor = useEditor()
|
|
12
12
|
|
|
13
13
|
const latestPresence = useValue(
|
|
@@ -6,7 +6,9 @@ import { LicenseManager } from './LicenseManager'
|
|
|
6
6
|
export const LicenseContext = createContext({} as LicenseManager)
|
|
7
7
|
|
|
8
8
|
/** @internal */
|
|
9
|
-
export
|
|
9
|
+
export function useLicenseContext() {
|
|
10
|
+
return useContext(LicenseContext)
|
|
11
|
+
}
|
|
10
12
|
|
|
11
13
|
function shouldHideEditorAfterDelay(licenseState: string): boolean {
|
|
12
14
|
return licenseState === 'expired' || licenseState === 'unlicensed-production'
|
|
@@ -363,7 +363,7 @@ export function toFixed(v: number) {
|
|
|
363
363
|
* Check if a float is safe to use. ie: Not too big or small.
|
|
364
364
|
* @public
|
|
365
365
|
*/
|
|
366
|
-
export
|
|
366
|
+
export function isSafeFloat(n: number) {
|
|
367
367
|
return Math.abs(n) < Number.MAX_SAFE_INTEGER
|
|
368
368
|
}
|
|
369
369
|
|
package/src/lib/utils/dom.ts
CHANGED
|
@@ -86,14 +86,16 @@ export function releasePointerCapture(
|
|
|
86
86
|
*
|
|
87
87
|
* @public
|
|
88
88
|
*/
|
|
89
|
-
export
|
|
89
|
+
export function stopEventPropagation(e: any) {
|
|
90
|
+
return e.stopPropagation()
|
|
91
|
+
}
|
|
90
92
|
|
|
91
93
|
/** @internal */
|
|
92
|
-
export
|
|
94
|
+
export function setStyleProperty(
|
|
93
95
|
elm: HTMLElement | null,
|
|
94
96
|
property: string,
|
|
95
97
|
value: string | number
|
|
96
|
-
)
|
|
98
|
+
) {
|
|
97
99
|
if (!elm) return
|
|
98
100
|
elm.style.setProperty(property, String(value))
|
|
99
101
|
}
|
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.
|
|
4
|
+
export const version = '5.2.0-canary.05c017c18b15'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2026-05-06T16:28:18.473Z',
|
|
7
|
-
minor: '2026-06-
|
|
8
|
-
patch: '2026-06-
|
|
7
|
+
minor: '2026-06-16T13:24:21.828Z',
|
|
8
|
+
patch: '2026-06-16T13:24:21.828Z',
|
|
9
9
|
}
|
|
@@ -1,161 +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 ShapeIndicatorOverlayUtil_exports = {};
|
|
20
|
-
__export(ShapeIndicatorOverlayUtil_exports, {
|
|
21
|
-
ShapeIndicatorOverlayUtil: () => ShapeIndicatorOverlayUtil,
|
|
22
|
-
strokeShapeIndicators: () => strokeShapeIndicators
|
|
23
|
-
});
|
|
24
|
-
module.exports = __toCommonJS(ShapeIndicatorOverlayUtil_exports);
|
|
25
|
-
var import_state = require("@tldraw/state");
|
|
26
|
-
var import_store = require("@tldraw/store");
|
|
27
|
-
var import_OverlayUtil = require("./OverlayUtil");
|
|
28
|
-
const indicatorPathCache = (0, import_store.createComputedCache)(
|
|
29
|
-
"shapeIndicatorPath",
|
|
30
|
-
(editor, shape) => {
|
|
31
|
-
const util = editor.getShapeUtil(shape);
|
|
32
|
-
return util.getIndicatorPath(shape);
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
areRecordsEqual(a, b) {
|
|
36
|
-
return a.props === b.props;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
function strokeShapeIndicators(editor, ctx, shapeIds) {
|
|
41
|
-
if (shapeIds.length === 0) return;
|
|
42
|
-
const batched = new Path2D();
|
|
43
|
-
for (const shapeId of shapeIds) {
|
|
44
|
-
const shape = editor.getShape(shapeId);
|
|
45
|
-
if (!shape || shape.isLocked) continue;
|
|
46
|
-
const pageTransform = editor.getShapePageTransform(shape);
|
|
47
|
-
if (!pageTransform) continue;
|
|
48
|
-
const indicatorPath = indicatorPathCache.get(editor, shape.id);
|
|
49
|
-
if (!indicatorPath) continue;
|
|
50
|
-
if (indicatorPath instanceof Path2D) {
|
|
51
|
-
batched.addPath(indicatorPath, pageTransform);
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
const { path, clipPath, additionalPaths } = indicatorPath;
|
|
55
|
-
if (!clipPath) {
|
|
56
|
-
batched.addPath(path, pageTransform);
|
|
57
|
-
if (additionalPaths) {
|
|
58
|
-
for (const p of additionalPaths) batched.addPath(p, pageTransform);
|
|
59
|
-
}
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
ctx.save();
|
|
63
|
-
ctx.transform(
|
|
64
|
-
pageTransform.a,
|
|
65
|
-
pageTransform.b,
|
|
66
|
-
pageTransform.c,
|
|
67
|
-
pageTransform.d,
|
|
68
|
-
pageTransform.e,
|
|
69
|
-
pageTransform.f
|
|
70
|
-
);
|
|
71
|
-
ctx.save();
|
|
72
|
-
ctx.clip(clipPath, "evenodd");
|
|
73
|
-
ctx.stroke(path);
|
|
74
|
-
ctx.restore();
|
|
75
|
-
if (additionalPaths) {
|
|
76
|
-
for (const p of additionalPaths) ctx.stroke(p);
|
|
77
|
-
}
|
|
78
|
-
ctx.restore();
|
|
79
|
-
}
|
|
80
|
-
ctx.stroke(batched);
|
|
81
|
-
}
|
|
82
|
-
class ShapeIndicatorOverlayUtil extends import_OverlayUtil.OverlayUtil {
|
|
83
|
-
static type = "shape_indicator";
|
|
84
|
-
options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 };
|
|
85
|
-
// Narrow projection of instance state. Reading the full record would
|
|
86
|
-
// re-fire getOverlays on every cursor move / brush update; gating on these
|
|
87
|
-
// three booleans means we only re-fire when one of them actually flips.
|
|
88
|
-
_instanceFlags$ = (0, import_state.computed)(
|
|
89
|
-
"shape indicator instance flags",
|
|
90
|
-
() => {
|
|
91
|
-
const i = this.editor.getInstanceState();
|
|
92
|
-
return {
|
|
93
|
-
isChangingStyle: i.isChangingStyle,
|
|
94
|
-
isHoveringCanvas: i.isHoveringCanvas,
|
|
95
|
-
isCoarsePointer: i.isCoarsePointer
|
|
96
|
-
};
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
isEqual: (a, b) => a.isChangingStyle === b.isChangingStyle && a.isHoveringCanvas === b.isHoveringCanvas && a.isCoarsePointer === b.isCoarsePointer
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
isActive() {
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
getOverlays() {
|
|
106
|
-
const editor = this.editor;
|
|
107
|
-
const renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id));
|
|
108
|
-
const idsToDisplay = [];
|
|
109
|
-
const { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get();
|
|
110
|
-
const isIdleOrEditing = editor.isInAny("select.idle", "select.editing_shape");
|
|
111
|
-
const isInSelectState = editor.isInAny(
|
|
112
|
-
"select.brushing",
|
|
113
|
-
"select.scribble_brushing",
|
|
114
|
-
"select.pointing_shape",
|
|
115
|
-
"select.pointing_selection",
|
|
116
|
-
"select.pointing_handle"
|
|
117
|
-
);
|
|
118
|
-
if (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {
|
|
119
|
-
for (const id of editor.getSelectedShapeIds()) {
|
|
120
|
-
if (renderingShapeIds.has(id)) idsToDisplay.push(id);
|
|
121
|
-
}
|
|
122
|
-
if (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {
|
|
123
|
-
const hovered = editor.getHoveredShapeId();
|
|
124
|
-
if (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {
|
|
125
|
-
idsToDisplay.push(hovered);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const hintingShapeIds = [];
|
|
130
|
-
for (const id of editor.getHintingShapeIds()) {
|
|
131
|
-
if (renderingShapeIds.has(id)) hintingShapeIds.push(id);
|
|
132
|
-
}
|
|
133
|
-
if (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
return [
|
|
137
|
-
{
|
|
138
|
-
id: "shape_indicator",
|
|
139
|
-
type: "shape_indicator",
|
|
140
|
-
props: { idsToDisplay, hintingShapeIds }
|
|
141
|
-
}
|
|
142
|
-
];
|
|
143
|
-
}
|
|
144
|
-
render(ctx, overlays) {
|
|
145
|
-
const overlay = overlays[0];
|
|
146
|
-
if (!overlay) return;
|
|
147
|
-
const editor = this.editor;
|
|
148
|
-
const zoom = editor.getZoomLevel();
|
|
149
|
-
const { idsToDisplay, hintingShapeIds } = overlay.props;
|
|
150
|
-
ctx.lineCap = "round";
|
|
151
|
-
ctx.lineJoin = "round";
|
|
152
|
-
ctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke;
|
|
153
|
-
ctx.lineWidth = this.options.lineWidth / zoom;
|
|
154
|
-
strokeShapeIndicators(editor, ctx, idsToDisplay);
|
|
155
|
-
if (hintingShapeIds.length > 0) {
|
|
156
|
-
ctx.lineWidth = this.options.hintedLineWidth / zoom;
|
|
157
|
-
strokeShapeIndicators(editor, ctx, hintingShapeIds);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
//# sourceMappingURL=ShapeIndicatorOverlayUtil.js.map
|