@tldraw/editor 5.1.0 → 5.2.0-canary.019da1aa690a
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 +52 -50
- package/dist-cjs/index.js +4 -4
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +8 -5
- package/dist-cjs/lib/components/MenuClickCapture.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 +125 -56
- 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/InputsManager/InputsManager.js +14 -3
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.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/TickManager/TickManager.js +0 -1
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.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/useCanvasEvents.js +14 -7
- package/dist-cjs/lib/hooks/useCanvasEvents.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/lib/utils/getPointerInfo.js +2 -1
- package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
- package/dist-cjs/lib/utils/pointer.js +32 -0
- package/dist-cjs/lib/utils/pointer.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +52 -50
- package/dist-esm/index.mjs +5 -7
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +8 -5
- package/dist-esm/lib/components/MenuClickCapture.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 +125 -56
- 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/InputsManager/InputsManager.mjs +14 -3
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.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/TickManager/TickManager.mjs +0 -1
- package/dist-esm/lib/editor/managers/TickManager/TickManager.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/useCanvasEvents.mjs +14 -7
- package/dist-esm/lib/hooks/useCanvasEvents.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/lib/utils/getPointerInfo.mjs +2 -1
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/lib/utils/pointer.mjs +12 -0
- package/dist-esm/lib/utils/pointer.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +5 -3
- package/package.json +11 -8
- package/src/index.ts +2 -5
- package/src/lib/components/MenuClickCapture.tsx +8 -4
- 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 +174 -73
- 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/InputsManager/InputsManager.test.ts +61 -0
- package/src/lib/editor/managers/InputsManager/InputsManager.ts +16 -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/TickManager/TickManager.test.ts +0 -40
- package/src/lib/editor/managers/TickManager/TickManager.ts +0 -1
- 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/useCanvasEvents.ts +19 -12
- 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/lib/utils/getPointerInfo.ts +2 -1
- package/src/lib/utils/pointer.test.ts +48 -0
- package/src/lib/utils/pointer.ts +18 -0
- 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
|
@@ -11,6 +11,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
11
11
|
import { atom, computed, unsafe__withoutCapture } from "@tldraw/state";
|
|
12
12
|
import { AtomSet } from "@tldraw/store";
|
|
13
13
|
import { TLINSTANCE_ID, TLPOINTER_ID } from "@tldraw/tlschema";
|
|
14
|
+
import { bind } from "@tldraw/utils";
|
|
14
15
|
import { INTERNAL_POINTER_IDS } from "../../../constants.mjs";
|
|
15
16
|
import { Vec } from "../../../primitives/Vec.mjs";
|
|
16
17
|
import { isAccelKey } from "../../../utils/keyboard.mjs";
|
|
@@ -19,8 +20,16 @@ const POINTER_VELOCITY_REFERENCE_SMOOTHING = 0.5;
|
|
|
19
20
|
class InputsManager {
|
|
20
21
|
constructor(editor) {
|
|
21
22
|
this.editor = editor;
|
|
23
|
+
this.editor.on("frame", this._onFrame);
|
|
22
24
|
}
|
|
23
25
|
editor;
|
|
26
|
+
/** @internal */
|
|
27
|
+
dispose() {
|
|
28
|
+
this.editor.off("frame", this._onFrame);
|
|
29
|
+
}
|
|
30
|
+
_onFrame(elapsed) {
|
|
31
|
+
this.updatePointerVelocity(elapsed);
|
|
32
|
+
}
|
|
24
33
|
_originPagePoint = atom("originPagePoint", new Vec());
|
|
25
34
|
/**
|
|
26
35
|
* The most recent pointer down's position in the current page space.
|
|
@@ -120,8 +129,7 @@ class InputsManager {
|
|
|
120
129
|
return this.getPointerVelocity();
|
|
121
130
|
}
|
|
122
131
|
/**
|
|
123
|
-
* Normally you shouldn't need to set the pointer velocity directly
|
|
124
|
-
* However, this is currently used in tests to fake pointer velocity.
|
|
132
|
+
* Normally you shouldn't need to set the pointer velocity directly. Used in tests to fake pointer velocity.
|
|
125
133
|
* @param pointerVelocity - The pointer velocity.
|
|
126
134
|
* @internal
|
|
127
135
|
*/
|
|
@@ -441,7 +449,7 @@ class InputsManager {
|
|
|
441
449
|
*/
|
|
442
450
|
_velocityPrevPoint = new Vec();
|
|
443
451
|
/**
|
|
444
|
-
* Update the pointer velocity based on elapsed time. Called
|
|
452
|
+
* Update the pointer velocity based on elapsed time. Called each frame.
|
|
445
453
|
* @param elapsed - The time elapsed since the last tick in milliseconds.
|
|
446
454
|
* @internal
|
|
447
455
|
*/
|
|
@@ -541,6 +549,9 @@ class InputsManager {
|
|
|
541
549
|
};
|
|
542
550
|
}
|
|
543
551
|
}
|
|
552
|
+
__decorateClass([
|
|
553
|
+
bind
|
|
554
|
+
], InputsManager.prototype, "_onFrame", 1);
|
|
544
555
|
__decorateClass([
|
|
545
556
|
computed
|
|
546
557
|
], InputsManager.prototype, "_getHasCollaborators", 1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/InputsManager/InputsManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { atom, computed, unsafe__withoutCapture } from '@tldraw/state'\nimport { AtomSet } from '@tldraw/store'\nimport { TLINSTANCE_ID, TLPOINTER_ID } from '@tldraw/tlschema'\nimport { INTERNAL_POINTER_IDS } from '../../../constants'\nimport { Vec } from '../../../primitives/Vec'\nimport { isAccelKey } from '../../../utils/keyboard'\nimport type { Editor } from '../../Editor'\nimport { TLPinchEventInfo, TLPointerEventInfo, TLWheelEventInfo } from '../../types/event-types'\n\nconst POINTER_VELOCITY_REFERENCE_INTERVAL_MS = 16\nconst POINTER_VELOCITY_REFERENCE_SMOOTHING = 0.5\n\n/** @public */\nexport class InputsManager {\n\tconstructor(private readonly editor: Editor) {}\n\n\tprivate _originPagePoint = atom<Vec>('originPagePoint', new Vec())\n\t/**\n\t * The most recent pointer down's position in the current page space.\n\t */\n\tgetOriginPagePoint() {\n\t\treturn this._originPagePoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getOriginPagePoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget originPagePoint() {\n\t\treturn this.getOriginPagePoint()\n\t}\n\n\tprivate _originScreenPoint = atom<Vec>('originScreenPoint', new Vec())\n\t/**\n\t * The most recent pointer down's position in screen space.\n\t */\n\tgetOriginScreenPoint() {\n\t\treturn this._originScreenPoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getOriginScreenPoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget originScreenPoint() {\n\t\treturn this.getOriginScreenPoint()\n\t}\n\n\tprivate _previousPagePoint = atom<Vec>('previousPagePoint', new Vec())\n\t/**\n\t * The previous pointer position in the current page space.\n\t */\n\tgetPreviousPagePoint() {\n\t\treturn this._previousPagePoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getPreviousPagePoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget previousPagePoint() {\n\t\treturn this.getPreviousPagePoint()\n\t}\n\n\tprivate _previousScreenPoint = atom<Vec>('previousScreenPoint', new Vec())\n\t/**\n\t * The previous pointer position in screen space.\n\t */\n\tgetPreviousScreenPoint() {\n\t\treturn this._previousScreenPoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getPreviousScreenPoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget previousScreenPoint() {\n\t\treturn this.getPreviousScreenPoint()\n\t}\n\n\tprivate _currentPagePoint = atom<Vec>('currentPagePoint', new Vec())\n\t/**\n\t * The most recent pointer position in the current page space.\n\t */\n\tgetCurrentPagePoint() {\n\t\treturn this._currentPagePoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getCurrentPagePoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget currentPagePoint() {\n\t\treturn this.getCurrentPagePoint()\n\t}\n\n\tprivate _currentScreenPoint = atom<Vec>('currentScreenPoint', new Vec())\n\t/**\n\t * The most recent pointer position in screen space.\n\t */\n\tgetCurrentScreenPoint() {\n\t\treturn this._currentScreenPoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getCurrentScreenPoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget currentScreenPoint() {\n\t\treturn this.getCurrentScreenPoint()\n\t}\n\n\tprivate _pointerVelocity = atom<Vec>('pointerVelocity', new Vec())\n\t/**\n\t * Velocity of mouse pointer, in pixels per millisecond.\n\t */\n\tgetPointerVelocity() {\n\t\treturn this._pointerVelocity.get()\n\t}\n\t/**\n\t * @deprecated Use `getPointerVelocity()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget pointerVelocity() {\n\t\treturn this.getPointerVelocity()\n\t}\n\n\t/**\n\t * Normally you shouldn't need to set the pointer velocity directly, this is set by the tick manager.\n\t * However, this is currently used in tests to fake pointer velocity.\n\t * @param pointerVelocity - The pointer velocity.\n\t * @internal\n\t */\n\tsetPointerVelocity(pointerVelocity: Vec) {\n\t\tthis._pointerVelocity.set(pointerVelocity)\n\t}\n\n\t/**\n\t * A set containing the currently pressed keys.\n\t */\n\treadonly keys = new AtomSet<string>('keys')\n\n\t/**\n\t * A set containing the currently pressed buttons.\n\t */\n\treadonly buttons = new AtomSet<number>('buttons')\n\n\tprivate _isPen = atom<boolean>('isPen', false)\n\n\t/**\n\t * Whether the input is from a pen.\n\t */\n\tgetIsPen() {\n\t\treturn this._isPen.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPen()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPen() {\n\t\treturn this.getIsPen()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPen(isPen: boolean) {\n\t\tthis.setIsPen(isPen)\n\t}\n\t/**\n\t * @param isPen - Whether the input is from a pen.\n\t */\n\tsetIsPen(isPen: boolean) {\n\t\tthis._isPen.set(isPen)\n\t}\n\n\tprivate _shiftKey = atom<boolean>('shiftKey', false)\n\t/**\n\t * Whether the shift key is currently pressed.\n\t */\n\tgetShiftKey() {\n\t\treturn this._shiftKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getShiftKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget shiftKey() {\n\t\treturn this.getShiftKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset shiftKey(shiftKey: boolean) {\n\t\tthis.setShiftKey(shiftKey)\n\t}\n\t/**\n\t * @param shiftKey - Whether the shift key is pressed.\n\t * @internal\n\t */\n\tsetShiftKey(shiftKey: boolean) {\n\t\tthis._shiftKey.set(shiftKey)\n\t}\n\n\tprivate _metaKey = atom<boolean>('metaKey', false)\n\t/**\n\t * Whether the meta key is currently pressed.\n\t */\n\tgetMetaKey() {\n\t\treturn this._metaKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getMetaKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget metaKey() {\n\t\treturn this.getMetaKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset metaKey(metaKey: boolean) {\n\t\tthis.setMetaKey(metaKey)\n\t}\n\t/**\n\t * @param metaKey - Whether the meta key is pressed.\n\t * @internal\n\t */\n\tsetMetaKey(metaKey: boolean) {\n\t\tthis._metaKey.set(metaKey)\n\t}\n\n\tprivate _ctrlKey = atom<boolean>('ctrlKey', false)\n\t/**\n\t * Whether the ctrl or command key is currently pressed.\n\t */\n\tgetCtrlKey() {\n\t\treturn this._ctrlKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getCtrlKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget ctrlKey() {\n\t\treturn this.getCtrlKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset ctrlKey(ctrlKey: boolean) {\n\t\tthis.setCtrlKey(ctrlKey)\n\t}\n\t/**\n\t * @param ctrlKey - Whether the ctrl key is pressed.\n\t * @internal\n\t */\n\tsetCtrlKey(ctrlKey: boolean) {\n\t\tthis._ctrlKey.set(ctrlKey)\n\t}\n\n\tprivate _altKey = atom<boolean>('altKey', false)\n\t/**\n\t * Whether the alt or option key is currently pressed.\n\t */\n\tgetAltKey() {\n\t\treturn this._altKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getAltKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget altKey() {\n\t\treturn this.getAltKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset altKey(altKey: boolean) {\n\t\tthis.setAltKey(altKey)\n\t}\n\t/**\n\t * @param altKey - Whether the alt key is pressed.\n\t * @internal\n\t */\n\tsetAltKey(altKey: boolean) {\n\t\tthis._altKey.set(altKey)\n\t}\n\n\t/**\n\t * Is the accelerator key (cmd on mac, ctrl elsewhere) currently pressed.\n\t */\n\tgetAccelKey() {\n\t\treturn isAccelKey({ metaKey: this.getMetaKey(), ctrlKey: this.getCtrlKey() })\n\t}\n\t/**\n\t * @deprecated Use `getAccelKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget accelKey() {\n\t\treturn this.getAccelKey()\n\t}\n\n\tprivate _isDragging = atom<boolean>('isDragging', false)\n\t/**\n\t * Whether the user is dragging.\n\t */\n\tgetIsDragging() {\n\t\treturn this._isDragging.get()\n\t}\n\t/**\n\t * Soon to be deprecated, use `getIsDragging()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isDragging() {\n\t\treturn this.getIsDragging()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isDragging(isDragging: boolean) {\n\t\tthis.setIsDragging(isDragging)\n\t}\n\t/**\n\t * @param isDragging - Whether the user is dragging.\n\t */\n\tsetIsDragging(isDragging: boolean) {\n\t\tthis._isDragging.set(isDragging)\n\t}\n\n\tprivate _isPointing = atom<boolean>('isPointing', false)\n\t/**\n\t * Whether the user is pointing.\n\t */\n\tgetIsPointing() {\n\t\treturn this._isPointing.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPointing()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPointing() {\n\t\treturn this.getIsPointing()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPointing(isPointing: boolean) {\n\t\tthis.setIsPointing(isPointing)\n\t}\n\t/**\n\t * @param isPointing - Whether the user is pointing.\n\t * @internal\n\t */\n\tsetIsPointing(isPointing: boolean) {\n\t\tthis._isPointing.set(isPointing)\n\t}\n\n\tprivate _isRightPointing = atom<boolean>('isRightPointing', false)\n\t/**\n\t * Whether the user is right-click pointing (before drag threshold).\n\t */\n\tgetIsRightPointing() {\n\t\treturn this._isRightPointing.get()\n\t}\n\t/** @internal */\n\tsetIsRightPointing(isRightPointing: boolean) {\n\t\tthis._isRightPointing.set(isRightPointing)\n\t}\n\n\tprivate _isPinching = atom<boolean>('isPinching', false)\n\t/**\n\t * Whether the user is pinching.\n\t */\n\tgetIsPinching() {\n\t\treturn this._isPinching.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPinching()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPinching() {\n\t\treturn this.getIsPinching()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPinching(isPinching: boolean) {\n\t\tthis.setIsPinching(isPinching)\n\t}\n\t/**\n\t * @param isPinching - Whether the user is pinching.\n\t * @internal\n\t */\n\tsetIsPinching(isPinching: boolean) {\n\t\tthis._isPinching.set(isPinching)\n\t}\n\n\tprivate _isEditing = atom<boolean>('isEditing', false)\n\t/**\n\t * Whether the user is editing.\n\t */\n\tgetIsEditing() {\n\t\treturn this._isEditing.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsEditing()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isEditing() {\n\t\treturn this.getIsEditing()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isEditing(isEditing: boolean) {\n\t\tthis.setIsEditing(isEditing)\n\t}\n\t/**\n\t * @param isEditing - Whether the user is editing.\n\t */\n\tsetIsEditing(isEditing: boolean) {\n\t\tthis._isEditing.set(isEditing)\n\t}\n\n\tprivate _isPanning = atom<boolean>('isPanning', false)\n\t/**\n\t * Whether the user is panning.\n\t */\n\tgetIsPanning() {\n\t\treturn this._isPanning.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPanning()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPanning() {\n\t\treturn this.getIsPanning()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPanning(isPanning: boolean) {\n\t\tthis.setIsPanning(isPanning)\n\t}\n\t/**\n\t * @param isPanning - Whether the user is panning.\n\t * @internal\n\t */\n\tsetIsPanning(isPanning: boolean) {\n\t\tthis._isPanning.set(isPanning)\n\t}\n\n\tprivate _isSpacebarPanning = atom<boolean>('isSpacebarPanning', false)\n\t/**\n\t * Whether the user is spacebar panning.\n\t */\n\tgetIsSpacebarPanning() {\n\t\treturn this._isSpacebarPanning.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsSpacebarPanning()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isSpacebarPanning() {\n\t\treturn this.getIsSpacebarPanning()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isSpacebarPanning(isSpacebarPanning: boolean) {\n\t\tthis.setIsSpacebarPanning(isSpacebarPanning)\n\t}\n\t/**\n\t * @param isSpacebarPanning - Whether the user is spacebar panning.\n\t * @internal\n\t */\n\tsetIsSpacebarPanning(isSpacebarPanning: boolean) {\n\t\tthis._isSpacebarPanning.set(isSpacebarPanning)\n\t}\n\n\t@computed private _getHasCollaborators() {\n\t\treturn this.editor.getCollaborators().length > 0 // could we do this more efficiently?\n\t}\n\n\t/**\n\t * The previous point used for velocity calculation (updated each tick, not each pointer event).\n\t * @internal\n\t */\n\tprivate _velocityPrevPoint = new Vec()\n\n\t/**\n\t * Update the pointer velocity based on elapsed time. Called by the tick manager.\n\t * @param elapsed - The time elapsed since the last tick in milliseconds.\n\t * @internal\n\t */\n\tupdatePointerVelocity(elapsed: number) {\n\t\tconst currentScreenPoint = this.getCurrentScreenPoint()\n\t\tconst pointerVelocity = this.getPointerVelocity()\n\n\t\tif (elapsed === 0) return\n\n\t\tconst delta = Vec.Sub(currentScreenPoint, this._velocityPrevPoint)\n\t\tthis._velocityPrevPoint = currentScreenPoint.clone()\n\n\t\tconst length = delta.len()\n\t\tconst direction = length ? delta.div(length) : new Vec(0, 0)\n\n\t\t// Preserve the old 16ms smoothing with alpha = 1 - (1 - 0.5)^(elapsed / 16).\n\t\tconst smoothing =\n\t\t\t1 -\n\t\t\tMath.pow(\n\t\t\t\t1 - POINTER_VELOCITY_REFERENCE_SMOOTHING,\n\t\t\t\telapsed / POINTER_VELOCITY_REFERENCE_INTERVAL_MS\n\t\t\t)\n\t\tconst next = pointerVelocity.clone().lrp(direction.mul(length / elapsed), smoothing)\n\n\t\t// if the velocity is very small, just set it to 0\n\t\tif (Math.abs(next.x) < 0.01) next.x = 0\n\t\tif (Math.abs(next.y) < 0.01) next.y = 0\n\n\t\tif (!pointerVelocity.equals(next)) {\n\t\t\tthis._pointerVelocity.set(next)\n\t\t}\n\t}\n\n\t/**\n\t * Update the input points from a pointer, pinch, or wheel event.\n\t *\n\t * @param info - The event info.\n\t * @internal\n\t */\n\tupdateFromEvent(info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo): void {\n\t\tconst currentScreenPoint = this._currentScreenPoint.__unsafe__getWithoutCapture()\n\t\tconst currentPagePoint = this._currentPagePoint.__unsafe__getWithoutCapture()\n\t\tconst isPinching = this._isPinching.__unsafe__getWithoutCapture()\n\t\tconst { screenBounds } = this.editor.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!\n\t\tconst { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.editor.getCamera())\n\n\t\tconst sx = info.point.x - screenBounds.x\n\t\tconst sy = info.point.y - screenBounds.y\n\t\tconst sz = info.point.z ?? 0.5\n\n\t\tthis._previousScreenPoint.set(currentScreenPoint)\n\t\tthis._previousPagePoint.set(currentPagePoint)\n\n\t\t// The \"screen bounds\" is relative to the user's actual screen.\n\t\t// The \"screen point\" is relative to the \"screen bounds\";\n\t\t// it will be 0,0 when its actual screen position is equal\n\t\t// to screenBounds.point. This is confusing!\n\t\tthis._currentScreenPoint.set(new Vec(sx, sy))\n\t\tconst nx = sx / cz - cx\n\t\tconst ny = sy / cz - cy\n\t\tif (isFinite(nx) && isFinite(ny)) {\n\t\t\tthis._currentPagePoint.set(new Vec(nx, ny, sz))\n\t\t}\n\n\t\tthis._isPen.set(info.type === 'pointer' && info.isPen)\n\n\t\t// Reset velocity on pointer down, or when a pinch starts or ends\n\t\tif (info.name === 'pointer_down' || isPinching) {\n\t\t\tthis._pointerVelocity.set(new Vec())\n\t\t\tthis._originScreenPoint.set(this._currentScreenPoint.__unsafe__getWithoutCapture())\n\t\t\tthis._originPagePoint.set(this._currentPagePoint.__unsafe__getWithoutCapture())\n\t\t}\n\n\t\tif (this._getHasCollaborators()) {\n\t\t\tthis.editor.run(\n\t\t\t\t() => {\n\t\t\t\t\tconst pagePoint = this._currentPagePoint.__unsafe__getWithoutCapture()\n\t\t\t\t\tthis.editor.store.put([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: TLPOINTER_ID,\n\t\t\t\t\t\t\ttypeName: 'pointer',\n\t\t\t\t\t\t\tx: pagePoint.x,\n\t\t\t\t\t\t\ty: pagePoint.y,\n\t\t\t\t\t\t\tlastActivityTimestamp:\n\t\t\t\t\t\t\t\t// If our pointer moved only because we're following some other user, then don't\n\t\t\t\t\t\t\t\t// update our last activity timestamp; otherwise, update it to the current timestamp.\n\t\t\t\t\t\t\t\tinfo.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE\n\t\t\t\t\t\t\t\t\t? (this.editor.store.unsafeGetWithoutCapture(TLPOINTER_ID)\n\t\t\t\t\t\t\t\t\t\t\t?.lastActivityTimestamp ?? Date.now())\n\t\t\t\t\t\t\t\t\t: Date.now(),\n\t\t\t\t\t\t\tmeta: {},\n\t\t\t\t\t\t},\n\t\t\t\t\t])\n\t\t\t\t},\n\t\t\t\t{ history: 'ignore' }\n\t\t\t)\n\t\t}\n\t}\n\n\ttoJson() {\n\t\treturn {\n\t\t\toriginPagePoint: this._originPagePoint.get().toJson(),\n\t\t\toriginScreenPoint: this._originScreenPoint.get().toJson(),\n\t\t\tpreviousPagePoint: this._previousPagePoint.get().toJson(),\n\t\t\tpreviousScreenPoint: this._previousScreenPoint.get().toJson(),\n\t\t\tcurrentPagePoint: this._currentPagePoint.get().toJson(),\n\t\t\tcurrentScreenPoint: this._currentScreenPoint.get().toJson(),\n\t\t\tpointerVelocity: this._pointerVelocity.get().toJson(),\n\t\t\tshiftKey: this._shiftKey.get(),\n\t\t\tmetaKey: this._metaKey.get(),\n\t\t\tctrlKey: this._ctrlKey.get(),\n\t\t\taltKey: this._altKey.get(),\n\t\t\tisPen: this._isPen.get(),\n\t\t\tisDragging: this._isDragging.get(),\n\t\t\tisPointing: this._isPointing.get(),\n\t\t\tisPinching: this._isPinching.get(),\n\t\t\tisEditing: this._isEditing.get(),\n\t\t\tisPanning: this._isPanning.get(),\n\t\t\tisSpacebarPanning: this._isSpacebarPanning.get(),\n\t\t\tkeys: Array.from(this.keys.keys()),\n\t\t\tbuttons: Array.from(this.buttons.keys()),\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,MAAM,UAAU,8BAA8B;AACvD,SAAS,eAAe;AACxB,SAAS,eAAe,oBAAoB;AAC5C,SAAS,4BAA4B;AACrC,SAAS,WAAW;AACpB,SAAS,kBAAkB;AAI3B,MAAM,yCAAyC;AAC/C,MAAM,uCAAuC;AAGtC,MAAM,cAAc;AAAA,EAC1B,YAA6B,QAAgB;AAAhB;AAAA,
|
|
4
|
+
"sourcesContent": ["import { atom, computed, unsafe__withoutCapture } from '@tldraw/state'\nimport { AtomSet } from '@tldraw/store'\nimport { TLINSTANCE_ID, TLPOINTER_ID } from '@tldraw/tlschema'\nimport { bind } from '@tldraw/utils'\nimport { INTERNAL_POINTER_IDS } from '../../../constants'\nimport { Vec } from '../../../primitives/Vec'\nimport { isAccelKey } from '../../../utils/keyboard'\nimport type { Editor } from '../../Editor'\nimport { TLPinchEventInfo, TLPointerEventInfo, TLWheelEventInfo } from '../../types/event-types'\n\nconst POINTER_VELOCITY_REFERENCE_INTERVAL_MS = 16\nconst POINTER_VELOCITY_REFERENCE_SMOOTHING = 0.5\n\n/** @public */\nexport class InputsManager {\n\tconstructor(private readonly editor: Editor) {\n\t\tthis.editor.on('frame', this._onFrame)\n\t}\n\n\t/** @internal */\n\tdispose() {\n\t\tthis.editor.off('frame', this._onFrame)\n\t}\n\n\t@bind\n\tprivate _onFrame(elapsed: number) {\n\t\tthis.updatePointerVelocity(elapsed)\n\t}\n\n\tprivate _originPagePoint = atom<Vec>('originPagePoint', new Vec())\n\t/**\n\t * The most recent pointer down's position in the current page space.\n\t */\n\tgetOriginPagePoint() {\n\t\treturn this._originPagePoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getOriginPagePoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget originPagePoint() {\n\t\treturn this.getOriginPagePoint()\n\t}\n\n\tprivate _originScreenPoint = atom<Vec>('originScreenPoint', new Vec())\n\t/**\n\t * The most recent pointer down's position in screen space.\n\t */\n\tgetOriginScreenPoint() {\n\t\treturn this._originScreenPoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getOriginScreenPoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget originScreenPoint() {\n\t\treturn this.getOriginScreenPoint()\n\t}\n\n\tprivate _previousPagePoint = atom<Vec>('previousPagePoint', new Vec())\n\t/**\n\t * The previous pointer position in the current page space.\n\t */\n\tgetPreviousPagePoint() {\n\t\treturn this._previousPagePoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getPreviousPagePoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget previousPagePoint() {\n\t\treturn this.getPreviousPagePoint()\n\t}\n\n\tprivate _previousScreenPoint = atom<Vec>('previousScreenPoint', new Vec())\n\t/**\n\t * The previous pointer position in screen space.\n\t */\n\tgetPreviousScreenPoint() {\n\t\treturn this._previousScreenPoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getPreviousScreenPoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget previousScreenPoint() {\n\t\treturn this.getPreviousScreenPoint()\n\t}\n\n\tprivate _currentPagePoint = atom<Vec>('currentPagePoint', new Vec())\n\t/**\n\t * The most recent pointer position in the current page space.\n\t */\n\tgetCurrentPagePoint() {\n\t\treturn this._currentPagePoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getCurrentPagePoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget currentPagePoint() {\n\t\treturn this.getCurrentPagePoint()\n\t}\n\n\tprivate _currentScreenPoint = atom<Vec>('currentScreenPoint', new Vec())\n\t/**\n\t * The most recent pointer position in screen space.\n\t */\n\tgetCurrentScreenPoint() {\n\t\treturn this._currentScreenPoint.get()\n\t}\n\t/**\n\t * @deprecated Use `getCurrentScreenPoint()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget currentScreenPoint() {\n\t\treturn this.getCurrentScreenPoint()\n\t}\n\n\tprivate _pointerVelocity = atom<Vec>('pointerVelocity', new Vec())\n\t/**\n\t * Velocity of mouse pointer, in pixels per millisecond.\n\t */\n\tgetPointerVelocity() {\n\t\treturn this._pointerVelocity.get()\n\t}\n\t/**\n\t * @deprecated Use `getPointerVelocity()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget pointerVelocity() {\n\t\treturn this.getPointerVelocity()\n\t}\n\n\t/**\n\t * Normally you shouldn't need to set the pointer velocity directly. Used in tests to fake pointer velocity.\n\t * @param pointerVelocity - The pointer velocity.\n\t * @internal\n\t */\n\tsetPointerVelocity(pointerVelocity: Vec) {\n\t\tthis._pointerVelocity.set(pointerVelocity)\n\t}\n\n\t/**\n\t * A set containing the currently pressed keys.\n\t */\n\treadonly keys = new AtomSet<string>('keys')\n\n\t/**\n\t * A set containing the currently pressed buttons.\n\t */\n\treadonly buttons = new AtomSet<number>('buttons')\n\n\tprivate _isPen = atom<boolean>('isPen', false)\n\n\t/**\n\t * Whether the input is from a pen.\n\t */\n\tgetIsPen() {\n\t\treturn this._isPen.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPen()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPen() {\n\t\treturn this.getIsPen()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPen(isPen: boolean) {\n\t\tthis.setIsPen(isPen)\n\t}\n\t/**\n\t * @param isPen - Whether the input is from a pen.\n\t */\n\tsetIsPen(isPen: boolean) {\n\t\tthis._isPen.set(isPen)\n\t}\n\n\tprivate _shiftKey = atom<boolean>('shiftKey', false)\n\t/**\n\t * Whether the shift key is currently pressed.\n\t */\n\tgetShiftKey() {\n\t\treturn this._shiftKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getShiftKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget shiftKey() {\n\t\treturn this.getShiftKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset shiftKey(shiftKey: boolean) {\n\t\tthis.setShiftKey(shiftKey)\n\t}\n\t/**\n\t * @param shiftKey - Whether the shift key is pressed.\n\t * @internal\n\t */\n\tsetShiftKey(shiftKey: boolean) {\n\t\tthis._shiftKey.set(shiftKey)\n\t}\n\n\tprivate _metaKey = atom<boolean>('metaKey', false)\n\t/**\n\t * Whether the meta key is currently pressed.\n\t */\n\tgetMetaKey() {\n\t\treturn this._metaKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getMetaKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget metaKey() {\n\t\treturn this.getMetaKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset metaKey(metaKey: boolean) {\n\t\tthis.setMetaKey(metaKey)\n\t}\n\t/**\n\t * @param metaKey - Whether the meta key is pressed.\n\t * @internal\n\t */\n\tsetMetaKey(metaKey: boolean) {\n\t\tthis._metaKey.set(metaKey)\n\t}\n\n\tprivate _ctrlKey = atom<boolean>('ctrlKey', false)\n\t/**\n\t * Whether the ctrl or command key is currently pressed.\n\t */\n\tgetCtrlKey() {\n\t\treturn this._ctrlKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getCtrlKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget ctrlKey() {\n\t\treturn this.getCtrlKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset ctrlKey(ctrlKey: boolean) {\n\t\tthis.setCtrlKey(ctrlKey)\n\t}\n\t/**\n\t * @param ctrlKey - Whether the ctrl key is pressed.\n\t * @internal\n\t */\n\tsetCtrlKey(ctrlKey: boolean) {\n\t\tthis._ctrlKey.set(ctrlKey)\n\t}\n\n\tprivate _altKey = atom<boolean>('altKey', false)\n\t/**\n\t * Whether the alt or option key is currently pressed.\n\t */\n\tgetAltKey() {\n\t\treturn this._altKey.get()\n\t}\n\t/**\n\t * @deprecated Use `getAltKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget altKey() {\n\t\treturn this.getAltKey()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset altKey(altKey: boolean) {\n\t\tthis.setAltKey(altKey)\n\t}\n\t/**\n\t * @param altKey - Whether the alt key is pressed.\n\t * @internal\n\t */\n\tsetAltKey(altKey: boolean) {\n\t\tthis._altKey.set(altKey)\n\t}\n\n\t/**\n\t * Is the accelerator key (cmd on mac, ctrl elsewhere) currently pressed.\n\t */\n\tgetAccelKey() {\n\t\treturn isAccelKey({ metaKey: this.getMetaKey(), ctrlKey: this.getCtrlKey() })\n\t}\n\t/**\n\t * @deprecated Use `getAccelKey()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget accelKey() {\n\t\treturn this.getAccelKey()\n\t}\n\n\tprivate _isDragging = atom<boolean>('isDragging', false)\n\t/**\n\t * Whether the user is dragging.\n\t */\n\tgetIsDragging() {\n\t\treturn this._isDragging.get()\n\t}\n\t/**\n\t * Soon to be deprecated, use `getIsDragging()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isDragging() {\n\t\treturn this.getIsDragging()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isDragging(isDragging: boolean) {\n\t\tthis.setIsDragging(isDragging)\n\t}\n\t/**\n\t * @param isDragging - Whether the user is dragging.\n\t */\n\tsetIsDragging(isDragging: boolean) {\n\t\tthis._isDragging.set(isDragging)\n\t}\n\n\tprivate _isPointing = atom<boolean>('isPointing', false)\n\t/**\n\t * Whether the user is pointing.\n\t */\n\tgetIsPointing() {\n\t\treturn this._isPointing.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPointing()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPointing() {\n\t\treturn this.getIsPointing()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPointing(isPointing: boolean) {\n\t\tthis.setIsPointing(isPointing)\n\t}\n\t/**\n\t * @param isPointing - Whether the user is pointing.\n\t * @internal\n\t */\n\tsetIsPointing(isPointing: boolean) {\n\t\tthis._isPointing.set(isPointing)\n\t}\n\n\tprivate _isRightPointing = atom<boolean>('isRightPointing', false)\n\t/**\n\t * Whether the user is right-click pointing (before drag threshold).\n\t */\n\tgetIsRightPointing() {\n\t\treturn this._isRightPointing.get()\n\t}\n\t/** @internal */\n\tsetIsRightPointing(isRightPointing: boolean) {\n\t\tthis._isRightPointing.set(isRightPointing)\n\t}\n\n\tprivate _isPinching = atom<boolean>('isPinching', false)\n\t/**\n\t * Whether the user is pinching.\n\t */\n\tgetIsPinching() {\n\t\treturn this._isPinching.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPinching()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPinching() {\n\t\treturn this.getIsPinching()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPinching(isPinching: boolean) {\n\t\tthis.setIsPinching(isPinching)\n\t}\n\t/**\n\t * @param isPinching - Whether the user is pinching.\n\t * @internal\n\t */\n\tsetIsPinching(isPinching: boolean) {\n\t\tthis._isPinching.set(isPinching)\n\t}\n\n\tprivate _isEditing = atom<boolean>('isEditing', false)\n\t/**\n\t * Whether the user is editing.\n\t */\n\tgetIsEditing() {\n\t\treturn this._isEditing.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsEditing()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isEditing() {\n\t\treturn this.getIsEditing()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isEditing(isEditing: boolean) {\n\t\tthis.setIsEditing(isEditing)\n\t}\n\t/**\n\t * @param isEditing - Whether the user is editing.\n\t */\n\tsetIsEditing(isEditing: boolean) {\n\t\tthis._isEditing.set(isEditing)\n\t}\n\n\tprivate _isPanning = atom<boolean>('isPanning', false)\n\t/**\n\t * Whether the user is panning.\n\t */\n\tgetIsPanning() {\n\t\treturn this._isPanning.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsPanning()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isPanning() {\n\t\treturn this.getIsPanning()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isPanning(isPanning: boolean) {\n\t\tthis.setIsPanning(isPanning)\n\t}\n\t/**\n\t * @param isPanning - Whether the user is panning.\n\t * @internal\n\t */\n\tsetIsPanning(isPanning: boolean) {\n\t\tthis._isPanning.set(isPanning)\n\t}\n\n\tprivate _isSpacebarPanning = atom<boolean>('isSpacebarPanning', false)\n\t/**\n\t * Whether the user is spacebar panning.\n\t */\n\tgetIsSpacebarPanning() {\n\t\treturn this._isSpacebarPanning.get()\n\t}\n\t/**\n\t * @deprecated Use `getIsSpacebarPanning()` instead.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isSpacebarPanning() {\n\t\treturn this.getIsSpacebarPanning()\n\t}\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tset isSpacebarPanning(isSpacebarPanning: boolean) {\n\t\tthis.setIsSpacebarPanning(isSpacebarPanning)\n\t}\n\t/**\n\t * @param isSpacebarPanning - Whether the user is spacebar panning.\n\t * @internal\n\t */\n\tsetIsSpacebarPanning(isSpacebarPanning: boolean) {\n\t\tthis._isSpacebarPanning.set(isSpacebarPanning)\n\t}\n\n\t@computed private _getHasCollaborators() {\n\t\treturn this.editor.getCollaborators().length > 0 // could we do this more efficiently?\n\t}\n\n\t/**\n\t * The previous point used for velocity calculation (updated each tick, not each pointer event).\n\t * @internal\n\t */\n\tprivate _velocityPrevPoint = new Vec()\n\n\t/**\n\t * Update the pointer velocity based on elapsed time. Called each frame.\n\t * @param elapsed - The time elapsed since the last tick in milliseconds.\n\t * @internal\n\t */\n\tupdatePointerVelocity(elapsed: number) {\n\t\tconst currentScreenPoint = this.getCurrentScreenPoint()\n\t\tconst pointerVelocity = this.getPointerVelocity()\n\n\t\tif (elapsed === 0) return\n\n\t\tconst delta = Vec.Sub(currentScreenPoint, this._velocityPrevPoint)\n\t\tthis._velocityPrevPoint = currentScreenPoint.clone()\n\n\t\tconst length = delta.len()\n\t\tconst direction = length ? delta.div(length) : new Vec(0, 0)\n\n\t\t// Preserve the old 16ms smoothing with alpha = 1 - (1 - 0.5)^(elapsed / 16).\n\t\tconst smoothing =\n\t\t\t1 -\n\t\t\tMath.pow(\n\t\t\t\t1 - POINTER_VELOCITY_REFERENCE_SMOOTHING,\n\t\t\t\telapsed / POINTER_VELOCITY_REFERENCE_INTERVAL_MS\n\t\t\t)\n\t\tconst next = pointerVelocity.clone().lrp(direction.mul(length / elapsed), smoothing)\n\n\t\t// if the velocity is very small, just set it to 0\n\t\tif (Math.abs(next.x) < 0.01) next.x = 0\n\t\tif (Math.abs(next.y) < 0.01) next.y = 0\n\n\t\tif (!pointerVelocity.equals(next)) {\n\t\t\tthis._pointerVelocity.set(next)\n\t\t}\n\t}\n\n\t/**\n\t * Update the input points from a pointer, pinch, or wheel event.\n\t *\n\t * @param info - The event info.\n\t * @internal\n\t */\n\tupdateFromEvent(info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo): void {\n\t\tconst currentScreenPoint = this._currentScreenPoint.__unsafe__getWithoutCapture()\n\t\tconst currentPagePoint = this._currentPagePoint.__unsafe__getWithoutCapture()\n\t\tconst isPinching = this._isPinching.__unsafe__getWithoutCapture()\n\t\tconst { screenBounds } = this.editor.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!\n\t\tconst { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.editor.getCamera())\n\n\t\tconst sx = info.point.x - screenBounds.x\n\t\tconst sy = info.point.y - screenBounds.y\n\t\tconst sz = info.point.z ?? 0.5\n\n\t\tthis._previousScreenPoint.set(currentScreenPoint)\n\t\tthis._previousPagePoint.set(currentPagePoint)\n\n\t\t// The \"screen bounds\" is relative to the user's actual screen.\n\t\t// The \"screen point\" is relative to the \"screen bounds\";\n\t\t// it will be 0,0 when its actual screen position is equal\n\t\t// to screenBounds.point. This is confusing!\n\t\tthis._currentScreenPoint.set(new Vec(sx, sy))\n\t\tconst nx = sx / cz - cx\n\t\tconst ny = sy / cz - cy\n\t\tif (isFinite(nx) && isFinite(ny)) {\n\t\t\tthis._currentPagePoint.set(new Vec(nx, ny, sz))\n\t\t}\n\n\t\tthis._isPen.set(info.type === 'pointer' && info.isPen)\n\n\t\t// Reset velocity on pointer down, or when a pinch starts or ends\n\t\tif (info.name === 'pointer_down' || isPinching) {\n\t\t\tthis._pointerVelocity.set(new Vec())\n\t\t\tthis._originScreenPoint.set(this._currentScreenPoint.__unsafe__getWithoutCapture())\n\t\t\tthis._originPagePoint.set(this._currentPagePoint.__unsafe__getWithoutCapture())\n\t\t}\n\n\t\tif (this._getHasCollaborators()) {\n\t\t\tthis.editor.run(\n\t\t\t\t() => {\n\t\t\t\t\tconst pagePoint = this._currentPagePoint.__unsafe__getWithoutCapture()\n\t\t\t\t\tthis.editor.store.put([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: TLPOINTER_ID,\n\t\t\t\t\t\t\ttypeName: 'pointer',\n\t\t\t\t\t\t\tx: pagePoint.x,\n\t\t\t\t\t\t\ty: pagePoint.y,\n\t\t\t\t\t\t\tlastActivityTimestamp:\n\t\t\t\t\t\t\t\t// If our pointer moved only because we're following some other user, then don't\n\t\t\t\t\t\t\t\t// update our last activity timestamp; otherwise, update it to the current timestamp.\n\t\t\t\t\t\t\t\tinfo.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE\n\t\t\t\t\t\t\t\t\t? (this.editor.store.unsafeGetWithoutCapture(TLPOINTER_ID)\n\t\t\t\t\t\t\t\t\t\t\t?.lastActivityTimestamp ?? Date.now())\n\t\t\t\t\t\t\t\t\t: Date.now(),\n\t\t\t\t\t\t\tmeta: {},\n\t\t\t\t\t\t},\n\t\t\t\t\t])\n\t\t\t\t},\n\t\t\t\t{ history: 'ignore' }\n\t\t\t)\n\t\t}\n\t}\n\n\ttoJson() {\n\t\treturn {\n\t\t\toriginPagePoint: this._originPagePoint.get().toJson(),\n\t\t\toriginScreenPoint: this._originScreenPoint.get().toJson(),\n\t\t\tpreviousPagePoint: this._previousPagePoint.get().toJson(),\n\t\t\tpreviousScreenPoint: this._previousScreenPoint.get().toJson(),\n\t\t\tcurrentPagePoint: this._currentPagePoint.get().toJson(),\n\t\t\tcurrentScreenPoint: this._currentScreenPoint.get().toJson(),\n\t\t\tpointerVelocity: this._pointerVelocity.get().toJson(),\n\t\t\tshiftKey: this._shiftKey.get(),\n\t\t\tmetaKey: this._metaKey.get(),\n\t\t\tctrlKey: this._ctrlKey.get(),\n\t\t\taltKey: this._altKey.get(),\n\t\t\tisPen: this._isPen.get(),\n\t\t\tisDragging: this._isDragging.get(),\n\t\t\tisPointing: this._isPointing.get(),\n\t\t\tisPinching: this._isPinching.get(),\n\t\t\tisEditing: this._isEditing.get(),\n\t\t\tisPanning: this._isPanning.get(),\n\t\t\tisSpacebarPanning: this._isSpacebarPanning.get(),\n\t\t\tkeys: Array.from(this.keys.keys()),\n\t\t\tbuttons: Array.from(this.buttons.keys()),\n\t\t}\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -24,19 +24,21 @@ class SpatialIndexManager {
|
|
|
24
24
|
}
|
|
25
25
|
createSpatialIndexComputed() {
|
|
26
26
|
const shapeHistory = this.editor.store.query.filterHistory("shape");
|
|
27
|
+
const bindingHistory = this.editor.store.query.filterHistory("binding");
|
|
27
28
|
return computed("spatialIndex", (_prevValue, lastComputedEpoch) => {
|
|
28
29
|
if (isUninitialized(_prevValue)) {
|
|
29
30
|
return this.rebuildAndBumpEpoch();
|
|
30
31
|
}
|
|
31
32
|
const shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch);
|
|
32
|
-
|
|
33
|
+
const bindingDiff = bindingHistory.getDiffSince(lastComputedEpoch);
|
|
34
|
+
if (shapeDiff === RESET_VALUE || bindingDiff === RESET_VALUE) {
|
|
33
35
|
return this.rebuildAndBumpEpoch();
|
|
34
36
|
}
|
|
35
37
|
const currentPageId = this.editor.getCurrentPageId();
|
|
36
38
|
if (this.lastPageId !== currentPageId) {
|
|
37
39
|
return this.rebuildAndBumpEpoch();
|
|
38
40
|
}
|
|
39
|
-
if (shapeDiff.length === 0) return this._boundsEpoch;
|
|
41
|
+
if (shapeDiff.length === 0 && bindingDiff.length === 0) return this._boundsEpoch;
|
|
40
42
|
if (this.processIncrementalUpdate(shapeDiff)) {
|
|
41
43
|
this._boundsEpoch++;
|
|
42
44
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport type { RecordsDiff } from '@tldraw/store'\nimport { TLPageId, TLShape, TLShapeId, isShape } from '@tldraw/tlschema'\nimport type { TLRecord } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport { Box } from '../../../primitives/Box'\nimport type { Editor } from '../../Editor'\nimport { RBushIndex, type SpatialElement } from './RBushIndex'\n\n/**\n * Manages spatial indexing for efficient shape location queries.\n *\n * Uses an R-tree (via RBush) to enable O(log n) spatial queries instead of O(n) iteration.\n * Handles shapes with computed bounds (arrows, groups, custom shapes) by checking all shapes'\n * bounds on each update using the reactive bounds cache.\n *\n * Key features:\n * - Incremental updates using filterHistory pattern\n * - Leverages existing bounds cache reactivity for dependency tracking\n * - Works with any custom shape type with computed bounds\n * - Per-page index (rebuilds on page change)\n * - Optimized for viewport culling queries\n *\n * @internal\n */\nexport class SpatialIndexManager {\n\tprivate rbush: RBushIndex\n\tprivate spatialIndexComputed: Computed<number>\n\tprivate lastPageId: TLPageId | null = null\n\n\t// Bumps only when the rbush content may have changed. Consumers subscribe\n\t// via the computed; a stable epoch lets prop-only diffs skip downstream\n\t// invalidations.\n\tprivate _boundsEpoch = 0\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.rbush = new RBushIndex()\n\t\tthis.spatialIndexComputed = this.createSpatialIndexComputed()\n\t}\n\n\tprivate rebuildAndBumpEpoch(): number {\n\t\tthis.buildFromScratch()\n\t\tthis._boundsEpoch++\n\t\treturn this._boundsEpoch\n\t}\n\n\tprivate createSpatialIndexComputed() {\n\t\tconst shapeHistory = this.editor.store.query.filterHistory('shape')\n\n\t\treturn computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(_prevValue)) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (shapeDiff === RESET_VALUE) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\t\tif (this.lastPageId !== currentPageId) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tif (shapeDiff.length === 0) return this._boundsEpoch\n\n\t\t\tif (this.processIncrementalUpdate(shapeDiff)) {\n\t\t\t\tthis._boundsEpoch++\n\t\t\t}\n\t\t\treturn this._boundsEpoch\n\t\t})\n\t}\n\n\tprivate buildFromScratch(): void {\n\t\tthis.rbush.clear()\n\t\tthis.lastPageId = this.editor.getCurrentPageId()\n\n\t\tconst elements: SpatialElement[] = []\n\t\tfor (const shape of this.editor.getCurrentPageShapes()) {\n\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\telements.push({\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tid: shape.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Bulk load for efficiency\n\t\tthis.rbush.bulkLoad(elements)\n\t}\n\n\tprivate processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): boolean {\n\t\tconst processedShapeIds = new Set<TLShapeId>()\n\t\tlet changed = false\n\n\t\t// Step 1: apply diff entries directly. `changed` flips only on real\n\t\t// rbush mutations, so prop-only updates and no-op removes (e.g. shapes\n\t\t// from other pages, or never-indexed shapes with invalid bounds) don't\n\t\t// bump the epoch.\n\t\tfor (const changes of shapeDiff) {\n\t\t\tfor (const shape of objectMapValues(changes.added) as TLShape[]) {\n\t\t\t\tif (isShape(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tthis.rbush.upsert(shape.id, bounds)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const shape of objectMapValues(changes.removed) as TLShape[]) {\n\t\t\t\tif (isShape(shape)) {\n\t\t\t\t\tif (this.rbush.has(shape.id)) {\n\t\t\t\t\t\tthis.rbush.remove(shape.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const [, to] of objectMapValues(changes.updated) as [TLShape, TLShape][]) {\n\t\t\t\tif (!isShape(to)) continue\n\t\t\t\tprocessedShapeIds.add(to.id)\n\n\t\t\t\tconst isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId\n\n\t\t\t\tif (isOnPage) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(to.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tconst indexedElement = this.rbush.getElement(to.id)\n\t\t\t\t\t\tif (!this.areBoundsEqualToSpatialElement(bounds, indexedElement)) {\n\t\t\t\t\t\t\tthis.rbush.upsert(to.id, bounds)\n\t\t\t\t\t\t\tchanged = true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: must always run. Diff entries can dirty derived bounds \u2014\n\t\t// arrows bound to moved shapes, groups with moved children \u2014 without\n\t\t// touching any record visited in step 1. Also catches outline-only\n\t\t// changes (e.g. geo rectangle\u2192ellipse at the same w/h) that shift a\n\t\t// bound arrow's intersection points: step 1 sees the geo's\n\t\t// axis-aligned bounds unchanged and skips, but the dependent arrow's\n\t\t// bounds have moved.\n\t\t//\n\t\t// Iterating the rbush's element map directly avoids allocating a\n\t\t// shape-id array per pointer move. Mutation here is limited to\n\t\t// upserts of existing keys and deletions, both safe during Map\n\t\t// iteration.\n\t\tfor (const [shapeId, indexedElement] of this.rbush.entries()) {\n\t\t\tif (processedShapeIds.has(shapeId)) continue\n\n\t\t\tconst currentBounds = this.editor.getShapePageBounds(shapeId)\n\t\t\tif (this.areBoundsEqualToSpatialElement(currentBounds, indexedElement)) continue\n\n\t\t\tif (currentBounds && currentBounds.isValid()) {\n\t\t\t\tthis.rbush.upsert(shapeId, currentBounds)\n\t\t\t} else {\n\t\t\t\tthis.rbush.remove(shapeId)\n\t\t\t}\n\t\t\tchanged = true\n\t\t}\n\n\t\treturn changed\n\t}\n\n\tprivate areBoundsEqualToSpatialElement(\n\t\ta: Box | undefined,\n\t\tb: SpatialElement | undefined\n\t): boolean {\n\t\tif (!a && !b) return true\n\t\tif (!a || !b) return false\n\t\treturn a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY\n\t}\n\n\t/**\n\t * Get shape IDs within the given bounds.\n\t * Optimized for viewport culling queries.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param bounds - The bounds to search within\n\t * @returns Unordered set of shape IDs within the bounds\n\t *\n\t * @public\n\t */\n\tgetShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(bounds)\n\t}\n\n\t/**\n\t * Get shape IDs at a point (with optional margin).\n\t * Creates a small bounding box around the point and searches the spatial index.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param point - The point to search at\n\t * @param margin - The margin around the point to search (default: 0)\n\t * @returns Unordered set of shape IDs that could potentially contain the point\n\t *\n\t * @public\n\t */\n\tgetShapeIdsAtPoint(point: { x: number; y: number }, margin = 0): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(new Box(point.x - margin, point.y - margin, margin * 2, margin * 2))\n\t}\n\n\t/**\n\t * Dispose of the spatial index manager.\n\t * Clears the R-tree to prevent memory leaks.\n\t *\n\t * @public\n\t */\n\tdispose(): void {\n\t\tthis.rbush.dispose()\n\t\tthis.lastPageId = null\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAmB,aAAa,UAAU,uBAAuB;AAEjE,SAAuC,eAAe;AAEtD,SAAS,uBAAuB;AAChC,SAAS,WAAW;AAEpB,SAAS,kBAAuC;AAkBzC,MAAM,oBAAoB;AAAA,EAUhC,YAA4B,QAAgB;AAAhB;AAC3B,SAAK,QAAQ,IAAI,WAAW;AAC5B,SAAK,uBAAuB,KAAK,2BAA2B;AAAA,EAC7D;AAAA,EAH4B;AAAA,EATpB;AAAA,EACA;AAAA,EACA,aAA8B;AAAA;AAAA;AAAA;AAAA,EAK9B,eAAe;AAAA,EAOf,sBAA8B;AACrC,SAAK,iBAAiB;AACtB,SAAK;AACL,WAAO,KAAK;AAAA,EACb;AAAA,EAEQ,6BAA6B;AACpC,UAAM,eAAe,KAAK,OAAO,MAAM,MAAM,cAAc,OAAO;
|
|
4
|
+
"sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport type { RecordsDiff } from '@tldraw/store'\nimport { TLPageId, TLShape, TLShapeId, isShape } from '@tldraw/tlschema'\nimport type { TLRecord } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport { Box } from '../../../primitives/Box'\nimport type { Editor } from '../../Editor'\nimport { RBushIndex, type SpatialElement } from './RBushIndex'\n\n/**\n * Manages spatial indexing for efficient shape location queries.\n *\n * Uses an R-tree (via RBush) to enable O(log n) spatial queries instead of O(n) iteration.\n * Handles shapes with computed bounds (arrows, groups, custom shapes) by checking all shapes'\n * bounds on each update using the reactive bounds cache.\n *\n * Key features:\n * - Incremental updates using filterHistory pattern\n * - Leverages existing bounds cache reactivity for dependency tracking\n * - Works with any custom shape type with computed bounds\n * - Per-page index (rebuilds on page change)\n * - Optimized for viewport culling queries\n *\n * @internal\n */\nexport class SpatialIndexManager {\n\tprivate rbush: RBushIndex\n\tprivate spatialIndexComputed: Computed<number>\n\tprivate lastPageId: TLPageId | null = null\n\n\t// Bumps only when the rbush content may have changed. Consumers subscribe\n\t// via the computed; a stable epoch lets prop-only diffs skip downstream\n\t// invalidations.\n\tprivate _boundsEpoch = 0\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.rbush = new RBushIndex()\n\t\tthis.spatialIndexComputed = this.createSpatialIndexComputed()\n\t}\n\n\tprivate rebuildAndBumpEpoch(): number {\n\t\tthis.buildFromScratch()\n\t\tthis._boundsEpoch++\n\t\treturn this._boundsEpoch\n\t}\n\n\tprivate createSpatialIndexComputed() {\n\t\tconst shapeHistory = this.editor.store.query.filterHistory('shape')\n\t\t// Binding changes can move a shape's derived bounds (e.g. creating or\n\t\t// deleting an arrow binding relocates the arrow's body) without\n\t\t// touching any shape record, so they must also invalidate the index.\n\t\tconst bindingHistory = this.editor.store.query.filterHistory('binding')\n\n\t\treturn computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(_prevValue)) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)\n\t\t\tconst bindingDiff = bindingHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (shapeDiff === RESET_VALUE || bindingDiff === RESET_VALUE) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\t\tif (this.lastPageId !== currentPageId) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tif (shapeDiff.length === 0 && bindingDiff.length === 0) return this._boundsEpoch\n\n\t\t\t// A binding-only diff passes an empty shape diff: step 1 is a no-op\n\t\t\t// and the step-2 sweep re-checks the indexed bounds of every shape.\n\t\t\tif (this.processIncrementalUpdate(shapeDiff)) {\n\t\t\t\tthis._boundsEpoch++\n\t\t\t}\n\t\t\treturn this._boundsEpoch\n\t\t})\n\t}\n\n\tprivate buildFromScratch(): void {\n\t\tthis.rbush.clear()\n\t\tthis.lastPageId = this.editor.getCurrentPageId()\n\n\t\tconst elements: SpatialElement[] = []\n\t\tfor (const shape of this.editor.getCurrentPageShapes()) {\n\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\telements.push({\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tid: shape.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Bulk load for efficiency\n\t\tthis.rbush.bulkLoad(elements)\n\t}\n\n\tprivate processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): boolean {\n\t\tconst processedShapeIds = new Set<TLShapeId>()\n\t\tlet changed = false\n\n\t\t// Step 1: apply diff entries directly. `changed` flips only on real\n\t\t// rbush mutations, so prop-only updates and no-op removes (e.g. shapes\n\t\t// from other pages, or never-indexed shapes with invalid bounds) don't\n\t\t// bump the epoch.\n\t\tfor (const changes of shapeDiff) {\n\t\t\tfor (const shape of objectMapValues(changes.added) as TLShape[]) {\n\t\t\t\tif (isShape(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tthis.rbush.upsert(shape.id, bounds)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const shape of objectMapValues(changes.removed) as TLShape[]) {\n\t\t\t\tif (isShape(shape)) {\n\t\t\t\t\tif (this.rbush.has(shape.id)) {\n\t\t\t\t\t\tthis.rbush.remove(shape.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const [, to] of objectMapValues(changes.updated) as [TLShape, TLShape][]) {\n\t\t\t\tif (!isShape(to)) continue\n\t\t\t\tprocessedShapeIds.add(to.id)\n\n\t\t\t\tconst isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId\n\n\t\t\t\tif (isOnPage) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(to.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tconst indexedElement = this.rbush.getElement(to.id)\n\t\t\t\t\t\tif (!this.areBoundsEqualToSpatialElement(bounds, indexedElement)) {\n\t\t\t\t\t\t\tthis.rbush.upsert(to.id, bounds)\n\t\t\t\t\t\t\tchanged = true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: must always run. Diff entries can dirty derived bounds \u2014\n\t\t// arrows bound to moved shapes, groups with moved children \u2014 without\n\t\t// touching any record visited in step 1. Also catches outline-only\n\t\t// changes (e.g. geo rectangle\u2192ellipse at the same w/h) that shift a\n\t\t// bound arrow's intersection points: step 1 sees the geo's\n\t\t// axis-aligned bounds unchanged and skips, but the dependent arrow's\n\t\t// bounds have moved.\n\t\t//\n\t\t// Iterating the rbush's element map directly avoids allocating a\n\t\t// shape-id array per pointer move. Mutation here is limited to\n\t\t// upserts of existing keys and deletions, both safe during Map\n\t\t// iteration.\n\t\tfor (const [shapeId, indexedElement] of this.rbush.entries()) {\n\t\t\tif (processedShapeIds.has(shapeId)) continue\n\n\t\t\tconst currentBounds = this.editor.getShapePageBounds(shapeId)\n\t\t\tif (this.areBoundsEqualToSpatialElement(currentBounds, indexedElement)) continue\n\n\t\t\tif (currentBounds && currentBounds.isValid()) {\n\t\t\t\tthis.rbush.upsert(shapeId, currentBounds)\n\t\t\t} else {\n\t\t\t\tthis.rbush.remove(shapeId)\n\t\t\t}\n\t\t\tchanged = true\n\t\t}\n\n\t\treturn changed\n\t}\n\n\tprivate areBoundsEqualToSpatialElement(\n\t\ta: Box | undefined,\n\t\tb: SpatialElement | undefined\n\t): boolean {\n\t\tif (!a && !b) return true\n\t\tif (!a || !b) return false\n\t\treturn a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY\n\t}\n\n\t/**\n\t * Get shape IDs within the given bounds.\n\t * Optimized for viewport culling queries.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param bounds - The bounds to search within\n\t * @returns Unordered set of shape IDs within the bounds\n\t *\n\t * @public\n\t */\n\tgetShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(bounds)\n\t}\n\n\t/**\n\t * Get shape IDs at a point (with optional margin).\n\t * Creates a small bounding box around the point and searches the spatial index.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param point - The point to search at\n\t * @param margin - The margin around the point to search (default: 0)\n\t * @returns Unordered set of shape IDs that could potentially contain the point\n\t *\n\t * @public\n\t */\n\tgetShapeIdsAtPoint(point: { x: number; y: number }, margin = 0): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(new Box(point.x - margin, point.y - margin, margin * 2, margin * 2))\n\t}\n\n\t/**\n\t * Dispose of the spatial index manager.\n\t * Clears the R-tree to prevent memory leaks.\n\t *\n\t * @public\n\t */\n\tdispose(): void {\n\t\tthis.rbush.dispose()\n\t\tthis.lastPageId = null\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { objectMapKeys } from "@tldraw/utils";
|
|
2
|
+
function resolveLineHeightPx(fontSize, lineHeight) {
|
|
3
|
+
return Math.round(fontSize * lineHeight);
|
|
4
|
+
}
|
|
2
5
|
const fixNewLines = /\r?\n|\r/g;
|
|
3
6
|
function normalizeTextForDom(text) {
|
|
4
7
|
return text.replace(fixNewLines, "\n").split("\n").map((x) => x || " ").join("\n");
|
|
@@ -76,7 +79,7 @@ class TextManager {
|
|
|
76
79
|
"font-style": opts.fontStyle,
|
|
77
80
|
"font-weight": opts.fontWeight,
|
|
78
81
|
"font-size": opts.fontSize + "px",
|
|
79
|
-
"line-height": opts.lineHeight
|
|
82
|
+
"line-height": `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,
|
|
80
83
|
padding: opts.padding,
|
|
81
84
|
"max-width": opts.maxWidth ? opts.maxWidth + "px" : void 0,
|
|
82
85
|
"min-width": opts.minWidth ? opts.minWidth + "px" : void 0,
|
|
@@ -247,7 +250,7 @@ class TextManager {
|
|
|
247
250
|
"font-style": opts.fontStyle,
|
|
248
251
|
"font-weight": opts.fontWeight,
|
|
249
252
|
"font-size": opts.fontSize + "px",
|
|
250
|
-
"line-height": opts.lineHeight
|
|
253
|
+
"line-height": `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,
|
|
251
254
|
width: `${elementWidth}px`,
|
|
252
255
|
height: "min-content",
|
|
253
256
|
"text-align": textAlignmentsForLtr[opts.textAlign],
|
|
@@ -289,6 +292,7 @@ class TextManager {
|
|
|
289
292
|
}
|
|
290
293
|
}
|
|
291
294
|
export {
|
|
292
|
-
TextManager
|
|
295
|
+
TextManager,
|
|
296
|
+
resolveLineHeightPx
|
|
293
297
|
};
|
|
294
298
|
//# sourceMappingURL=TextManager.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/TextManager/TextManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { objectMapKeys } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\ninterface PoolItem {\n\tel: HTMLDivElement\n\thtml: string\n\tappliedStyleKeys: string[]\n}\n\n/** @public */\nexport interface BatchMeasurementRequest {\n\thtml: string\n\topts: TLMeasureTextOpts\n}\n\n/** @public */\nexport type TLMeasuredTextSize = BoxModel & {\n\tscrollWidth: number\n}\n\n/** @public */\nexport interface TLMeasureTextOpts {\n\tfontStyle: string\n\tfontWeight: string\n\tfontFamily: string\n\tfontSize: number\n\t/** This must be a number, e.g. 1.35, not a pixel value. */\n\tlineHeight: number\n\t/**\n\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t * space are preserved.\n\t */\n\tmaxWidth: null | number\n\tminWidth?: null | number\n\t// todo: make this a number so that it is consistent with other TLMeasureTextSpanOpts\n\tpadding: string\n\totherStyles?: Record<string, string>\n\tdisableOverflowWrapBreaking?: boolean\n\tmeasureScrollWidth?: boolean\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n\totherStyles?: Record<string, string>\n\tmeasureScrollWidth?: boolean\n}\n\nconst spaceCharacterRegex = /\\s/\n\nconst initialDefaultStyles = Object.freeze({\n\t'overflow-wrap': 'break-word',\n\t'word-break': 'auto',\n\twidth: null,\n\theight: null,\n\t'max-width': null,\n\t'min-width': null,\n})\n\n/** @public */\nexport class TextManager {\n\tprivate elm: HTMLDivElement\n\tprivate poolElms: PoolItem[] = []\n\n\tconstructor(public editor: Editor) {\n\t\tthis.elm = this.createMeasurementEl()\n\t\tthis.editor.getContainer().appendChild(this.elm)\n\t}\n\n\tprivate createMeasurementEl(): HTMLDivElement {\n\t\tconst elm = this.editor.getContainerDocument().createElement('div')\n\t\telm.classList.add('tl-text')\n\t\telm.classList.add('tl-text-measure')\n\t\telm.setAttribute('dir', 'auto')\n\t\telm.tabIndex = -1\n\t\tfor (const key of objectMapKeys(initialDefaultStyles)) {\n\t\t\telm.style.setProperty(key, initialDefaultStyles[key])\n\t\t}\n\n\t\treturn elm\n\t}\n\n\tprivate resetElementStyles(el: HTMLElement, appliedStyleKeys: string[]) {\n\t\tfor (const key of appliedStyleKeys) {\n\t\t\tif (key in initialDefaultStyles) {\n\t\t\t\tel.style.setProperty(key, initialDefaultStyles[key as keyof typeof initialDefaultStyles])\n\t\t\t} else {\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate setElementStyles(el: HTMLElement, styles: Record<string, string | undefined | null>) {\n\t\ttype StyleValue = string | null\n\t\ttype RestoreEntry = [prop: string, value: StyleValue]\n\n\t\tconst restore: RestoreEntry[] = []\n\n\t\tfor (const [key, nextValue] of Object.entries(styles)) {\n\t\t\tconst oldValue = el.style.getPropertyValue(key)\n\n\t\t\tif (typeof nextValue === 'string') {\n\t\t\t\tif (oldValue === nextValue) continue\n\t\t\t\trestore.push([key, oldValue || null])\n\t\t\t\tel.style.setProperty(key, nextValue)\n\t\t\t} else {\n\t\t\t\tif (!oldValue) continue\n\t\t\t\trestore.push([key, oldValue])\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\n\t\treturn () => {\n\t\t\tfor (const [key, value] of restore) {\n\t\t\t\tif (value === null || value === '') el.style.removeProperty(key)\n\t\t\t\telse el.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getMeasureStyles(opts: TLMeasureTextOpts): Record<string, string | undefined> {\n\t\treturn {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': opts.lineHeight.toString(),\n\t\t\tpadding: opts.padding,\n\t\t\t'max-width': opts.maxWidth ? opts.maxWidth + 'px' : undefined,\n\t\t\t'min-width': opts.minWidth ? opts.minWidth + 'px' : undefined,\n\t\t\t'overflow-wrap': opts.disableOverflowWrapBreaking ? 'normal' : 'break-word',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t}\n\n\tdispose() {\n\t\tthis.elm.remove()\n\t\tfor (const { el } of this.poolElms) {\n\t\t\tel.remove()\n\t\t}\n\t\tthis.poolElms.length = 0\n\t}\n\n\tprivate ensurePoolSize(size: number) {\n\t\tif (this.poolElms.length >= size) return\n\n\t\tconst fragment = this.editor.getContainerDocument().createDocumentFragment()\n\t\twhile (this.poolElms.length < size) {\n\t\t\tconst el = this.createMeasurementEl()\n\t\t\tthis.poolElms.push({ el, html: '', appliedStyleKeys: [] })\n\t\t\tfragment.appendChild(el)\n\t\t}\n\t\tthis.editor.getContainer().appendChild(fragment)\n\t}\n\n\tprivate getPoolItem(index: number): PoolItem {\n\t\tthis.ensurePoolSize(index + 1)\n\t\treturn this.poolElms[index]\n\t}\n\n\tmeasureHtmlBatch(requests: BatchMeasurementRequest[]): TLMeasuredTextSize[] {\n\t\tif (requests.length === 0) return []\n\n\t\twhile (this.poolElms.length > requests.length) {\n\t\t\tconst { el } = this.poolElms.pop()!\n\t\t\tel.remove()\n\t\t}\n\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst { html, opts } = requests[i]\n\t\t\tconst poolItem = this.getPoolItem(i)\n\n\t\t\tconst { el } = poolItem\n\t\t\tthis.resetElementStyles(el, poolItem.appliedStyleKeys)\n\t\t\tconst styles = this.getMeasureStyles(opts)\n\t\t\tthis.setElementStyles(el, styles)\n\t\t\tpoolItem.appliedStyleKeys = Object.keys(styles)\n\t\t\t// Skip innerHTML parsing if the content hasn't changed\n\t\t\tif (poolItem.html !== html) {\n\t\t\t\tel.innerHTML = html\n\t\t\t\tpoolItem.html = html\n\t\t\t}\n\t\t}\n\n\t\tconst results: TLMeasuredTextSize[] = []\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst el = this.getPoolItem(i).el\n\t\t\tconst scrollWidth = requests[i].opts.measureScrollWidth ? el.scrollWidth : 0\n\t\t\tconst rect = el.getBoundingClientRect()\n\t\t\tresults.push({\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t})\n\t\t}\n\n\t\treturn results\n\t}\n\n\tmeasureText(textToMeasure: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst div = this.editor.getContainerDocument().createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(html: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst { elm } = this\n\n\t\tconst restoreStyles = this.setElementStyles(elm, this.getMeasureStyles(opts))\n\n\t\ttry {\n\t\t\telm.innerHTML = html\n\n\t\t\tconst scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0\n\t\t\tconst rect = elm.getBoundingClientRect()\n\n\t\t\treturn {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t}\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst { elm } = this\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\tconst newStyles = {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': opts.lineHeight.toString(),\n\t\t\twidth: `${elementWidth}px`,\n\t\t\theight: 'min-content',\n\t\t\t'text-align': textAlignmentsForLtr[opts.textAlign],\n\t\t\t'overflow-wrap': shouldTruncateToFirstLine ? 'anywhere' : 'break-word',\n\t\t\t'word-break': shouldTruncateToFirstLine ? 'break-all' : 'normal',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t\tconst restoreStyles = this.setElementStyles(elm, newStyles)\n\n\t\ttry {\n\t\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t\t// Render the text into the measurement element:\n\t\t\telm.textContent = normalizedText\n\n\t\t\t// actually measure the text:\n\t\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine,\n\t\t\t})\n\n\t\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\t\telm.textContent = '\u2026'\n\t\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\t\telm.textContent = normalizedText\n\t\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t\t}).spans\n\n\t\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t\t// browser.\n\t\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\t\ttruncatedSpans.push({\n\t\t\t\t\ttext: '\u2026',\n\t\t\t\t\tbox: {\n\t\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\treturn truncatedSpans\n\t\t\t}\n\n\t\t\treturn spans\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,qBAAqB;
|
|
4
|
+
"sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { objectMapKeys } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * The whole-pixel line-height for a given font size and tldraw's unitless line-height\n * multiplier. tldraw's theme stores line-height as a multiplier (e.g. 1.35); resolving it\n * to a whole pixel keeps line spacing identical across rendering engines, which otherwise\n * disagree on fractional line boxes (WebKit snaps them to whole pixels, Blink keeps the\n * fraction) and let multi-line text drift apart. Apply it everywhere line-height is used \u2014\n * measurement, on-canvas render, and export \u2014 so geometry and rendering agree.\n * See https://github.com/tldraw/tldraw/issues/8970.\n *\n * @public\n */\nexport function resolveLineHeightPx(fontSize: number, lineHeight: number): number {\n\treturn Math.round(fontSize * lineHeight)\n}\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\ninterface PoolItem {\n\tel: HTMLDivElement\n\thtml: string\n\tappliedStyleKeys: string[]\n}\n\n/** @public */\nexport interface BatchMeasurementRequest {\n\thtml: string\n\topts: TLMeasureTextOpts\n}\n\n/** @public */\nexport type TLMeasuredTextSize = BoxModel & {\n\tscrollWidth: number\n}\n\n/** @public */\nexport interface TLMeasureTextOpts {\n\tfontStyle: string\n\tfontWeight: string\n\tfontFamily: string\n\tfontSize: number\n\t/** This must be a number, e.g. 1.35, not a pixel value. */\n\tlineHeight: number\n\t/**\n\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t * space are preserved.\n\t */\n\tmaxWidth: null | number\n\tminWidth?: null | number\n\t// todo: make this a number so that it is consistent with other TLMeasureTextSpanOpts\n\tpadding: string\n\totherStyles?: Record<string, string>\n\tdisableOverflowWrapBreaking?: boolean\n\tmeasureScrollWidth?: boolean\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n\totherStyles?: Record<string, string>\n\tmeasureScrollWidth?: boolean\n}\n\nconst spaceCharacterRegex = /\\s/\n\nconst initialDefaultStyles = Object.freeze({\n\t'overflow-wrap': 'break-word',\n\t'word-break': 'auto',\n\twidth: null,\n\theight: null,\n\t'max-width': null,\n\t'min-width': null,\n})\n\n/** @public */\nexport class TextManager {\n\tprivate elm: HTMLDivElement\n\tprivate poolElms: PoolItem[] = []\n\n\tconstructor(public editor: Editor) {\n\t\tthis.elm = this.createMeasurementEl()\n\t\tthis.editor.getContainer().appendChild(this.elm)\n\t}\n\n\tprivate createMeasurementEl(): HTMLDivElement {\n\t\tconst elm = this.editor.getContainerDocument().createElement('div')\n\t\telm.classList.add('tl-text')\n\t\telm.classList.add('tl-text-measure')\n\t\telm.setAttribute('dir', 'auto')\n\t\telm.tabIndex = -1\n\t\tfor (const key of objectMapKeys(initialDefaultStyles)) {\n\t\t\telm.style.setProperty(key, initialDefaultStyles[key])\n\t\t}\n\n\t\treturn elm\n\t}\n\n\tprivate resetElementStyles(el: HTMLElement, appliedStyleKeys: string[]) {\n\t\tfor (const key of appliedStyleKeys) {\n\t\t\tif (key in initialDefaultStyles) {\n\t\t\t\tel.style.setProperty(key, initialDefaultStyles[key as keyof typeof initialDefaultStyles])\n\t\t\t} else {\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate setElementStyles(el: HTMLElement, styles: Record<string, string | undefined | null>) {\n\t\ttype StyleValue = string | null\n\t\ttype RestoreEntry = [prop: string, value: StyleValue]\n\n\t\tconst restore: RestoreEntry[] = []\n\n\t\tfor (const [key, nextValue] of Object.entries(styles)) {\n\t\t\tconst oldValue = el.style.getPropertyValue(key)\n\n\t\t\tif (typeof nextValue === 'string') {\n\t\t\t\tif (oldValue === nextValue) continue\n\t\t\t\trestore.push([key, oldValue || null])\n\t\t\t\tel.style.setProperty(key, nextValue)\n\t\t\t} else {\n\t\t\t\tif (!oldValue) continue\n\t\t\t\trestore.push([key, oldValue])\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\n\t\treturn () => {\n\t\t\tfor (const [key, value] of restore) {\n\t\t\t\tif (value === null || value === '') el.style.removeProperty(key)\n\t\t\t\telse el.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getMeasureStyles(opts: TLMeasureTextOpts): Record<string, string | undefined> {\n\t\treturn {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,\n\t\t\tpadding: opts.padding,\n\t\t\t'max-width': opts.maxWidth ? opts.maxWidth + 'px' : undefined,\n\t\t\t'min-width': opts.minWidth ? opts.minWidth + 'px' : undefined,\n\t\t\t'overflow-wrap': opts.disableOverflowWrapBreaking ? 'normal' : 'break-word',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t}\n\n\tdispose() {\n\t\tthis.elm.remove()\n\t\tfor (const { el } of this.poolElms) {\n\t\t\tel.remove()\n\t\t}\n\t\tthis.poolElms.length = 0\n\t}\n\n\tprivate ensurePoolSize(size: number) {\n\t\tif (this.poolElms.length >= size) return\n\n\t\tconst fragment = this.editor.getContainerDocument().createDocumentFragment()\n\t\twhile (this.poolElms.length < size) {\n\t\t\tconst el = this.createMeasurementEl()\n\t\t\tthis.poolElms.push({ el, html: '', appliedStyleKeys: [] })\n\t\t\tfragment.appendChild(el)\n\t\t}\n\t\tthis.editor.getContainer().appendChild(fragment)\n\t}\n\n\tprivate getPoolItem(index: number): PoolItem {\n\t\tthis.ensurePoolSize(index + 1)\n\t\treturn this.poolElms[index]\n\t}\n\n\tmeasureHtmlBatch(requests: BatchMeasurementRequest[]): TLMeasuredTextSize[] {\n\t\tif (requests.length === 0) return []\n\n\t\twhile (this.poolElms.length > requests.length) {\n\t\t\tconst { el } = this.poolElms.pop()!\n\t\t\tel.remove()\n\t\t}\n\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst { html, opts } = requests[i]\n\t\t\tconst poolItem = this.getPoolItem(i)\n\n\t\t\tconst { el } = poolItem\n\t\t\tthis.resetElementStyles(el, poolItem.appliedStyleKeys)\n\t\t\tconst styles = this.getMeasureStyles(opts)\n\t\t\tthis.setElementStyles(el, styles)\n\t\t\tpoolItem.appliedStyleKeys = Object.keys(styles)\n\t\t\t// Skip innerHTML parsing if the content hasn't changed\n\t\t\tif (poolItem.html !== html) {\n\t\t\t\tel.innerHTML = html\n\t\t\t\tpoolItem.html = html\n\t\t\t}\n\t\t}\n\n\t\tconst results: TLMeasuredTextSize[] = []\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst el = this.getPoolItem(i).el\n\t\t\tconst scrollWidth = requests[i].opts.measureScrollWidth ? el.scrollWidth : 0\n\t\t\tconst rect = el.getBoundingClientRect()\n\t\t\tresults.push({\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t})\n\t\t}\n\n\t\treturn results\n\t}\n\n\tmeasureText(textToMeasure: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst div = this.editor.getContainerDocument().createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(html: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst { elm } = this\n\n\t\tconst restoreStyles = this.setElementStyles(elm, this.getMeasureStyles(opts))\n\n\t\ttry {\n\t\t\telm.innerHTML = html\n\n\t\t\tconst scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0\n\t\t\tconst rect = elm.getBoundingClientRect()\n\n\t\t\treturn {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t}\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst { elm } = this\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\tconst newStyles = {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,\n\t\t\twidth: `${elementWidth}px`,\n\t\t\theight: 'min-content',\n\t\t\t'text-align': textAlignmentsForLtr[opts.textAlign],\n\t\t\t'overflow-wrap': shouldTruncateToFirstLine ? 'anywhere' : 'break-word',\n\t\t\t'word-break': shouldTruncateToFirstLine ? 'break-all' : 'normal',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t\tconst restoreStyles = this.setElementStyles(elm, newStyles)\n\n\t\ttry {\n\t\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t\t// Render the text into the measurement element:\n\t\t\telm.textContent = normalizedText\n\n\t\t\t// actually measure the text:\n\t\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine,\n\t\t\t})\n\n\t\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\t\telm.textContent = '\u2026'\n\t\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\t\telm.textContent = normalizedText\n\t\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t\t}).spans\n\n\t\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t\t// browser.\n\t\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\t\ttruncatedSpans.push({\n\t\t\t\t\ttext: '\u2026',\n\t\t\t\t\tbox: {\n\t\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\treturn truncatedSpans\n\t\t\t}\n\n\t\t\treturn spans\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -40,7 +40,6 @@ class TickManager {
|
|
|
40
40
|
const now = Date.now();
|
|
41
41
|
const elapsed = now - this.now;
|
|
42
42
|
this.now = now;
|
|
43
|
-
this.editor.inputs.updatePointerVelocity(elapsed);
|
|
44
43
|
this.editor.emit("frame", elapsed);
|
|
45
44
|
this.editor.emit("tick", elapsed);
|
|
46
45
|
this.cancelRaf = throttleToNextFrame(this.tick);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/TickManager/TickManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\nconst throttleToNextFrame =\n\ttypeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\t\t? // At test time we should use actual raf and not throttle, because throttle was set up to evaluate immediately during tests, which causes stack overflow\n\t\t\t// for the tick manager since it sets up a raf loop.\n\t\t\tfunction mockThrottle(cb: any) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\tconst frame = requestAnimationFrame(cb)\n\t\t\t\treturn () => cancelAnimationFrame(frame)\n\t\t\t}\n\t\t: _throttleToNextFrame\n\n/** @internal */\nexport class TickManager {\n\tconstructor(public editor: Editor) {\n\t\tthis.editor.disposables.add(this.dispose)\n\t\tthis.start()\n\t}\n\n\tcancelRaf?: null | (() => void)\n\tisPaused = true\n\tnow = 0\n\n\tstart() {\n\t\tthis.isPaused = false\n\t\tthis.cancelRaf?.()\n\t\tthis.cancelRaf = throttleToNextFrame(this.tick)\n\t\tthis.now = Date.now()\n\t}\n\n\t@bind\n\ttick() {\n\t\tif (this.isPaused) {\n\t\t\treturn\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst elapsed = now - this.now\n\t\tthis.now = now\n\n\t\tthis.editor.
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,uBAAuB,sBAAsB,YAAY;AAGlE,MAAM,sBACL,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AAAA;AAAA;AAAA,EAGzD,SAAS,aAAa,IAAS;AAE9B,UAAM,QAAQ,sBAAsB,EAAE;AACtC,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACxC;AAAA,IACC;AAGG,MAAM,YAAY;AAAA,EACxB,YAAmB,QAAgB;AAAhB;AAClB,SAAK,OAAO,YAAY,IAAI,KAAK,OAAO;AACxC,SAAK,MAAM;AAAA,EACZ;AAAA,EAHmB;AAAA,EAKnB;AAAA,EACA,WAAW;AAAA,EACX,MAAM;AAAA,EAEN,QAAQ;AACP,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,YAAY,oBAAoB,KAAK,IAAI;AAC9C,SAAK,MAAM,KAAK,IAAI;AAAA,EACrB;AAAA,EAGA,OAAO;AACN,QAAI,KAAK,UAAU;AAClB;AAAA,IACD;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,MAAM,KAAK;AAC3B,SAAK,MAAM;AAEX,SAAK,OAAO,
|
|
4
|
+
"sourcesContent": ["import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\nconst throttleToNextFrame =\n\ttypeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\t\t? // At test time we should use actual raf and not throttle, because throttle was set up to evaluate immediately during tests, which causes stack overflow\n\t\t\t// for the tick manager since it sets up a raf loop.\n\t\t\tfunction mockThrottle(cb: any) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\tconst frame = requestAnimationFrame(cb)\n\t\t\t\treturn () => cancelAnimationFrame(frame)\n\t\t\t}\n\t\t: _throttleToNextFrame\n\n/** @internal */\nexport class TickManager {\n\tconstructor(public editor: Editor) {\n\t\tthis.editor.disposables.add(this.dispose)\n\t\tthis.start()\n\t}\n\n\tcancelRaf?: null | (() => void)\n\tisPaused = true\n\tnow = 0\n\n\tstart() {\n\t\tthis.isPaused = false\n\t\tthis.cancelRaf?.()\n\t\tthis.cancelRaf = throttleToNextFrame(this.tick)\n\t\tthis.now = Date.now()\n\t}\n\n\t@bind\n\ttick() {\n\t\tif (this.isPaused) {\n\t\t\treturn\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst elapsed = now - this.now\n\t\tthis.now = now\n\n\t\tthis.editor.emit('frame', elapsed)\n\t\tthis.editor.emit('tick', elapsed)\n\t\tthis.cancelRaf = throttleToNextFrame(this.tick)\n\t}\n\n\t// Clear the listener\n\t@bind\n\tdispose() {\n\t\tthis.isPaused = true\n\n\t\tthis.cancelRaf?.()\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;AAAA,SAAS,uBAAuB,sBAAsB,YAAY;AAGlE,MAAM,sBACL,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AAAA;AAAA;AAAA,EAGzD,SAAS,aAAa,IAAS;AAE9B,UAAM,QAAQ,sBAAsB,EAAE;AACtC,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACxC;AAAA,IACC;AAGG,MAAM,YAAY;AAAA,EACxB,YAAmB,QAAgB;AAAhB;AAClB,SAAK,OAAO,YAAY,IAAI,KAAK,OAAO;AACxC,SAAK,MAAM;AAAA,EACZ;AAAA,EAHmB;AAAA,EAKnB;AAAA,EACA,WAAW;AAAA,EACX,MAAM;AAAA,EAEN,QAAQ;AACP,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,YAAY,oBAAoB,KAAK,IAAI;AAC9C,SAAK,MAAM,KAAK,IAAI;AAAA,EACrB;AAAA,EAGA,OAAO;AACN,QAAI,KAAK,UAAU;AAClB;AAAA,IACD;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,MAAM,KAAK;AAC3B,SAAK,MAAM;AAEX,SAAK,OAAO,KAAK,SAAS,OAAO;AACjC,SAAK,OAAO,KAAK,QAAQ,OAAO;AAChC,SAAK,YAAY,oBAAoB,KAAK,IAAI;AAAA,EAC/C;AAAA,EAIA,UAAU;AACT,SAAK,WAAW;AAEhB,SAAK,YAAY;AAAA,EAClB;AACD;AArBC;AAAA,EADC;AAAA,GAjBW,YAkBZ;AAgBA;AAAA,EADC;AAAA,GAjCW,YAkCZ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,6 +9,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
9
9
|
return result;
|
|
10
10
|
};
|
|
11
11
|
import { atom, computed } from "@tldraw/state";
|
|
12
|
+
import { createUserId } from "@tldraw/tlschema";
|
|
12
13
|
import { defaultUserPreferences } from "../../../config/TLUserPreferences.mjs";
|
|
13
14
|
import { getGlobalWindow } from "../../../utils/dom.mjs";
|
|
14
15
|
class UserPreferencesManager {
|
|
@@ -45,7 +46,7 @@ class UserPreferencesManager {
|
|
|
45
46
|
}
|
|
46
47
|
getUserPreferences() {
|
|
47
48
|
return {
|
|
48
|
-
id: this.
|
|
49
|
+
id: this.getExternalId(),
|
|
49
50
|
name: this.getName(),
|
|
50
51
|
locale: this.getLocale(),
|
|
51
52
|
color: this.getColor(),
|
|
@@ -84,9 +85,15 @@ class UserPreferencesManager {
|
|
|
84
85
|
getAreKeyboardShortcutsEnabled() {
|
|
85
86
|
return this.user.userPreferences.get().areKeyboardShortcutsEnabled ?? defaultUserPreferences.areKeyboardShortcutsEnabled;
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
+
getExternalId() {
|
|
88
89
|
return this.user.userPreferences.get().id;
|
|
89
90
|
}
|
|
91
|
+
getId() {
|
|
92
|
+
return this.getExternalId();
|
|
93
|
+
}
|
|
94
|
+
getRecordId() {
|
|
95
|
+
return createUserId(this.getExternalId());
|
|
96
|
+
}
|
|
90
97
|
getName() {
|
|
91
98
|
return this.user.userPreferences.get().name?.trim() ?? defaultUserPreferences.name;
|
|
92
99
|
}
|
|
@@ -133,9 +140,15 @@ __decorateClass([
|
|
|
133
140
|
__decorateClass([
|
|
134
141
|
computed
|
|
135
142
|
], UserPreferencesManager.prototype, "getAreKeyboardShortcutsEnabled", 1);
|
|
143
|
+
__decorateClass([
|
|
144
|
+
computed
|
|
145
|
+
], UserPreferencesManager.prototype, "getExternalId", 1);
|
|
136
146
|
__decorateClass([
|
|
137
147
|
computed
|
|
138
148
|
], UserPreferencesManager.prototype, "getId", 1);
|
|
149
|
+
__decorateClass([
|
|
150
|
+
computed
|
|
151
|
+
], UserPreferencesManager.prototype, "getRecordId", 1);
|
|
139
152
|
__decorateClass([
|
|
140
153
|
computed
|
|
141
154
|
], UserPreferencesManager.prototype, "getName", 1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { atom, computed } from '@tldraw/state'\nimport { TLCurrentUser } from '../../../config/createTLCurrentUser'\nimport { TLUserPreferences, defaultUserPreferences } from '../../../config/TLUserPreferences'\nimport { getGlobalWindow } from '../../../utils/dom'\n\n/** @public */\nexport class UserPreferencesManager {\n\tsystemColorScheme = atom<'dark' | 'light'>('systemColorScheme', 'light')\n\tdisposables = new Set<() => void>()\n\tdispose() {\n\t\tthis.disposables.forEach((d) => d())\n\t}\n\tconstructor(\n\t\tprivate readonly user: TLCurrentUser,\n\t\tprivate readonly colorScheme: 'light' | 'dark' | 'system'\n\t) {\n\t\tif (typeof window === 'undefined' || !getGlobalWindow().matchMedia) return\n\n\t\tconst darkModeMediaQuery = getGlobalWindow().matchMedia('(prefers-color-scheme: dark)')\n\t\tif (darkModeMediaQuery?.matches) {\n\t\t\tthis.systemColorScheme.set('dark')\n\t\t}\n\t\tconst handleChange = (e: MediaQueryListEvent) => {\n\t\t\tif (e.matches) {\n\t\t\t\tthis.systemColorScheme.set('dark')\n\t\t\t} else {\n\t\t\t\tthis.systemColorScheme.set('light')\n\t\t\t}\n\t\t}\n\t\tdarkModeMediaQuery?.addEventListener('change', handleChange)\n\t\tthis.disposables.add(() => darkModeMediaQuery?.removeEventListener('change', handleChange))\n\t}\n\n\tupdateUserPreferences(userPreferences: Partial<TLUserPreferences>) {\n\t\tthis.user.setUserPreferences({\n\t\t\t...this.user.userPreferences.get(),\n\t\t\t...userPreferences,\n\t\t})\n\t}\n\t@computed getUserPreferences() {\n\t\treturn {\n\t\t\tid: this.
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,MAAM,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import { atom, computed } from '@tldraw/state'\nimport { createUserId, TLUserId } from '@tldraw/tlschema'\nimport { TLCurrentUser } from '../../../config/createTLCurrentUser'\nimport { TLUserPreferences, defaultUserPreferences } from '../../../config/TLUserPreferences'\nimport { getGlobalWindow } from '../../../utils/dom'\n\n/** @public */\nexport class UserPreferencesManager {\n\tsystemColorScheme = atom<'dark' | 'light'>('systemColorScheme', 'light')\n\tdisposables = new Set<() => void>()\n\tdispose() {\n\t\tthis.disposables.forEach((d) => d())\n\t}\n\tconstructor(\n\t\tprivate readonly user: TLCurrentUser,\n\t\tprivate readonly colorScheme: 'light' | 'dark' | 'system'\n\t) {\n\t\tif (typeof window === 'undefined' || !getGlobalWindow().matchMedia) return\n\n\t\tconst darkModeMediaQuery = getGlobalWindow().matchMedia('(prefers-color-scheme: dark)')\n\t\tif (darkModeMediaQuery?.matches) {\n\t\t\tthis.systemColorScheme.set('dark')\n\t\t}\n\t\tconst handleChange = (e: MediaQueryListEvent) => {\n\t\t\tif (e.matches) {\n\t\t\t\tthis.systemColorScheme.set('dark')\n\t\t\t} else {\n\t\t\t\tthis.systemColorScheme.set('light')\n\t\t\t}\n\t\t}\n\t\tdarkModeMediaQuery?.addEventListener('change', handleChange)\n\t\tthis.disposables.add(() => darkModeMediaQuery?.removeEventListener('change', handleChange))\n\t}\n\n\tupdateUserPreferences(userPreferences: Partial<TLUserPreferences>) {\n\t\tthis.user.setUserPreferences({\n\t\t\t...this.user.userPreferences.get(),\n\t\t\t...userPreferences,\n\t\t})\n\t}\n\t@computed getUserPreferences() {\n\t\treturn {\n\t\t\tid: this.getExternalId(),\n\t\t\tname: this.getName(),\n\t\t\tlocale: this.getLocale(),\n\t\t\tcolor: this.getColor(),\n\t\t\tanimationSpeed: this.getAnimationSpeed(),\n\t\t\tareKeyboardShortcutsEnabled: this.getAreKeyboardShortcutsEnabled(),\n\t\t\tisSnapMode: this.getIsSnapMode(),\n\t\t\tcolorScheme: this.user.userPreferences.get().colorScheme,\n\t\t\tisDarkMode: this.getIsDarkMode(),\n\t\t\tisWrapMode: this.getIsWrapMode(),\n\t\t\tisDynamicResizeMode: this.getIsDynamicResizeMode(),\n\t\t\tenhancedA11yMode: this.getEnhancedA11yMode(),\n\t\t\tinputMode: this.getInputMode(),\n\t\t\tisZoomDirectionInverted: this.getIsZoomDirectionInverted(),\n\t\t}\n\t}\n\n\t@computed getIsDarkMode() {\n\t\tconst userColorScheme = this.user.userPreferences.get().colorScheme\n\t\tconst scheme = userColorScheme ?? this.colorScheme\n\t\tswitch (scheme) {\n\t\t\tcase 'dark':\n\t\t\t\treturn true\n\t\t\tcase 'light':\n\t\t\t\treturn false\n\t\t\tcase 'system':\n\t\t\t\treturn this.systemColorScheme.get() === 'dark'\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * The speed at which the user can scroll by dragging toward the edge of the screen.\n\t */\n\t@computed getEdgeScrollSpeed() {\n\t\treturn this.user.userPreferences.get().edgeScrollSpeed ?? defaultUserPreferences.edgeScrollSpeed\n\t}\n\n\t@computed getAnimationSpeed() {\n\t\treturn this.user.userPreferences.get().animationSpeed ?? defaultUserPreferences.animationSpeed\n\t}\n\n\t@computed getAreKeyboardShortcutsEnabled() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().areKeyboardShortcutsEnabled ??\n\t\t\tdefaultUserPreferences.areKeyboardShortcutsEnabled\n\t\t)\n\t}\n\n\t/**\n\t * The current user's raw, app-provided id \u2014 the value set in the user's\n\t * {@link @tldraw/editor#TLUserPreferences}. Use this when you need the id your application\n\t * assigned to the user. To compare against or look up store records, use\n\t * {@link UserPreferencesManager.getRecordId} instead.\n\t */\n\t@computed getExternalId(): string {\n\t\treturn this.user.userPreferences.get().id\n\t}\n\n\t/**\n\t * @deprecated Use {@link UserPreferencesManager.getExternalId} for the raw app-provided id, or\n\t * {@link UserPreferencesManager.getRecordId} for the prefixed `TLUserId` record id.\n\t */\n\t@computed getId() {\n\t\treturn this.getExternalId()\n\t}\n\n\t/**\n\t * The current user's id as a tldraw {@link @tldraw/tlschema#TLUserId} record id (prefixed\n\t * with `user:`). Use this when comparing against or looking up store records, such as a\n\t * presence record's `userId` or `followingUserId`. For the raw, app-provided id, use\n\t * {@link UserPreferencesManager.getExternalId}.\n\t */\n\t@computed getRecordId(): TLUserId {\n\t\treturn createUserId(this.getExternalId())\n\t}\n\n\t@computed getName() {\n\t\treturn this.user.userPreferences.get().name?.trim() ?? defaultUserPreferences.name\n\t}\n\n\t@computed getLocale() {\n\t\treturn this.user.userPreferences.get().locale ?? defaultUserPreferences.locale\n\t}\n\n\t@computed getColor() {\n\t\treturn this.user.userPreferences.get().color ?? defaultUserPreferences.color\n\t}\n\n\t@computed getIsSnapMode() {\n\t\treturn this.user.userPreferences.get().isSnapMode ?? defaultUserPreferences.isSnapMode\n\t}\n\n\t@computed getIsWrapMode() {\n\t\treturn this.user.userPreferences.get().isWrapMode ?? defaultUserPreferences.isWrapMode\n\t}\n\n\t@computed getIsDynamicResizeMode() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().isDynamicSizeMode ?? defaultUserPreferences.isDynamicSizeMode\n\t\t)\n\t}\n\n\t@computed getIsPasteAtCursorMode() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().isPasteAtCursorMode ??\n\t\t\tdefaultUserPreferences.isPasteAtCursorMode\n\t\t)\n\t}\n\n\t@computed getEnhancedA11yMode() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().enhancedA11yMode ?? defaultUserPreferences.enhancedA11yMode\n\t\t)\n\t}\n\n\t@computed getInputMode() {\n\t\treturn this.user.userPreferences.get().inputMode ?? defaultUserPreferences.inputMode\n\t}\n\n\t@computed getIsZoomDirectionInverted() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().isZoomDirectionInverted ??\n\t\t\tdefaultUserPreferences.isZoomDirectionInverted\n\t\t)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|