@tldraw/editor 5.4.0-next.ef99ab47e5ce → 5.5.0-canary.078a77321dc3
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/dist-cjs/index.d.ts +119 -49
- package/dist-cjs/index.js +4 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +7 -2
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +6 -8
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/default-components/CanvasOverlays.js +1 -3
- package/dist-cjs/lib/components/default-components/CanvasOverlays.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +1 -1
- package/dist-cjs/lib/config/TLUserPreferences.js +6 -2
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +1 -1
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/constants.js +2 -0
- package/dist-cjs/lib/constants.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +190 -300
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +7 -3
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +3 -3
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +8 -8
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +10 -6
- package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -1
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +79 -6
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +7 -0
- package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +0 -2
- package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +7 -3
- package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +12 -1
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/OverlayManager.js +3 -7
- package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +0 -1
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -1
- package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +1 -0
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +1 -1
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +3 -2
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/globals/editors.js +57 -0
- package/dist-cjs/lib/globals/editors.js.map +7 -0
- package/dist-cjs/lib/globals/menus.js +9 -4
- package/dist-cjs/lib/globals/menus.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -0
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +39 -9
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useLocalStore.js +10 -6
- package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +1 -0
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePeerIds.js +2 -1
- package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +9 -2
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +16 -3
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/useLicenseManagerState.js +5 -4
- package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +2 -1
- package/dist-cjs/lib/primitives/Box.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +4 -3
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Edge2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +71 -26
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Point2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +2 -1
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/deepLinks.js +5 -1
- package/dist-cjs/lib/utils/deepLinks.js.map +2 -2
- package/dist-cjs/lib/utils/normalizeWheel.js +2 -0
- package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
- package/dist-cjs/lib/utils/rotation.js +1 -9
- package/dist-cjs/lib/utils/rotation.js.map +2 -2
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +17 -0
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/lib/utils/sync/alerts.js +1 -1
- package/dist-cjs/lib/utils/sync/alerts.js.map +1 -1
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +119 -49
- package/dist-esm/index.mjs +9 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +9 -4
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +6 -8
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +1 -3
- package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +1 -1
- package/dist-esm/lib/config/TLUserPreferences.mjs +6 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +1 -1
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/constants.mjs +2 -0
- package/dist-esm/lib/constants.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +199 -301
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +7 -3
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +3 -3
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +9 -9
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +10 -6
- package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -1
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +81 -8
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +7 -0
- package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +0 -2
- package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +2 -3
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +7 -3
- package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +12 -1
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/OverlayManager.mjs +3 -7
- package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +0 -1
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -1
- package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +1 -0
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +1 -1
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +3 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/globals/editors.mjs +37 -0
- package/dist-esm/lib/globals/editors.mjs.map +7 -0
- package/dist-esm/lib/globals/menus.mjs +9 -4
- package/dist-esm/lib/globals/menus.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -0
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +40 -10
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useLocalStore.mjs +11 -7
- package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +1 -0
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePeerIds.mjs +2 -1
- package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +9 -2
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +16 -3
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/useLicenseManagerState.mjs +5 -4
- package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +2 -1
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +4 -3
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +75 -27
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Point2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +2 -1
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/deepLinks.mjs +5 -1
- package/dist-esm/lib/utils/deepLinks.mjs.map +2 -2
- package/dist-esm/lib/utils/normalizeWheel.mjs +2 -0
- package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
- package/dist-esm/lib/utils/rotation.mjs +1 -9
- package/dist-esm/lib/utils/rotation.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +17 -0
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/alerts.mjs +1 -1
- package/dist-esm/lib/utils/sync/alerts.mjs.map +1 -1
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +13 -7
- package/src/index.ts +6 -1
- package/src/lib/TldrawEditor.tsx +16 -4
- package/src/lib/components/MenuClickCapture.tsx +19 -18
- package/src/lib/components/default-components/CanvasOverlays.tsx +1 -3
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
- package/src/lib/config/TLUserPreferences.test.ts +18 -1
- package/src/lib/config/TLUserPreferences.ts +8 -4
- package/src/lib/config/createTLStore.ts +1 -1
- package/src/lib/constants.ts +8 -0
- package/src/lib/editor/Editor.ts +285 -392
- package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +7 -3
- package/src/lib/editor/managers/ClickManager/ClickManager.ts +5 -3
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +9 -9
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +35 -4
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +13 -7
- package/src/lib/editor/managers/FontManager/FontManager.ts +2 -1
- package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +199 -3
- package/src/lib/editor/managers/InputsManager/InputsManager.ts +128 -9
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +12 -1
- package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +0 -2
- package/src/lib/editor/managers/TextManager/TextManager.ts +2 -6
- package/src/lib/editor/managers/ThemeManager/ThemeManager.test.ts +53 -0
- package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +9 -3
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
- package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +64 -3
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +18 -1
- package/src/lib/editor/overlays/OverlayManager.ts +3 -7
- package/src/lib/editor/overlays/strokeShapeIndicators.ts +0 -1
- package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +5 -1
- package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +3 -0
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -1
- package/src/lib/editor/tools/StateNode.test.ts +64 -0
- package/src/lib/editor/tools/StateNode.ts +12 -2
- package/src/lib/globals/editors.test.ts +76 -0
- package/src/lib/globals/editors.ts +53 -0
- package/src/lib/globals/menus.test.ts +33 -0
- package/src/lib/globals/menus.ts +11 -4
- package/src/lib/hooks/useCanvasEvents.ts +2 -0
- package/src/lib/hooks/useDocumentEvents.ts +60 -16
- package/src/lib/hooks/useLocalStore.ts +16 -8
- package/src/lib/hooks/usePassThroughWheelEvents.ts +3 -0
- package/src/lib/hooks/usePeerIds.ts +2 -1
- package/src/lib/license/LicenseManager.test.ts +160 -0
- package/src/lib/license/LicenseManager.ts +13 -4
- package/src/lib/license/LicenseProvider.tsx +24 -3
- package/src/lib/license/useLicenseManagerState.ts +8 -6
- package/src/lib/primitives/Box.test.ts +9 -1
- package/src/lib/primitives/Box.ts +3 -1
- package/src/lib/primitives/Vec.test.ts +10 -0
- package/src/lib/primitives/Vec.ts +4 -4
- package/src/lib/primitives/geometry/Arc2d.test.ts +235 -3
- package/src/lib/primitives/geometry/Arc2d.ts +1 -1
- package/src/lib/primitives/geometry/CubicBezier2d.test.ts +186 -3
- package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -1
- package/src/lib/primitives/geometry/CubicSpline2d.test.ts +160 -3
- package/src/lib/primitives/geometry/Edge2d.test.ts +21 -3
- package/src/lib/primitives/geometry/Edge2d.ts +1 -1
- package/src/lib/primitives/geometry/Ellipse2d.test.ts +243 -3
- package/src/lib/primitives/geometry/Geometry2d.test.ts +553 -1
- package/src/lib/primitives/geometry/Group2d.test.ts +419 -3
- package/src/lib/primitives/geometry/Group2d.ts +81 -34
- package/src/lib/primitives/geometry/Point2d.test.ts +14 -3
- package/src/lib/primitives/geometry/Point2d.ts +1 -1
- package/src/lib/primitives/geometry/Polyline2d.test.ts +207 -0
- package/src/lib/primitives/geometry/Stadium2d.test.ts +191 -3
- package/src/lib/primitives/utils.test.ts +10 -1
- package/src/lib/primitives/utils.ts +4 -1
- package/src/lib/test/useCanvasEvents.test.tsx +74 -0
- package/src/lib/test/useDocumentEvents.test.tsx +107 -0
- package/src/lib/test/usePassThroughWheelEvents.test.tsx +25 -2
- package/src/lib/utils/deepLinks.test.ts +15 -0
- package/src/lib/utils/deepLinks.ts +7 -1
- package/src/lib/utils/normalizeWheel.test.ts +39 -0
- package/src/lib/utils/normalizeWheel.ts +6 -0
- package/src/lib/utils/rotation.ts +4 -12
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +56 -0
- package/src/lib/utils/sync/TLLocalSyncClient.ts +24 -0
- package/src/lib/utils/sync/alerts.ts +1 -1
- package/src/version.ts +3 -3
- package/src/lib/primitives/geometry/Polyline.test.ts +0 -5
|
@@ -19,9 +19,13 @@ function deriveShapeIdsInCurrentPage(store, getCurrentPageId) {
|
|
|
19
19
|
function fromScratch() {
|
|
20
20
|
const currentPageId = getCurrentPageId();
|
|
21
21
|
lastPageId = currentPageId;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const result = /* @__PURE__ */ new Set();
|
|
23
|
+
for (const id of shapesIndex.get()) {
|
|
24
|
+
if (isShapeInPage(store, currentPageId, store.get(id))) {
|
|
25
|
+
result.add(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
25
29
|
}
|
|
26
30
|
return computed("_shapeIdsInCurrentPage", (prevValue, lastComputedEpoch) => {
|
|
27
31
|
if (isUninitialized(prevValue)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/derivations/shapeIdsInCurrentPage.ts"],
|
|
4
|
-
"sourcesContent": ["import { computed, isUninitialized, RESET_VALUE, withDiff } from '@tldraw/state'\nimport { IncrementalSetConstructor } from '@tldraw/store'\nimport {\n\tisPageId,\n\tisShape,\n\tisShapeId,\n\tTLPageId,\n\tTLShape,\n\tTLShapeId,\n\tTLStore,\n} from '@tldraw/tlschema'\n\n/**\n * Get whether a shape is in the current page.\n *\n * @param store - The tldraw store.\n * @param pageId - The id of the page to check.\n * @param shape - The the shape to check.\n */\nconst isShapeInPage = (store: TLStore, pageId: TLPageId, shape: TLShape): boolean => {\n\twhile (!isPageId(shape.parentId)) {\n\t\tconst parent = store.get(shape.parentId)\n\t\tif (!parent) return false\n\t\tshape = parent\n\t}\n\n\treturn shape.parentId === pageId\n}\n\n/**\n * A derivation that returns a list of shape ids in the current page.\n *\n * @param store - The tldraw store.\n * @param getCurrentPageId - A function that returns the current page id.\n */\nexport function deriveShapeIdsInCurrentPage(store: TLStore, getCurrentPageId: () => TLPageId) {\n\tconst shapesIndex = store.query.ids('shape')\n\tlet lastPageId: null | TLPageId = null\n\tfunction fromScratch() {\n\t\tconst currentPageId = getCurrentPageId()\n\t\tlastPageId = currentPageId\n\t\
|
|
5
|
-
"mappings": "AAAA,SAAS,UAAU,iBAAiB,aAAa,gBAAgB;AACjE,SAAS,iCAAiC;AAC1C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAKM;AASP,MAAM,gBAAgB,CAAC,OAAgB,QAAkB,UAA4B;AACpF,SAAO,CAAC,SAAS,MAAM,QAAQ,GAAG;AACjC,UAAM,SAAS,MAAM,IAAI,MAAM,QAAQ;AACvC,QAAI,CAAC,OAAQ,QAAO;AACpB,YAAQ;AAAA,EACT;AAEA,SAAO,MAAM,aAAa;AAC3B;AAQO,SAAS,4BAA4B,OAAgB,kBAAkC;AAC7F,QAAM,cAAc,MAAM,MAAM,IAAI,OAAO;AAC3C,MAAI,aAA8B;AAClC,WAAS,cAAc;AACtB,UAAM,gBAAgB,iBAAiB;AACvC,iBAAa;AACb,
|
|
4
|
+
"sourcesContent": ["import { computed, isUninitialized, RESET_VALUE, withDiff } from '@tldraw/state'\nimport { IncrementalSetConstructor } from '@tldraw/store'\nimport {\n\tisPageId,\n\tisShape,\n\tisShapeId,\n\tTLPageId,\n\tTLShape,\n\tTLShapeId,\n\tTLStore,\n} from '@tldraw/tlschema'\n\n/**\n * Get whether a shape is in the current page.\n *\n * @param store - The tldraw store.\n * @param pageId - The id of the page to check.\n * @param shape - The the shape to check.\n */\nconst isShapeInPage = (store: TLStore, pageId: TLPageId, shape: TLShape): boolean => {\n\twhile (!isPageId(shape.parentId)) {\n\t\tconst parent = store.get(shape.parentId)\n\t\tif (!parent) return false\n\t\tshape = parent\n\t}\n\n\treturn shape.parentId === pageId\n}\n\n/**\n * A derivation that returns a list of shape ids in the current page.\n *\n * @param store - The tldraw store.\n * @param getCurrentPageId - A function that returns the current page id.\n */\nexport function deriveShapeIdsInCurrentPage(store: TLStore, getCurrentPageId: () => TLPageId) {\n\tconst shapesIndex = store.query.ids('shape')\n\tlet lastPageId: null | TLPageId = null\n\tfunction fromScratch() {\n\t\tconst currentPageId = getCurrentPageId()\n\t\tlastPageId = currentPageId\n\t\tconst result = new Set<TLShapeId>()\n\t\tfor (const id of shapesIndex.get()) {\n\t\t\tif (isShapeInPage(store, currentPageId, store.get(id)!)) {\n\t\t\t\tresult.add(id)\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n\treturn computed<Set<TLShapeId>>('_shapeIdsInCurrentPage', (prevValue, lastComputedEpoch) => {\n\t\tif (isUninitialized(prevValue)) {\n\t\t\treturn fromScratch()\n\t\t}\n\n\t\tconst currentPageId = getCurrentPageId()\n\n\t\tif (currentPageId !== lastPageId) {\n\t\t\treturn fromScratch()\n\t\t}\n\n\t\tconst diff = store.history.getDiffSince(lastComputedEpoch)\n\n\t\tif (diff === RESET_VALUE) {\n\t\t\treturn fromScratch()\n\t\t}\n\n\t\tconst builder = new IncrementalSetConstructor<TLShapeId>(\n\t\t\tprevValue\n\t\t) as IncrementalSetConstructor<TLShapeId>\n\n\t\tfor (const changes of diff) {\n\t\t\tfor (const record of Object.values(changes.added)) {\n\t\t\t\tif (isShape(record) && isShapeInPage(store, currentPageId, record)) {\n\t\t\t\t\tbuilder.add(record.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const [_from, to] of Object.values(changes.updated)) {\n\t\t\t\tif (isShape(to)) {\n\t\t\t\t\tif (isShapeInPage(store, currentPageId, to)) {\n\t\t\t\t\t\tbuilder.add(to.id)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuilder.remove(to.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const id of Object.keys(changes.removed)) {\n\t\t\t\tif (isShapeId(id)) {\n\t\t\t\t\tbuilder.remove(id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst result = builder.get()\n\t\tif (!result) {\n\t\t\treturn prevValue\n\t\t}\n\n\t\treturn withDiff(result.value, result.diff)\n\t})\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,UAAU,iBAAiB,aAAa,gBAAgB;AACjE,SAAS,iCAAiC;AAC1C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAKM;AASP,MAAM,gBAAgB,CAAC,OAAgB,QAAkB,UAA4B;AACpF,SAAO,CAAC,SAAS,MAAM,QAAQ,GAAG;AACjC,UAAM,SAAS,MAAM,IAAI,MAAM,QAAQ;AACvC,QAAI,CAAC,OAAQ,QAAO;AACpB,YAAQ;AAAA,EACT;AAEA,SAAO,MAAM,aAAa;AAC3B;AAQO,SAAS,4BAA4B,OAAgB,kBAAkC;AAC7F,QAAM,cAAc,MAAM,MAAM,IAAI,OAAO;AAC3C,MAAI,aAA8B;AAClC,WAAS,cAAc;AACtB,UAAM,gBAAgB,iBAAiB;AACvC,iBAAa;AACb,UAAM,SAAS,oBAAI,IAAe;AAClC,eAAW,MAAM,YAAY,IAAI,GAAG;AACnC,UAAI,cAAc,OAAO,eAAe,MAAM,IAAI,EAAE,CAAE,GAAG;AACxD,eAAO,IAAI,EAAE;AAAA,MACd;AAAA,IACD;AACA,WAAO;AAAA,EACR;AACA,SAAO,SAAyB,0BAA0B,CAAC,WAAW,sBAAsB;AAC3F,QAAI,gBAAgB,SAAS,GAAG;AAC/B,aAAO,YAAY;AAAA,IACpB;AAEA,UAAM,gBAAgB,iBAAiB;AAEvC,QAAI,kBAAkB,YAAY;AACjC,aAAO,YAAY;AAAA,IACpB;AAEA,UAAM,OAAO,MAAM,QAAQ,aAAa,iBAAiB;AAEzD,QAAI,SAAS,aAAa;AACzB,aAAO,YAAY;AAAA,IACpB;AAEA,UAAM,UAAU,IAAI;AAAA,MACnB;AAAA,IACD;AAEA,eAAW,WAAW,MAAM;AAC3B,iBAAW,UAAU,OAAO,OAAO,QAAQ,KAAK,GAAG;AAClD,YAAI,QAAQ,MAAM,KAAK,cAAc,OAAO,eAAe,MAAM,GAAG;AACnE,kBAAQ,IAAI,OAAO,EAAE;AAAA,QACtB;AAAA,MACD;AAEA,iBAAW,CAAC,OAAO,EAAE,KAAK,OAAO,OAAO,QAAQ,OAAO,GAAG;AACzD,YAAI,QAAQ,EAAE,GAAG;AAChB,cAAI,cAAc,OAAO,eAAe,EAAE,GAAG;AAC5C,oBAAQ,IAAI,GAAG,EAAE;AAAA,UAClB,OAAO;AACN,oBAAQ,OAAO,GAAG,EAAE;AAAA,UACrB;AAAA,QACD;AAAA,MACD;AAEA,iBAAW,MAAM,OAAO,KAAK,QAAQ,OAAO,GAAG;AAC9C,YAAI,UAAU,EAAE,GAAG;AAClB,kBAAQ,OAAO,EAAE;AAAA,QAClB;AAAA,MACD;AAAA,IACD;AAEA,UAAM,SAAS,QAAQ,IAAI;AAC3B,QAAI,CAAC,QAAQ;AACZ,aAAO;AAAA,IACR;AAEA,WAAO,SAAS,OAAO,OAAO,OAAO,IAAI;AAAA,EAC1C,CAAC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -76,7 +76,7 @@ class ClickManager {
|
|
|
76
76
|
switch (this._clickState) {
|
|
77
77
|
case "pendingDouble": {
|
|
78
78
|
this._clickState = "pendingOverflow";
|
|
79
|
-
this.
|
|
79
|
+
this._getClickTimeout(this._clickState);
|
|
80
80
|
return {
|
|
81
81
|
...info,
|
|
82
82
|
type: "click",
|
|
@@ -95,7 +95,7 @@ class ClickManager {
|
|
|
95
95
|
default: {
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
this.
|
|
98
|
+
this._getClickTimeout(this._clickState);
|
|
99
99
|
return info;
|
|
100
100
|
}
|
|
101
101
|
case "pointer_up": {
|
|
@@ -117,7 +117,7 @@ class ClickManager {
|
|
|
117
117
|
return info;
|
|
118
118
|
}
|
|
119
119
|
case "pointer_move": {
|
|
120
|
-
if (this._clickState !== "idle" && this._clickScreenPoint && Vec.Dist2(this._clickScreenPoint,
|
|
120
|
+
if (this._clickState !== "idle" && this._clickScreenPoint && Vec.Dist2(this._clickScreenPoint, info.point) > (this.editor.getInstanceState().isCoarsePointer ? this.editor.options.coarseDragDistanceSquared : this.editor.options.dragDistanceSquared)) {
|
|
121
121
|
this.cancelDoubleClickTimeout();
|
|
122
122
|
}
|
|
123
123
|
return info;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/ClickManager/ClickManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { bind, uniqueId } from '@tldraw/utils'\nimport { Vec } from '../../../primitives/Vec'\nimport type { Editor } from '../../Editor'\nimport { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'\n\n/** @public */\nexport type TLClickState = 'idle' | 'pendingDouble' | 'pendingOverflow' | 'overflow'\n\nconst MAX_CLICK_DISTANCE = 40\n\n/** @public */\nexport class ClickManager {\n\tconstructor(public editor: Editor) {}\n\n\tprivate _clickId = ''\n\n\tprivate _clickTimeout?: any\n\n\tprivate _clickScreenPoint?: Vec\n\n\tprivate _previousScreenPoint?: Vec\n\n\tprivate _isPressingWhilePending = false\n\n\t@bind\n\t_getClickTimeout(state: TLClickState, id = uniqueId()) {\n\t\tthis._clickId = id\n\t\tclearTimeout(this._clickTimeout)\n\t\tthis._clickTimeout = this.editor.timers.setTimeout(\n\t\t\t() => {\n\t\t\t\tif (this._clickState === state && this._clickId === id) {\n\t\t\t\t\tswitch (this._clickState) {\n\t\t\t\t\t\tcase 'pendingOverflow': {\n\t\t\t\t\t\t\tthis.editor.dispatch({\n\t\t\t\t\t\t\t\t...this.lastPointerInfo,\n\t\t\t\t\t\t\t\ttype: 'click',\n\t\t\t\t\t\t\t\tname: 'double_click',\n\t\t\t\t\t\t\t\tphase: this._isPressingWhilePending ? 'settle-down' : 'settle-up',\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t// noop\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._clickState = 'idle'\n\t\t\t\t}\n\t\t\t},\n\t\t\tstate === 'idle' || state === 'pendingDouble'\n\t\t\t\t? this.editor.options.doubleClickDurationMs\n\t\t\t\t: this.editor.options.multiClickDurationMs\n\t\t)\n\t}\n\n\t/**\n\t * The current click state.\n\t *\n\t * @internal\n\t */\n\tprivate _clickState?: TLClickState = 'idle'\n\n\t/**\n\t * The current click state.\n\t *\n\t * @public\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget clickState() {\n\t\treturn this._clickState\n\t}\n\n\tlastPointerInfo = {} as TLPointerEventInfo\n\n\thandlePointerEvent(info: TLPointerEventInfo): TLPointerEventInfo | TLClickEventInfo {\n\t\tswitch (info.name) {\n\t\t\tcase 'pointer_down': {\n\t\t\t\tif (!this._clickState) return info\n\t\t\t\tthis._clickScreenPoint = Vec.From(info.point)\n\n\t\t\t\tthis._isPressingWhilePending = true\n\n\t\t\t\tif (\n\t\t\t\t\tthis._previousScreenPoint &&\n\t\t\t\t\tVec.Dist2(this._previousScreenPoint, this._clickScreenPoint) > MAX_CLICK_DISTANCE ** 2\n\t\t\t\t) {\n\t\t\t\t\tthis._clickState = 'idle'\n\t\t\t\t}\n\n\t\t\t\tthis._previousScreenPoint = this._clickScreenPoint\n\n\t\t\t\tthis.lastPointerInfo = info\n\n\t\t\t\tswitch (this._clickState) {\n\t\t\t\t\tcase 'pendingDouble': {\n\t\t\t\t\t\tthis._clickState = 'pendingOverflow'\n\t\t\t\t\t\tthis.
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,MAAM,gBAAgB;AAC/B,SAAS,WAAW;AAOpB,MAAM,qBAAqB;AAGpB,MAAM,aAAa;AAAA,EACzB,YAAmB,QAAgB;AAAhB;AAAA,EAAiB;AAAA,EAAjB;AAAA,EAEX,WAAW;AAAA,EAEX;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA,0BAA0B;AAAA,EAGlC,iBAAiB,OAAqB,KAAK,SAAS,GAAG;AACtD,SAAK,WAAW;AAChB,iBAAa,KAAK,aAAa;AAC/B,SAAK,gBAAgB,KAAK,OAAO,OAAO;AAAA,MACvC,MAAM;AACL,YAAI,KAAK,gBAAgB,SAAS,KAAK,aAAa,IAAI;AACvD,kBAAQ,KAAK,aAAa;AAAA,YACzB,KAAK,mBAAmB;AACvB,mBAAK,OAAO,SAAS;AAAA,gBACpB,GAAG,KAAK;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,gBACN,OAAO,KAAK,0BAA0B,gBAAgB;AAAA,cACvD,CAAC;AACD;AAAA,YACD;AAAA,YACA,SAAS;AAAA,YAET;AAAA,UACD;AAEA,eAAK,cAAc;AAAA,QACpB;AAAA,MACD;AAAA,MACA,UAAU,UAAU,UAAU,kBAC3B,KAAK,OAAO,QAAQ,wBACpB,KAAK,OAAO,QAAQ;AAAA,IACxB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,cAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQrC,IAAI,aAAa;AAChB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,kBAAkB,CAAC;AAAA,EAEnB,mBAAmB,MAAiE;AACnF,YAAQ,KAAK,MAAM;AAAA,MAClB,KAAK,gBAAgB;AACpB,YAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,aAAK,oBAAoB,IAAI,KAAK,KAAK,KAAK;AAE5C,aAAK,0BAA0B;AAE/B,YACC,KAAK,wBACL,IAAI,MAAM,KAAK,sBAAsB,KAAK,iBAAiB,IAAI,sBAAsB,GACpF;AACD,eAAK,cAAc;AAAA,QACpB;AAEA,aAAK,uBAAuB,KAAK;AAEjC,aAAK,kBAAkB;AAEvB,gBAAQ,KAAK,aAAa;AAAA,UACzB,KAAK,iBAAiB;AACrB,iBAAK,cAAc;AACnB,iBAAK,
|
|
4
|
+
"sourcesContent": ["import { bind, uniqueId } from '@tldraw/utils'\nimport { Vec } from '../../../primitives/Vec'\nimport type { Editor } from '../../Editor'\nimport { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'\n\n/** @public */\nexport type TLClickState = 'idle' | 'pendingDouble' | 'pendingOverflow' | 'overflow'\n\nconst MAX_CLICK_DISTANCE = 40\n\n/** @public */\nexport class ClickManager {\n\tconstructor(public editor: Editor) {}\n\n\tprivate _clickId = ''\n\n\tprivate _clickTimeout?: any\n\n\tprivate _clickScreenPoint?: Vec\n\n\tprivate _previousScreenPoint?: Vec\n\n\tprivate _isPressingWhilePending = false\n\n\t@bind\n\t_getClickTimeout(state: TLClickState, id = uniqueId()) {\n\t\tthis._clickId = id\n\t\tclearTimeout(this._clickTimeout)\n\t\tthis._clickTimeout = this.editor.timers.setTimeout(\n\t\t\t() => {\n\t\t\t\tif (this._clickState === state && this._clickId === id) {\n\t\t\t\t\tswitch (this._clickState) {\n\t\t\t\t\t\tcase 'pendingOverflow': {\n\t\t\t\t\t\t\tthis.editor.dispatch({\n\t\t\t\t\t\t\t\t...this.lastPointerInfo,\n\t\t\t\t\t\t\t\ttype: 'click',\n\t\t\t\t\t\t\t\tname: 'double_click',\n\t\t\t\t\t\t\t\tphase: this._isPressingWhilePending ? 'settle-down' : 'settle-up',\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t// noop\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._clickState = 'idle'\n\t\t\t\t}\n\t\t\t},\n\t\t\tstate === 'idle' || state === 'pendingDouble'\n\t\t\t\t? this.editor.options.doubleClickDurationMs\n\t\t\t\t: this.editor.options.multiClickDurationMs\n\t\t)\n\t}\n\n\t/**\n\t * The current click state.\n\t *\n\t * @internal\n\t */\n\tprivate _clickState?: TLClickState = 'idle'\n\n\t/**\n\t * The current click state.\n\t *\n\t * @public\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget clickState() {\n\t\treturn this._clickState\n\t}\n\n\tlastPointerInfo = {} as TLPointerEventInfo\n\n\thandlePointerEvent(info: TLPointerEventInfo): TLPointerEventInfo | TLClickEventInfo {\n\t\tswitch (info.name) {\n\t\t\tcase 'pointer_down': {\n\t\t\t\tif (!this._clickState) return info\n\t\t\t\tthis._clickScreenPoint = Vec.From(info.point)\n\n\t\t\t\tthis._isPressingWhilePending = true\n\n\t\t\t\tif (\n\t\t\t\t\tthis._previousScreenPoint &&\n\t\t\t\t\tVec.Dist2(this._previousScreenPoint, this._clickScreenPoint) > MAX_CLICK_DISTANCE ** 2\n\t\t\t\t) {\n\t\t\t\t\tthis._clickState = 'idle'\n\t\t\t\t}\n\n\t\t\t\tthis._previousScreenPoint = this._clickScreenPoint\n\n\t\t\t\tthis.lastPointerInfo = info\n\n\t\t\t\tswitch (this._clickState) {\n\t\t\t\t\tcase 'pendingDouble': {\n\t\t\t\t\t\tthis._clickState = 'pendingOverflow'\n\t\t\t\t\t\tthis._getClickTimeout(this._clickState)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...info,\n\t\t\t\t\t\t\ttype: 'click',\n\t\t\t\t\t\t\tname: 'double_click',\n\t\t\t\t\t\t\tphase: 'down',\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcase 'idle': {\n\t\t\t\t\t\tthis._clickState = 'pendingDouble'\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'pendingOverflow': {\n\t\t\t\t\t\tthis._clickState = 'overflow'\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\t// overflow\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._getClickTimeout(this._clickState)\n\t\t\t\treturn info\n\t\t\t}\n\t\t\tcase 'pointer_up': {\n\t\t\t\tif (!this._clickState) return info\n\n\t\t\t\tthis._clickScreenPoint = Vec.From(info.point)\n\n\t\t\t\tthis._isPressingWhilePending = false\n\n\t\t\t\tswitch (this._clickState) {\n\t\t\t\t\tcase 'pendingOverflow': {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...this.lastPointerInfo,\n\t\t\t\t\t\t\ttype: 'click',\n\t\t\t\t\t\t\tname: 'double_click',\n\t\t\t\t\t\t\tphase: 'up',\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\t// idle, pendingDouble, overflow\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn info\n\t\t\t}\n\t\t\tcase 'pointer_move': {\n\t\t\t\t// Compare in client coordinates like _clickScreenPoint; the container-relative\n\t\t\t\t// inputs screen point would add the container offset and cancel on any movement.\n\t\t\t\tif (\n\t\t\t\t\tthis._clickState !== 'idle' &&\n\t\t\t\t\tthis._clickScreenPoint &&\n\t\t\t\t\tVec.Dist2(this._clickScreenPoint, info.point) >\n\t\t\t\t\t\t(this.editor.getInstanceState().isCoarsePointer\n\t\t\t\t\t\t\t? this.editor.options.coarseDragDistanceSquared\n\t\t\t\t\t\t\t: this.editor.options.dragDistanceSquared)\n\t\t\t\t) {\n\t\t\t\t\tthis.cancelDoubleClickTimeout()\n\t\t\t\t}\n\t\t\t\treturn info\n\t\t\t}\n\t\t}\n\t\treturn info\n\t}\n\n\t/**\n\t * Cancel the double click timeout.\n\t *\n\t * @internal\n\t */\n\t@bind\n\tcancelDoubleClickTimeout() {\n\t\tthis._clickTimeout = clearTimeout(this._clickTimeout)\n\t\tthis._clickState = 'idle'\n\t\t// when a double click is cancelled, we are no longer pending any further\n\t\t// clicks, so we set this to false even if the user is still pressing\n\t\tthis._isPressingWhilePending = false\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
9
9
|
return result;
|
|
10
10
|
};
|
|
11
11
|
import { EMPTY_ARRAY, atom, computed } from "@tldraw/state";
|
|
12
|
-
import { areArraysShallowEqual
|
|
12
|
+
import { areArraysShallowEqual } from "@tldraw/utils";
|
|
13
13
|
class CollaboratorsManager {
|
|
14
14
|
constructor(editor) {
|
|
15
15
|
this.editor = editor;
|
|
@@ -36,14 +36,14 @@ class CollaboratorsManager {
|
|
|
36
36
|
getCollaborators() {
|
|
37
37
|
const allPresenceRecords = this._getCollaboratorsQuery().get();
|
|
38
38
|
if (!allPresenceRecords.length) return EMPTY_ARRAY;
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
const latestByUserId = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const presence of allPresenceRecords) {
|
|
41
|
+
const latest = latestByUserId.get(presence.userId);
|
|
42
|
+
if (!latest || (presence.lastActivityTimestamp ?? 0) > (latest.lastActivityTimestamp ?? 0)) {
|
|
43
|
+
latestByUserId.set(presence.userId, presence);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...latestByUserId.keys()].sort().map((id) => latestByUserId.get(id));
|
|
47
47
|
}
|
|
48
48
|
getCollaboratorsOnCurrentPage() {
|
|
49
49
|
const currentPageId = this.editor.getCurrentPageId();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport type { TLInstancePresence } from '@tldraw/tlschema'\nimport { areArraysShallowEqual
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,aAAa,MAAM,gBAAgB;AAE5C,SAAS,
|
|
4
|
+
"sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport type { TLInstancePresence } from '@tldraw/tlschema'\nimport { areArraysShallowEqual } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * Tracks remote peers and exposes the collaborator-related queries used by the\n * editor and its overlays. Encapsulates the visibility clock that periodically\n * re-evaluates which collaborators should be visible based on activity.\n *\n * Accessed via {@link Editor.collaborators}.\n *\n * @public\n */\nexport class CollaboratorsManager {\n\tconstructor(private readonly editor: Editor) {}\n\n\tprivate _visibilityClockStarted = false\n\n\tprivate _startVisibilityClock() {\n\t\tif (this._visibilityClockStarted) return\n\t\tthis._visibilityClockStarted = true\n\n\t\t// Editor disposes `editor.timers` on its own teardown, so the interval is\n\t\t// automatically cleared when the editor is disposed.\n\t\tthis.editor.timers.setInterval(() => {\n\t\t\tthis._visibilityClock.set(Date.now())\n\t\t}, this.editor.options.collaboratorCheckIntervalMs)\n\t}\n\n\t/**\n\t * Drives reactive re-evaluation of {@link CollaboratorsManager.getVisibleCollaborators}.\n\t * Ticked on a fixed interval so callers don't need to manage their own activity timers.\n\t */\n\tprivate readonly _visibilityClock = atom('collaboratorVisibilityClock', Date.now())\n\n\t@computed\n\tprivate _getCollaboratorsQuery() {\n\t\treturn this.editor.store.query.records('instance_presence', () => ({\n\t\t\tuserId: { neq: this.editor.user.getRecordId() },\n\t\t}))\n\t}\n\n\t// These queries all derive fresh arrays with map/filter, so they compare results with\n\t// shallow (element-identity) equality: a presence update that leaves a derived list's\n\t// elements unchanged \u2014 e.g. a peer moving on another page \u2014 keeps the previous array's\n\t// identity and doesn't invalidate downstream subscribers such as the cursor layer.\n\n\t/**\n\t * Returns a list of presence records for all peer collaborators.\n\t * This will return the latest presence record for each connected user.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetCollaborators(): TLInstancePresence[] {\n\t\tconst allPresenceRecords = this._getCollaboratorsQuery().get()\n\t\tif (!allPresenceRecords.length) return EMPTY_ARRAY\n\t\tconst latestByUserId = new Map<string, TLInstancePresence>()\n\t\tfor (const presence of allPresenceRecords) {\n\t\t\tconst latest = latestByUserId.get(presence.userId)\n\t\t\tif (!latest || (presence.lastActivityTimestamp ?? 0) > (latest.lastActivityTimestamp ?? 0)) {\n\t\t\t\tlatestByUserId.set(presence.userId, presence)\n\t\t\t}\n\t\t}\n\t\treturn [...latestByUserId.keys()].sort().map((id) => latestByUserId.get(id)!)\n\t}\n\n\t/**\n\t * Returns a list of presence records for all peer collaborators on the current page.\n\t * This will return the latest presence record for each connected user.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetCollaboratorsOnCurrentPage(): TLInstancePresence[] {\n\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\treturn this.getCollaborators().filter((c) => c.currentPageId === currentPageId)\n\t}\n\n\t/**\n\t * Returns a list of presence records for peer collaborators who should currently be\n\t * shown in the UI. Filters {@link CollaboratorsManager.getCollaborators} by activity\n\t * state (active / idle / inactive) and visibility rules such as following and\n\t * highlighted users. Re-evaluates on the visibility clock, so callers don't need to\n\t * drive their own activity timer.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetVisibleCollaborators(): TLInstancePresence[] {\n\t\tconst { editor } = this\n\t\tconst { collaboratorInactiveTimeoutMs, collaboratorIdleTimeoutMs } = editor.options\n\n\t\tthis._startVisibilityClock()\n\t\tthis._visibilityClock.get()\n\t\tconst now = Date.now()\n\t\tconst collaborators = this.getCollaborators()\n\t\tif (!collaborators.length) return EMPTY_ARRAY\n\n\t\tconst { followingUserId, highlightedUserIds } = this.editor.getInstanceState()\n\t\tconst currentUserId = this.editor.user.getRecordId()\n\n\t\treturn collaborators.filter((presence) => {\n\t\t\tconst { lastActivityTimestamp, userId, chatMessage } = presence\n\n\t\t\t// Treat a missing or zero `lastActivityTimestamp` as \"active right now\"\n\t\t\t// (elapsed = 0) so newly-joined peers aren't immediately classified as\n\t\t\t// idle/inactive. The broadcast default for peers who haven't moved their\n\t\t\t// pointer yet is `0` (e.g. someone on a touch device who joins and just\n\t\t\t// watches), so a plain `?? now` would leave them hidden. See issue #9017.\n\t\t\tconst elapsed = lastActivityTimestamp ? Math.max(0, now - lastActivityTimestamp) : 0\n\n\t\t\tif (elapsed > collaboratorInactiveTimeoutMs) {\n\t\t\t\t// Inactive: If they're inactive, only show if we're following them or they're highlighted\n\t\t\t\treturn followingUserId === userId || highlightedUserIds.includes(userId)\n\t\t\t}\n\n\t\t\tif (elapsed > collaboratorIdleTimeoutMs) {\n\t\t\t\t// Idle: If they're idle and following us, hide them unless they have a chat message or are highlighted\n\t\t\t\tif (presence.followingUserId === currentUserId) {\n\t\t\t\t\treturn !!(chatMessage || highlightedUserIds.includes(userId))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Active\n\t\t\treturn true\n\t\t})\n\t}\n\n\t/**\n\t * Returns a list of presence records for peer collaborators who should currently be\n\t * shown in the UI, filtered to those on the current page.\n\t */\n\t@computed({ isEqual: areArraysShallowEqual })\n\tgetVisibleCollaboratorsOnCurrentPage(): TLInstancePresence[] {\n\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\treturn this.getVisibleCollaborators().filter((c) => c.currentPageId === currentPageId)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FRAME_MS_60HZ } from "../../../constants.mjs";
|
|
1
2
|
import { EASINGS } from "../../../primitives/easings.mjs";
|
|
2
3
|
import { Vec } from "../../../primitives/Vec.mjs";
|
|
3
4
|
const EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX = 1e3;
|
|
@@ -40,10 +41,13 @@ class EdgeScrollManager {
|
|
|
40
41
|
this._edgeScrollDuration / (editor.options.edgeScrollDelay + editor.options.edgeScrollEaseDuration)
|
|
41
42
|
)
|
|
42
43
|
) : 1;
|
|
43
|
-
this.moveCameraWhenCloseToEdge(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
this.moveCameraWhenCloseToEdge(
|
|
45
|
+
{
|
|
46
|
+
x: edgeScrollProximityFactor.x * eased,
|
|
47
|
+
y: edgeScrollProximityFactor.y * eased
|
|
48
|
+
},
|
|
49
|
+
elapsed
|
|
50
|
+
);
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
}
|
|
@@ -90,14 +94,14 @@ class EdgeScrollManager {
|
|
|
90
94
|
* Moves the camera when the mouse is close to the edge of the screen.
|
|
91
95
|
* @public
|
|
92
96
|
*/
|
|
93
|
-
moveCameraWhenCloseToEdge(proximityFactor) {
|
|
97
|
+
moveCameraWhenCloseToEdge(proximityFactor, elapsed) {
|
|
94
98
|
if (proximityFactor.x === 0 && proximityFactor.y === 0) return;
|
|
95
99
|
const { editor } = this;
|
|
96
100
|
const screenBounds = editor.getViewportScreenBounds();
|
|
97
101
|
const screenSizeFactorX = screenBounds.w < EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX ? EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR : 1;
|
|
98
102
|
const screenSizeFactorY = screenBounds.h < EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX ? EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR : 1;
|
|
99
103
|
const zoomLevel = editor.getZoomLevel();
|
|
100
|
-
const pxSpeed = editor.user.getEdgeScrollSpeed() * editor.options.edgeScrollSpeed;
|
|
104
|
+
const pxSpeed = editor.user.getEdgeScrollSpeed() * editor.options.edgeScrollSpeed * (elapsed / FRAME_MS_60HZ);
|
|
101
105
|
const scrollDeltaX = pxSpeed * proximityFactor.x * screenSizeFactorX / zoomLevel;
|
|
102
106
|
const scrollDeltaY = pxSpeed * proximityFactor.y * screenSizeFactorY / zoomLevel;
|
|
103
107
|
const { x, y, z } = editor.getCamera();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { EASINGS } from '../../../primitives/easings'\nimport { Vec } from '../../../primitives/Vec'\nimport type { Editor } from '../../Editor'\n\n// Tuned for touch/small-screen feel; adjust together with coarsePointerWidth.\nconst EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX = 1000\nconst EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR = 0.612\n\n/** @public */\nexport class EdgeScrollManager {\n\tconstructor(public editor: Editor) {}\n\n\tprivate _isEdgeScrolling = false\n\tprivate _edgeScrollDuration = -1\n\n\tgetIsEdgeScrolling() {\n\t\treturn this._isEdgeScrolling\n\t}\n\n\t/**\n\t * Update the camera position when the mouse is close to the edge of the screen.\n\t * Run this on every tick when in a state where edge scrolling is enabled.\n\t *\n\t * @public\n\t */\n\tupdateEdgeScrolling(elapsed: number) {\n\t\tconst { editor } = this\n\n\t\tif (editor.getCameraOptions().isLocked) return\n\n\t\tconst edgeScrollProximityFactor = this.getEdgeScroll()\n\t\tif (edgeScrollProximityFactor.x === 0 && edgeScrollProximityFactor.y === 0) {\n\t\t\tif (this._isEdgeScrolling) {\n\t\t\t\tthis._isEdgeScrolling = false\n\t\t\t\tthis._edgeScrollDuration = 0\n\t\t\t}\n\t\t} else {\n\t\t\tif (!this._isEdgeScrolling) {\n\t\t\t\tthis._isEdgeScrolling = true\n\t\t\t\tthis._edgeScrollDuration = 0\n\t\t\t}\n\t\t\tthis._edgeScrollDuration += elapsed\n\t\t\tif (this._edgeScrollDuration > editor.options.edgeScrollDelay) {\n\t\t\t\tconst eased =\n\t\t\t\t\teditor.options.edgeScrollEaseDuration > 0\n\t\t\t\t\t\t? EASINGS.easeInCubic(\n\t\t\t\t\t\t\t\tMath.min(\n\t\t\t\t\t\t\t\t\t1,\n\t\t\t\t\t\t\t\t\tthis._edgeScrollDuration /\n\t\t\t\t\t\t\t\t\t\t(editor.options.edgeScrollDelay + editor.options.edgeScrollEaseDuration)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: 1\n\t\t\t\tthis.moveCameraWhenCloseToEdge({\n\t\t\t\t\tx: edgeScrollProximityFactor.x * eased,\n\t\t\t\t\ty: edgeScrollProximityFactor.y * eased,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Helper function to get the scroll proximity factor for a given position.\n\t * @param position - The mouse position on the axis.\n\t * @param dimension - The component dimension on the axis.\n\t * @param isCoarse - Whether the pointer is coarse.\n\t * @param insetStart - Whether the pointer is inset at the start of the axis.\n\t * @param insetEnd - Whether the pointer is inset at the end of the axis.\n\t * @internal\n\t */\n\tprivate getEdgeProximityFactors(\n\t\tposition: number,\n\t\tdimension: number,\n\t\tisCoarse: boolean,\n\t\tinsetStart: boolean,\n\t\tinsetEnd: boolean\n\t) {\n\t\tconst { editor } = this\n\t\tconst dist = editor.options.edgeScrollDistance\n\t\tconst pw = isCoarse ? editor.options.coarsePointerWidth : 0 // pointer width\n\t\tconst pMin = position - pw\n\t\tconst pMax = position + pw\n\t\tconst min = insetStart ? 0 : dist\n\t\tconst max = insetEnd ? dimension : dimension - dist\n\t\tif (pMin < min) {\n\t\t\treturn Math.min(1, (min - pMin) / dist)\n\t\t} else if (pMax > max) {\n\t\t\treturn -Math.min(1, (pMax - max) / dist)\n\t\t}\n\t\treturn 0\n\t}\n\n\tprivate getEdgeScroll() {\n\t\tconst { editor } = this\n\t\tconst { x, y } = editor.inputs.getCurrentScreenPoint()\n\t\tconst screenBounds = editor.getViewportScreenBounds()\n\n\t\tconst {\n\t\t\tisCoarsePointer,\n\t\t\tinsets: [t, r, b, l],\n\t\t} = editor.getInstanceState()\n\t\tconst proximityFactorX = this.getEdgeProximityFactors(x, screenBounds.w, isCoarsePointer, l, r)\n\t\tconst proximityFactorY = this.getEdgeProximityFactors(y, screenBounds.h, isCoarsePointer, t, b)\n\n\t\treturn {\n\t\t\tx: proximityFactorX,\n\t\t\ty: proximityFactorY,\n\t\t}\n\t}\n\n\t/**\n\t * Moves the camera when the mouse is close to the edge of the screen.\n\t * @public\n\t */\n\tprivate moveCameraWhenCloseToEdge(proximityFactor: { x: number; y: number }) {\n\t\tif (proximityFactor.x === 0 && proximityFactor.y === 0) return\n\t\tconst { editor } = this\n\n\t\tconst screenBounds = editor.getViewportScreenBounds()\n\n\t\t// Determines how much the speed is affected by the screen size\n\t\tconst screenSizeFactorX =\n\t\t\tscreenBounds.w < EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX\n\t\t\t\t? EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR\n\t\t\t\t: 1\n\t\tconst screenSizeFactorY =\n\t\t\tscreenBounds.h < EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX\n\t\t\t\t? EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR\n\t\t\t\t: 1\n\n\t\t// Determines the base speed of the scroll\n\t\tconst zoomLevel = editor.getZoomLevel()\n\t\tconst pxSpeed
|
|
5
|
-
"mappings": "AAAA,SAAS,eAAe;AACxB,SAAS,WAAW;AAIpB,MAAM,wCAAwC;AAC9C,MAAM,wCAAwC;AAGvC,MAAM,kBAAkB;AAAA,EAC9B,YAAmB,QAAgB;AAAhB;AAAA,EAAiB;AAAA,EAAjB;AAAA,EAEX,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EAE9B,qBAAqB;AACpB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,oBAAoB,SAAiB;AACpC,UAAM,EAAE,OAAO,IAAI;AAEnB,QAAI,OAAO,iBAAiB,EAAE,SAAU;AAExC,UAAM,4BAA4B,KAAK,cAAc;AACrD,QAAI,0BAA0B,MAAM,KAAK,0BAA0B,MAAM,GAAG;AAC3E,UAAI,KAAK,kBAAkB;AAC1B,aAAK,mBAAmB;AACxB,aAAK,sBAAsB;AAAA,MAC5B;AAAA,IACD,OAAO;AACN,UAAI,CAAC,KAAK,kBAAkB;AAC3B,aAAK,mBAAmB;AACxB,aAAK,sBAAsB;AAAA,MAC5B;AACA,WAAK,uBAAuB;AAC5B,UAAI,KAAK,sBAAsB,OAAO,QAAQ,iBAAiB;AAC9D,cAAM,QACL,OAAO,QAAQ,yBAAyB,IACrC,QAAQ;AAAA,UACR,KAAK;AAAA,YACJ;AAAA,YACA,KAAK,uBACH,OAAO,QAAQ,kBAAkB,OAAO,QAAQ;AAAA,UACnD;AAAA,QACD,IACC;AACJ,aAAK,
|
|
4
|
+
"sourcesContent": ["import { FRAME_MS_60HZ } from '../../../constants'\nimport { EASINGS } from '../../../primitives/easings'\nimport { Vec } from '../../../primitives/Vec'\nimport type { Editor } from '../../Editor'\n\n// Tuned for touch/small-screen feel; adjust together with coarsePointerWidth.\nconst EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX = 1000\nconst EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR = 0.612\n\n/** @public */\nexport class EdgeScrollManager {\n\tconstructor(public editor: Editor) {}\n\n\tprivate _isEdgeScrolling = false\n\tprivate _edgeScrollDuration = -1\n\n\tgetIsEdgeScrolling() {\n\t\treturn this._isEdgeScrolling\n\t}\n\n\t/**\n\t * Update the camera position when the mouse is close to the edge of the screen.\n\t * Run this on every tick when in a state where edge scrolling is enabled.\n\t *\n\t * @public\n\t */\n\tupdateEdgeScrolling(elapsed: number) {\n\t\tconst { editor } = this\n\n\t\tif (editor.getCameraOptions().isLocked) return\n\n\t\tconst edgeScrollProximityFactor = this.getEdgeScroll()\n\t\tif (edgeScrollProximityFactor.x === 0 && edgeScrollProximityFactor.y === 0) {\n\t\t\tif (this._isEdgeScrolling) {\n\t\t\t\tthis._isEdgeScrolling = false\n\t\t\t\tthis._edgeScrollDuration = 0\n\t\t\t}\n\t\t} else {\n\t\t\tif (!this._isEdgeScrolling) {\n\t\t\t\tthis._isEdgeScrolling = true\n\t\t\t\tthis._edgeScrollDuration = 0\n\t\t\t}\n\t\t\tthis._edgeScrollDuration += elapsed\n\t\t\tif (this._edgeScrollDuration > editor.options.edgeScrollDelay) {\n\t\t\t\tconst eased =\n\t\t\t\t\teditor.options.edgeScrollEaseDuration > 0\n\t\t\t\t\t\t? EASINGS.easeInCubic(\n\t\t\t\t\t\t\t\tMath.min(\n\t\t\t\t\t\t\t\t\t1,\n\t\t\t\t\t\t\t\t\tthis._edgeScrollDuration /\n\t\t\t\t\t\t\t\t\t\t(editor.options.edgeScrollDelay + editor.options.edgeScrollEaseDuration)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: 1\n\t\t\t\tthis.moveCameraWhenCloseToEdge(\n\t\t\t\t\t{\n\t\t\t\t\t\tx: edgeScrollProximityFactor.x * eased,\n\t\t\t\t\t\ty: edgeScrollProximityFactor.y * eased,\n\t\t\t\t\t},\n\t\t\t\t\telapsed\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Helper function to get the scroll proximity factor for a given position.\n\t * @param position - The mouse position on the axis.\n\t * @param dimension - The component dimension on the axis.\n\t * @param isCoarse - Whether the pointer is coarse.\n\t * @param insetStart - Whether the pointer is inset at the start of the axis.\n\t * @param insetEnd - Whether the pointer is inset at the end of the axis.\n\t * @internal\n\t */\n\tprivate getEdgeProximityFactors(\n\t\tposition: number,\n\t\tdimension: number,\n\t\tisCoarse: boolean,\n\t\tinsetStart: boolean,\n\t\tinsetEnd: boolean\n\t) {\n\t\tconst { editor } = this\n\t\tconst dist = editor.options.edgeScrollDistance\n\t\tconst pw = isCoarse ? editor.options.coarsePointerWidth : 0 // pointer width\n\t\tconst pMin = position - pw\n\t\tconst pMax = position + pw\n\t\tconst min = insetStart ? 0 : dist\n\t\tconst max = insetEnd ? dimension : dimension - dist\n\t\tif (pMin < min) {\n\t\t\treturn Math.min(1, (min - pMin) / dist)\n\t\t} else if (pMax > max) {\n\t\t\treturn -Math.min(1, (pMax - max) / dist)\n\t\t}\n\t\treturn 0\n\t}\n\n\tprivate getEdgeScroll() {\n\t\tconst { editor } = this\n\t\tconst { x, y } = editor.inputs.getCurrentScreenPoint()\n\t\tconst screenBounds = editor.getViewportScreenBounds()\n\n\t\tconst {\n\t\t\tisCoarsePointer,\n\t\t\tinsets: [t, r, b, l],\n\t\t} = editor.getInstanceState()\n\t\tconst proximityFactorX = this.getEdgeProximityFactors(x, screenBounds.w, isCoarsePointer, l, r)\n\t\tconst proximityFactorY = this.getEdgeProximityFactors(y, screenBounds.h, isCoarsePointer, t, b)\n\n\t\treturn {\n\t\t\tx: proximityFactorX,\n\t\t\ty: proximityFactorY,\n\t\t}\n\t}\n\n\t/**\n\t * Moves the camera when the mouse is close to the edge of the screen.\n\t * @public\n\t */\n\tprivate moveCameraWhenCloseToEdge(proximityFactor: { x: number; y: number }, elapsed: number) {\n\t\tif (proximityFactor.x === 0 && proximityFactor.y === 0) return\n\t\tconst { editor } = this\n\n\t\tconst screenBounds = editor.getViewportScreenBounds()\n\n\t\t// Determines how much the speed is affected by the screen size\n\t\tconst screenSizeFactorX =\n\t\t\tscreenBounds.w < EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX\n\t\t\t\t? EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR\n\t\t\t\t: 1\n\t\tconst screenSizeFactorY =\n\t\t\tscreenBounds.h < EDGE_SCROLL_SMALL_SCREEN_THRESHOLD_PX\n\t\t\t\t? EDGE_SCROLL_SMALL_SCREEN_SPEED_FACTOR\n\t\t\t\t: 1\n\n\t\t// Determines the base speed of the scroll. edgeScrollSpeed is px per 60 Hz frame, so scale\n\t\t// by elapsed time or a 120 Hz display scrolls twice as fast.\n\t\tconst zoomLevel = editor.getZoomLevel()\n\t\tconst pxSpeed =\n\t\t\teditor.user.getEdgeScrollSpeed() * editor.options.edgeScrollSpeed * (elapsed / FRAME_MS_60HZ)\n\t\tconst scrollDeltaX = (pxSpeed * proximityFactor.x * screenSizeFactorX) / zoomLevel\n\t\tconst scrollDeltaY = (pxSpeed * proximityFactor.y * screenSizeFactorY) / zoomLevel\n\n\t\t// update the camera\n\t\tconst { x, y, z } = editor.getCamera()\n\t\teditor.setCamera(new Vec(x + scrollDeltaX, y + scrollDeltaY, z))\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
mapObjectMapValues,
|
|
8
8
|
objectMapEntries
|
|
9
9
|
} from "@tldraw/utils";
|
|
10
|
+
import { areShapesContentEqual } from "../../../utils/areShapesContentEqual.mjs";
|
|
10
11
|
const EMPTY_SHAPE_FONT_FACES_CACHE = { get: () => void 0 };
|
|
11
12
|
const EMPTY_SHAPE_FONT_LOAD_STATE_CACHE = { get: () => void 0 };
|
|
12
13
|
class FontManager {
|
|
@@ -21,7 +22,7 @@ class FontManager {
|
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
24
|
areResultsEqual: areArraysShallowEqual,
|
|
24
|
-
areRecordsEqual:
|
|
25
|
+
areRecordsEqual: areShapesContentEqual
|
|
25
26
|
}
|
|
26
27
|
);
|
|
27
28
|
this.shapeFontLoadStateCache = editor.store.createCache(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/FontManager/FontManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { computed, EMPTY_ARRAY, transact } from '@tldraw/state'\nimport { AtomMap } from '@tldraw/store'\nimport { TLFontFace, TLShape, TLShapeId } from '@tldraw/tlschema'\nimport {\n\tareArraysShallowEqual,\n\tcompact,\n\tFileHelpers,\n\tmapObjectMapValues,\n\tobjectMapEntries,\n} from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\ninterface FontState {\n\treadonly state: 'loading' | 'ready' | 'error'\n\treadonly instance: FontFace\n\treadonly loadingPromise: Promise<void>\n}\n\ninterface ShapeFontFacesCache {\n\tget(id: TLShapeId): TLFontFace[] | undefined\n}\n\ninterface ShapeFontLoadStateCache {\n\tget(id: TLShapeId): (FontState | null)[] | undefined\n}\n\nconst EMPTY_SHAPE_FONT_FACES_CACHE: ShapeFontFacesCache = { get: () => undefined }\nconst EMPTY_SHAPE_FONT_LOAD_STATE_CACHE: ShapeFontLoadStateCache = { get: () => undefined }\n\n/** @public */\nexport class FontManager {\n\tconstructor(\n\t\tprivate readonly editor: Editor,\n\t\tprivate readonly assetUrls?: { [key: string]: string | undefined }\n\t) {\n\t\tthis.shapeFontFacesCache = editor.store.createComputedCache(\n\t\t\t'shape font faces',\n\t\t\t(shape: TLShape) => {\n\t\t\t\tconst shapeUtil = this.editor.getShapeUtil(shape)\n\t\t\t\treturn shapeUtil.getFontFaces(shape)\n\t\t\t},\n\t\t\t{\n\t\t\t\tareResultsEqual: areArraysShallowEqual,\n\t\t\t\tareRecordsEqual:
|
|
5
|
-
"mappings": "AAAA,SAAS,UAAU,aAAa,gBAAgB;AAChD,SAAS,eAAe;AAExB;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;
|
|
4
|
+
"sourcesContent": ["import { computed, EMPTY_ARRAY, transact } from '@tldraw/state'\nimport { AtomMap } from '@tldraw/store'\nimport { TLFontFace, TLShape, TLShapeId } from '@tldraw/tlschema'\nimport {\n\tareArraysShallowEqual,\n\tcompact,\n\tFileHelpers,\n\tmapObjectMapValues,\n\tobjectMapEntries,\n} from '@tldraw/utils'\nimport { areShapesContentEqual } from '../../../utils/areShapesContentEqual'\nimport type { Editor } from '../../Editor'\n\ninterface FontState {\n\treadonly state: 'loading' | 'ready' | 'error'\n\treadonly instance: FontFace\n\treadonly loadingPromise: Promise<void>\n}\n\ninterface ShapeFontFacesCache {\n\tget(id: TLShapeId): TLFontFace[] | undefined\n}\n\ninterface ShapeFontLoadStateCache {\n\tget(id: TLShapeId): (FontState | null)[] | undefined\n}\n\nconst EMPTY_SHAPE_FONT_FACES_CACHE: ShapeFontFacesCache = { get: () => undefined }\nconst EMPTY_SHAPE_FONT_LOAD_STATE_CACHE: ShapeFontLoadStateCache = { get: () => undefined }\n\n/** @public */\nexport class FontManager {\n\tconstructor(\n\t\tprivate readonly editor: Editor,\n\t\tprivate readonly assetUrls?: { [key: string]: string | undefined }\n\t) {\n\t\tthis.shapeFontFacesCache = editor.store.createComputedCache(\n\t\t\t'shape font faces',\n\t\t\t(shape: TLShape) => {\n\t\t\t\tconst shapeUtil = this.editor.getShapeUtil(shape)\n\t\t\t\treturn shapeUtil.getFontFaces(shape)\n\t\t\t},\n\t\t\t{\n\t\t\t\tareResultsEqual: areArraysShallowEqual,\n\t\t\t\tareRecordsEqual: areShapesContentEqual,\n\t\t\t}\n\t\t)\n\n\t\tthis.shapeFontLoadStateCache = editor.store.createCache<(FontState | null)[], TLShape>(\n\t\t\t(id: TLShapeId) => {\n\t\t\t\tconst fontFacesComputed = computed('font faces', () => this.getShapeFontFaces(id))\n\t\t\t\treturn computed(\n\t\t\t\t\t'font load state',\n\t\t\t\t\t() => {\n\t\t\t\t\t\tconst states = fontFacesComputed.get().map((face) => this.getFontState(face))\n\t\t\t\t\t\treturn states\n\t\t\t\t\t},\n\t\t\t\t\t{ isEqual: areArraysShallowEqual }\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\t}\n\n\tdispose() {\n\t\tthis.fontStates.clear()\n\t\tthis.fontsToLoad.clear()\n\t\tthis.shapeFontFacesCache = EMPTY_SHAPE_FONT_FACES_CACHE\n\t\tthis.shapeFontLoadStateCache = EMPTY_SHAPE_FONT_LOAD_STATE_CACHE\n\t}\n\n\tprivate shapeFontFacesCache: ShapeFontFacesCache\n\tprivate shapeFontLoadStateCache: ShapeFontLoadStateCache\n\n\tgetShapeFontFaces(shape: TLShape | TLShapeId): TLFontFace[] {\n\t\tconst shapeId = typeof shape === 'string' ? shape : shape.id\n\t\treturn this.shapeFontFacesCache.get(shapeId) ?? EMPTY_ARRAY\n\t}\n\n\ttrackFontsForShape(shape: TLShape | TLShapeId) {\n\t\tconst shapeId = typeof shape === 'string' ? shape : shape.id\n\t\tthis.shapeFontLoadStateCache.get(shapeId)\n\t}\n\n\tasync loadRequiredFontsForCurrentPage(limit = Infinity) {\n\t\tconst neededFonts = new Set<TLFontFace>()\n\t\tfor (const shapeId of this.editor.getCurrentPageShapeIds()) {\n\t\t\tfor (const font of this.getShapeFontFaces(this.editor.getShape(shapeId)!)) {\n\t\t\t\tneededFonts.add(font)\n\t\t\t}\n\t\t}\n\n\t\tif (neededFonts.size > limit) {\n\t\t\treturn\n\t\t}\n\n\t\tconst promises = Array.from(neededFonts, (font) => this.ensureFontIsLoaded(font))\n\t\tawait Promise.all(promises)\n\t}\n\n\tprivate readonly fontStates = new AtomMap<TLFontFace, FontState>('font states')\n\tprivate getFontState(font: TLFontFace): FontState | null {\n\t\treturn this.fontStates.get(font) ?? null\n\t}\n\n\tensureFontIsLoaded(font: TLFontFace): Promise<void> {\n\t\tconst existingState = this.getFontState(font)\n\t\tif (existingState) return existingState.loadingPromise\n\n\t\tconst instance = this.findOrCreateFontFace(font)\n\t\t// dispose() clears fontStates while loads may still be in flight, so a late\n\t\t// callback must only touch the exact entry it was created for - not a fresh\n\t\t// entry from a later request. Check identity, not just presence.\n\t\tconst isStale = () => this.fontStates.__unsafe__getWithoutCapture(font) !== state\n\t\tconst state: FontState = {\n\t\t\tstate: 'loading',\n\t\t\tinstance,\n\t\t\tloadingPromise: instance\n\t\t\t\t.load()\n\t\t\t\t.then(() => {\n\t\t\t\t\tif (isStale()) {\n\t\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\t\tconsole.debug(`Font \"${font.family}\" load interrupted by editor dispose`)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.editor.getContainerDocument().fonts.add(instance)\n\t\t\t\t\tthis.fontStates.update(font, (s) => ({ ...s, state: 'ready' }))\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tif (isStale()) {\n\t\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\t\tconsole.debug(`Font \"${font.family}\" load interrupted by editor dispose`, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconsole.error(err)\n\t\t\t\t\tthis.fontStates.update(font, (s) => ({ ...s, state: 'error' }))\n\t\t\t\t}),\n\t\t}\n\n\t\tthis.fontStates.set(font, state)\n\t\treturn state.loadingPromise\n\t}\n\n\tprivate fontsToLoad = new Set<TLFontFace>()\n\trequestFonts(fonts: TLFontFace[]) {\n\t\tif (!this.fontsToLoad.size) {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (this.editor.isDisposed) return\n\t\t\t\tconst toLoad = this.fontsToLoad\n\t\t\t\tthis.fontsToLoad = new Set()\n\t\t\t\ttransact(() => {\n\t\t\t\t\tfor (const font of toLoad) {\n\t\t\t\t\t\tthis.ensureFontIsLoaded(font)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t\tfor (const font of fonts) {\n\t\t\tthis.fontsToLoad.add(font)\n\t\t}\n\t}\n\n\tprivate findOrCreateFontFace(font: TLFontFace) {\n\t\tconst containerDocument = this.editor.getContainerDocument()\n\n\t\t// `findOrCreateFontFace` runs for every font on every editor mount, and a fresh\n\t\t// editor (e.g. switching documents) gets a fresh FontManager with no memory of the\n\t\t// previous one. The dedup below is an O(n) scan of the document's whole FontFaceSet,\n\t\t// so without a cache that scan re-ran on every mount (measurably expensive on mobile\n\t\t// Safari). Cache the resolved FontFace per document so repeated lookups - and\n\t\t// remounts - are O(1). Keyed per document for cross-window embedding.\n\t\tlet cache = fontFaceCacheByDocument.get(containerDocument)\n\t\tif (!cache) {\n\t\t\tcache = new Map()\n\t\t\tfontFaceCacheByDocument.set(containerDocument, cache)\n\t\t}\n\t\tconst key = getFontFaceCacheKey(font)\n\t\tconst cached = cache.get(key)\n\t\tif (cached) return cached\n\n\t\tconst fonts = containerDocument.fonts\n\t\t// On a cache miss we still scan once, so font faces added outside this manager\n\t\t// (e.g. preloaded fonts) are reused rather than duplicated.\n\t\tfor (const existing of fonts) {\n\t\t\tif (\n\t\t\t\texisting.family === font.family &&\n\t\t\t\tobjectMapEntries(defaultFontFaceDescriptors).every(\n\t\t\t\t\t([key, defaultValue]) => existing[key] === (font[key] ?? defaultValue)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcache.set(key, existing)\n\t\t\t\treturn existing\n\t\t\t}\n\t\t}\n\n\t\tconst url = this.assetUrls?.[font.src.url] ?? font.src.url\n\t\tconst instance = new FontFace(font.family, `url(${JSON.stringify(url)})`, {\n\t\t\t...mapObjectMapValues(defaultFontFaceDescriptors, (key) => font[key]),\n\t\t\tdisplay: 'swap',\n\t\t})\n\n\t\tfonts.add(instance)\n\t\tcache.set(key, instance)\n\n\t\treturn instance\n\t}\n\n\tasync toEmbeddedCssDeclaration(font: TLFontFace) {\n\t\tconst url = this.assetUrls?.[font.src.url] ?? font.src.url\n\t\tconst dataUrl = await FileHelpers.urlToDataUrl(url)\n\n\t\tconst src = compact([\n\t\t\t`url(\"${dataUrl}\")`,\n\t\t\tfont.src.format ? `format(${font.src.format})` : null,\n\t\t\tfont.src.tech ? `tech(${font.src.tech})` : null,\n\t\t]).join(' ')\n\t\treturn compact([\n\t\t\t`@font-face {`,\n\t\t\t` font-family: \"${font.family}\";`,\n\t\t\tfont.ascentOverride ? ` ascent-override: ${font.ascentOverride};` : null,\n\t\t\tfont.descentOverride ? ` descent-override: ${font.descentOverride};` : null,\n\t\t\tfont.stretch ? ` font-stretch: ${font.stretch};` : null,\n\t\t\tfont.style ? ` font-style: ${font.style};` : null,\n\t\t\tfont.weight ? ` font-weight: ${font.weight};` : null,\n\t\t\tfont.featureSettings ? ` font-feature-settings: ${font.featureSettings};` : null,\n\t\t\tfont.lineGapOverride ? ` line-gap-override: ${font.lineGapOverride};` : null,\n\t\t\tfont.unicodeRange ? ` unicode-range: ${font.unicodeRange};` : null,\n\t\t\t` src: ${src};`,\n\t\t\t`}`,\n\t\t]).join('\\n')\n\t}\n}\n\n// From https://drafts.csswg.org/css-font-loading/#fontface-interface\nconst defaultFontFaceDescriptors = {\n\tstyle: 'normal',\n\tweight: 'normal',\n\tstretch: 'normal',\n\tunicodeRange: 'U+0-10FFFF',\n\tfeatureSettings: 'normal',\n\tascentOverride: 'normal',\n\tdescentOverride: 'normal',\n\tlineGapOverride: 'normal',\n}\n\n// A FontFace is fully determined by its family and descriptors, so resolved faces can be\n// cached per document and reused across FontManager instances (e.g. editor remounts),\n// turning the per-lookup FontFaceSet scan into an O(1) map lookup. Faces are never removed\n// from `document.fonts` (FontManager.dispose leaves them), so the cache stays valid for the\n// document's lifetime. Keyed per document for cross-window embedding.\nlet fontFaceCacheByDocument = new WeakMap<Document, Map<string, FontFace>>()\n\nfunction getFontFaceCacheKey(font: TLFontFace): string {\n\treturn JSON.stringify([\n\t\tfont.family,\n\t\t...objectMapEntries(defaultFontFaceDescriptors).map(\n\t\t\t([key, defaultValue]) => font[key] ?? defaultValue\n\t\t),\n\t])\n}\n\n/**\n * Resets the per-document font-face cache. Only intended for tests.\n * @internal\n */\nexport function clearFontFaceCacheForTests() {\n\tfontFaceCacheByDocument = new WeakMap()\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["key"]
|
|
7
7
|
}
|
|
@@ -8,20 +8,67 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
8
8
|
if (kind && result) __defProp(target, key, result);
|
|
9
9
|
return result;
|
|
10
10
|
};
|
|
11
|
-
import { atom, computed, unsafe__withoutCapture } from "@tldraw/state";
|
|
11
|
+
import { atom, computed, react, 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";
|
|
15
|
-
import { INTERNAL_POINTER_IDS } from "../../../constants.mjs";
|
|
14
|
+
import { bind, throttle } from "@tldraw/utils";
|
|
16
15
|
import { Vec } from "../../../primitives/Vec.mjs";
|
|
17
16
|
import { isAccelKey } from "../../../utils/keyboard.mjs";
|
|
18
17
|
import { EditorManager } from "../EditorManager.mjs";
|
|
19
18
|
const POINTER_VELOCITY_REFERENCE_INTERVAL_MS = 16;
|
|
20
19
|
const POINTER_VELOCITY_REFERENCE_SMOOTHING = 0.5;
|
|
20
|
+
const ACTIVITY_TIMESTAMP_THROTTLE_MS = 1e3;
|
|
21
|
+
const ACTIVITY_TIMESTAMP_THROTTLE_FLOOR_MS = 100;
|
|
22
|
+
const ACTIVITY_EVENTS = [
|
|
23
|
+
"pointerdown",
|
|
24
|
+
"pointermove",
|
|
25
|
+
"pointerup",
|
|
26
|
+
"keydown",
|
|
27
|
+
"beforeinput",
|
|
28
|
+
"wheel",
|
|
29
|
+
"gesturestart",
|
|
30
|
+
"gesturechange"
|
|
31
|
+
];
|
|
21
32
|
class InputsManager extends EditorManager {
|
|
22
33
|
constructor(editor) {
|
|
23
34
|
super(editor);
|
|
24
35
|
this.addEditorEvent("frame", this._onFrame);
|
|
36
|
+
this.register(
|
|
37
|
+
react("presence activity listeners", () => {
|
|
38
|
+
if (this.editor.getIsMounted() && this._getHasCollaborators()) {
|
|
39
|
+
this._attachActivityListeners();
|
|
40
|
+
} else {
|
|
41
|
+
this._detachActivityListeners();
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
this.register(() => {
|
|
46
|
+
this._detachActivityListeners();
|
|
47
|
+
this._throttledActivityStamp.cancel();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
_detachActivityListenersFn = null;
|
|
51
|
+
/**
|
|
52
|
+
* User input anywhere in the editor's tab counts as activity for collaborator presence —
|
|
53
|
+
* presence means "this person is here", whether or not the input lands on the editor itself
|
|
54
|
+
* (e.g. typing in a sidebar next to the canvas). Listen in the capture phase so input that
|
|
55
|
+
* other handlers swallow still counts.
|
|
56
|
+
*/
|
|
57
|
+
_attachActivityListeners() {
|
|
58
|
+
if (this._detachActivityListenersFn) return;
|
|
59
|
+
const win = this.editor.getContainerWindow();
|
|
60
|
+
for (const name of ACTIVITY_EVENTS) {
|
|
61
|
+
win.addEventListener(name, this.markActivity, { capture: true });
|
|
62
|
+
}
|
|
63
|
+
this._detachActivityListenersFn = () => {
|
|
64
|
+
for (const name of ACTIVITY_EVENTS) {
|
|
65
|
+
win.removeEventListener(name, this.markActivity, { capture: true });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
_detachActivityListeners() {
|
|
70
|
+
this._detachActivityListenersFn?.();
|
|
71
|
+
this._detachActivityListenersFn = null;
|
|
25
72
|
}
|
|
26
73
|
_onFrame(elapsed) {
|
|
27
74
|
this.updatePointerVelocity(elapsed);
|
|
@@ -439,6 +486,29 @@ class InputsManager extends EditorManager {
|
|
|
439
486
|
_getHasCollaborators() {
|
|
440
487
|
return this.editor.getCollaborators().length > 0;
|
|
441
488
|
}
|
|
489
|
+
_throttledActivityStamp = throttle(
|
|
490
|
+
() => {
|
|
491
|
+
const pointer = this.editor.store.unsafeGetWithoutCapture(TLPOINTER_ID);
|
|
492
|
+
if (!pointer) return;
|
|
493
|
+
this.editor.run(
|
|
494
|
+
() => {
|
|
495
|
+
this.editor.store.put([{ ...pointer, lastActivityTimestamp: Date.now() }]);
|
|
496
|
+
},
|
|
497
|
+
{ history: "ignore" }
|
|
498
|
+
);
|
|
499
|
+
},
|
|
500
|
+
// Stay comfortably below the idle timeout so a continuously-active peer
|
|
501
|
+
// never flickers to idle between throttled stamps.
|
|
502
|
+
Math.max(
|
|
503
|
+
ACTIVITY_TIMESTAMP_THROTTLE_FLOOR_MS,
|
|
504
|
+
Math.min(ACTIVITY_TIMESTAMP_THROTTLE_MS, this.editor.options.collaboratorIdleTimeoutMs / 3)
|
|
505
|
+
),
|
|
506
|
+
{ trailing: false }
|
|
507
|
+
);
|
|
508
|
+
markActivity() {
|
|
509
|
+
if (!this._getHasCollaborators()) return;
|
|
510
|
+
this._throttledActivityStamp();
|
|
511
|
+
}
|
|
442
512
|
/**
|
|
443
513
|
* The previous point used for velocity calculation (updated each tick, not each pointer event).
|
|
444
514
|
* @internal
|
|
@@ -507,11 +577,11 @@ class InputsManager extends EditorManager {
|
|
|
507
577
|
typeName: "pointer",
|
|
508
578
|
x: pagePoint.x,
|
|
509
579
|
y: pagePoint.y,
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
),
|
|
580
|
+
// The activity timestamp is stamped by the container's input
|
|
581
|
+
// listeners (see `markActivity`), not by pointer position updates:
|
|
582
|
+
// synthetic moves, like the camera following another user, don't
|
|
583
|
+
// count as activity.
|
|
584
|
+
lastActivityTimestamp: this.editor.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ?? 0,
|
|
515
585
|
meta: {}
|
|
516
586
|
}
|
|
517
587
|
]);
|
|
@@ -551,6 +621,9 @@ __decorateClass([
|
|
|
551
621
|
__decorateClass([
|
|
552
622
|
computed
|
|
553
623
|
], InputsManager.prototype, "_getHasCollaborators", 1);
|
|
624
|
+
__decorateClass([
|
|
625
|
+
bind
|
|
626
|
+
], InputsManager.prototype, "markActivity", 1);
|
|
554
627
|
export {
|
|
555
628
|
InputsManager
|
|
556
629
|
};
|