@tldraw/editor 4.3.0-canary.c7096a59bf3b → 4.3.0-canary.cb6779b4f066

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +1 -1
  2. package/dist-cjs/index.d.ts +446 -120
  3. package/dist-cjs/index.js +8 -1
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js +1 -17
  7. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -5
  9. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  10. package/dist-cjs/lib/constants.js +1 -3
  11. package/dist-cjs/lib/constants.js.map +2 -2
  12. package/dist-cjs/lib/editor/Editor.js +342 -280
  13. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  14. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +16 -23
  15. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  16. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +12 -3
  17. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  18. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -1
  19. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  20. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +5 -6
  21. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +591 -0
  23. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +7 -0
  24. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
  25. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  26. package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js +144 -0
  27. package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js.map +7 -0
  28. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +181 -0
  29. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +7 -0
  30. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -22
  31. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  32. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +31 -23
  33. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  34. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
  35. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js.map +2 -2
  36. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -3
  37. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  38. package/dist-cjs/lib/exports/parseCss.js +1 -1
  39. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  40. package/dist-cjs/lib/globals/environment.js +45 -9
  41. package/dist-cjs/lib/globals/environment.js.map +2 -2
  42. package/dist-cjs/lib/hooks/useCoarsePointer.js +14 -29
  43. package/dist-cjs/lib/hooks/useCoarsePointer.js.map +2 -2
  44. package/dist-cjs/lib/hooks/useEvent.js +1 -1
  45. package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
  46. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  47. package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
  48. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  49. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  50. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  51. package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
  52. package/dist-cjs/lib/hooks/useStateAttribute.js +4 -1
  53. package/dist-cjs/lib/hooks/useStateAttribute.js.map +2 -2
  54. package/dist-cjs/lib/hooks/useTransform.js.map +1 -1
  55. package/dist-cjs/lib/hooks/useZoomCss.js +4 -8
  56. package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
  57. package/dist-cjs/lib/options.js +6 -1
  58. package/dist-cjs/lib/options.js.map +2 -2
  59. package/dist-cjs/lib/primitives/Box.js +3 -0
  60. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  61. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +1 -0
  62. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  63. package/dist-cjs/lib/utils/rotation.js +1 -1
  64. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  65. package/dist-cjs/version.js +3 -3
  66. package/dist-cjs/version.js.map +1 -1
  67. package/dist-esm/index.d.mts +446 -120
  68. package/dist-esm/index.mjs +9 -2
  69. package/dist-esm/index.mjs.map +2 -2
  70. package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
  71. package/dist-esm/lib/components/GeometryDebuggingView.mjs +1 -17
  72. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  73. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -5
  74. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  75. package/dist-esm/lib/constants.mjs +1 -3
  76. package/dist-esm/lib/constants.mjs.map +2 -2
  77. package/dist-esm/lib/editor/Editor.mjs +343 -283
  78. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  79. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +16 -23
  80. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  81. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +13 -4
  82. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  83. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -1
  84. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  85. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +5 -6
  86. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  87. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +573 -0
  88. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +7 -0
  89. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
  90. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  91. package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs +114 -0
  92. package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs.map +7 -0
  93. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +161 -0
  94. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +7 -0
  95. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -22
  96. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  97. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +31 -23
  98. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  99. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
  100. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs.map +2 -2
  101. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -3
  102. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  103. package/dist-esm/lib/exports/parseCss.mjs +1 -1
  104. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  105. package/dist-esm/lib/globals/environment.mjs +45 -9
  106. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  107. package/dist-esm/lib/hooks/useCoarsePointer.mjs +15 -30
  108. package/dist-esm/lib/hooks/useCoarsePointer.mjs.map +2 -2
  109. package/dist-esm/lib/hooks/useEvent.mjs +1 -1
  110. package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
  111. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  112. package/dist-esm/lib/hooks/useGestureEvents.mjs +1 -1
  113. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  114. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  115. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  116. package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
  117. package/dist-esm/lib/hooks/useStateAttribute.mjs +4 -1
  118. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +2 -2
  119. package/dist-esm/lib/hooks/useTransform.mjs.map +1 -1
  120. package/dist-esm/lib/hooks/useZoomCss.mjs +4 -8
  121. package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
  122. package/dist-esm/lib/options.mjs +6 -1
  123. package/dist-esm/lib/options.mjs.map +2 -2
  124. package/dist-esm/lib/primitives/Box.mjs +3 -0
  125. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  126. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +1 -0
  127. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  128. package/dist-esm/lib/utils/rotation.mjs +1 -1
  129. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  130. package/dist-esm/version.mjs +3 -3
  131. package/dist-esm/version.mjs.map +1 -1
  132. package/editor.css +14 -12
  133. package/package.json +21 -17
  134. package/src/index.ts +5 -1
  135. package/src/lib/components/ErrorBoundary.tsx +1 -1
  136. package/src/lib/components/GeometryDebuggingView.tsx +1 -19
  137. package/src/lib/components/default-components/DefaultCanvas.tsx +4 -8
  138. package/src/lib/config/TLUserPreferences.test.ts +40 -0
  139. package/src/lib/constants.ts +0 -2
  140. package/src/lib/editor/Editor.test.ts +140 -0
  141. package/src/lib/editor/Editor.ts +448 -326
  142. package/src/lib/editor/derivations/notVisibleShapes.ts +21 -33
  143. package/src/lib/editor/derivations/parentsToChildren.ts +18 -7
  144. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +17 -31
  145. package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
  146. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +129 -79
  147. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +10 -6
  148. package/src/lib/editor/managers/InputsManager/InputsManager.ts +566 -0
  149. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -4
  150. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +12 -0
  151. package/src/lib/editor/managers/SnapManager/SnapManager.ts +1 -1
  152. package/src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts +144 -0
  153. package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +215 -0
  154. package/src/lib/editor/managers/TickManager/TickManager.test.ts +40 -107
  155. package/src/lib/editor/managers/TickManager/TickManager.ts +2 -32
  156. package/src/lib/editor/shapes/ShapeUtil.ts +67 -24
  157. package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
  158. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +3 -3
  159. package/src/lib/exports/parseCss.test.ts +1 -0
  160. package/src/lib/exports/parseCss.ts +1 -1
  161. package/src/lib/globals/environment.ts +65 -10
  162. package/src/lib/hooks/useCoarsePointer.ts +16 -59
  163. package/src/lib/hooks/useEvent.tsx +1 -1
  164. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
  165. package/src/lib/hooks/useGestureEvents.ts +2 -2
  166. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +1 -1
  167. package/src/lib/hooks/usePassThroughWheelEvents.ts +1 -1
  168. package/src/lib/hooks/useScreenBounds.ts +1 -1
  169. package/src/lib/hooks/useStateAttribute.ts +4 -1
  170. package/src/lib/hooks/useTransform.ts +1 -1
  171. package/src/lib/hooks/useZoomCss.ts +3 -8
  172. package/src/lib/options.ts +32 -0
  173. package/src/lib/primitives/Box.ts +9 -0
  174. package/src/lib/primitives/geometry/Geometry2d.ts +1 -0
  175. package/src/lib/utils/rotation.ts +1 -1
  176. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/SnapManager/SnapManager.ts"],
4
- "sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport { TLParentId, TLShapeId, isShapeId } from '@tldraw/tlschema'\nimport { Vec, VecLike } from '../../../primitives/Vec'\nimport type { Editor } from '../../Editor'\nimport { BoundsSnaps } from './BoundsSnaps'\nimport { HandleSnaps } from './HandleSnaps'\n\n/** @public */\nexport interface PointsSnapIndicator {\n\tid: string\n\ttype: 'points'\n\tpoints: VecLike[]\n}\n\n/** @public */\nexport interface GapsSnapIndicator {\n\tid: string\n\ttype: 'gaps'\n\tdirection: 'horizontal' | 'vertical'\n\tgaps: Array<{\n\t\tstartEdge: [VecLike, VecLike]\n\t\tendEdge: [VecLike, VecLike]\n\t}>\n}\n\n/** @public */\nexport type SnapIndicator = PointsSnapIndicator | GapsSnapIndicator\n\n/** @public */\nexport interface SnapData {\n\tnudge: Vec\n}\n\n/** @public */\nexport class SnapManager {\n\treadonly shapeBounds: BoundsSnaps\n\treadonly handles: HandleSnaps\n\n\tprivate _snapIndicators = atom<SnapIndicator[] | undefined>('snapLines', undefined)\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.shapeBounds = new BoundsSnaps(this)\n\t\tthis.handles = new HandleSnaps(this)\n\t}\n\n\tgetIndicators() {\n\t\treturn this._snapIndicators.get() ?? (EMPTY_ARRAY as SnapIndicator[])\n\t}\n\n\tclearIndicators() {\n\t\tif (this.getIndicators().length) {\n\t\t\tthis._snapIndicators.set(undefined)\n\t\t}\n\t}\n\n\tsetIndicators(indicators: SnapIndicator[]) {\n\t\tthis._snapIndicators.set(indicators)\n\t}\n\n\t@computed getSnapThreshold() {\n\t\treturn 8 / this.editor.getZoomLevel()\n\t}\n\n\t// TODO: make this an incremental derivation\n\t@computed getSnappableShapes(): Set<TLShapeId> {\n\t\tconst { editor } = this\n\t\tconst renderingBounds = editor.getViewportPageBounds()\n\t\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\n\t\tconst snappableShapes: Set<TLShapeId> = new Set()\n\n\t\tconst collectSnappableShapesFromParent = (parentId: TLParentId) => {\n\t\t\tif (isShapeId(parentId)) {\n\t\t\t\tconst parent = editor.getShape(parentId)\n\t\t\t\tif (parent && editor.isShapeOfType(parent, 'frame')) {\n\t\t\t\t\tsnappableShapes.add(parentId)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst sortedChildIds = editor.getSortedChildIdsForParent(parentId)\n\t\t\tfor (const childId of sortedChildIds) {\n\t\t\t\t// Skip any selected ids\n\t\t\t\tif (selectedShapeIds.includes(childId)) continue\n\t\t\t\tconst childShape = editor.getShape(childId)\n\t\t\t\tif (!childShape) continue\n\t\t\t\tconst util = editor.getShapeUtil(childShape)\n\t\t\t\t// Skip any shapes that don't allow snapping\n\t\t\t\tif (!util.canSnap(childShape)) continue\n\t\t\t\t// Only consider shapes if they're inside of the viewport page bounds\n\t\t\t\tconst pageBounds = editor.getShapePageBounds(childId)\n\t\t\t\tif (!(pageBounds && renderingBounds.includes(pageBounds))) continue\n\t\t\t\t// Snap to children of groups but not group itself\n\t\t\t\tif (editor.isShapeOfType(childShape, 'group')) {\n\t\t\t\t\tcollectSnappableShapesFromParent(childId)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsnappableShapes.add(childId)\n\t\t\t}\n\t\t}\n\n\t\tcollectSnappableShapesFromParent(this.getCurrentCommonAncestor() ?? editor.getCurrentPageId())\n\n\t\treturn snappableShapes\n\t}\n\n\t// This needs to be external from any expensive work\n\t@computed getCurrentCommonAncestor() {\n\t\treturn this.editor.findCommonAncestor(this.editor.getSelectedShapes())\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4C;AAC5C,sBAAiD;AAGjD,yBAA4B;AAC5B,yBAA4B;AAL5B;AA2DC,yBAAC,wBAKD,2BAAC,wBAyCD,iCAAC;AAvEK,MAAM,YAAY;AAAA,EAMxB,YAA4B,QAAgB;AAAhB;AANtB;AACN,wBAAS;AACT,wBAAS;AAET,wBAAQ,uBAAkB,mBAAkC,aAAa,MAAS;AAGjF,SAAK,cAAc,IAAI,+BAAY,IAAI;AACvC,SAAK,UAAU,IAAI,+BAAY,IAAI;AAAA,EACpC;AAAA,EAEA,gBAAgB;AACf,WAAO,KAAK,gBAAgB,IAAI,KAAM;AAAA,EACvC;AAAA,EAEA,kBAAkB;AACjB,QAAI,KAAK,cAAc,EAAE,QAAQ;AAChC,WAAK,gBAAgB,IAAI,MAAS;AAAA,IACnC;AAAA,EACD;AAAA,EAEA,cAAc,YAA6B;AAC1C,SAAK,gBAAgB,IAAI,UAAU;AAAA,EACpC;AAAA,EAEU,mBAAmB;AAC5B,WAAO,IAAI,KAAK,OAAO,aAAa;AAAA,EACrC;AAAA,EAGU,qBAAqC;AAC9C,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,kBAAkB,OAAO,sBAAsB;AACrD,UAAM,mBAAmB,OAAO,oBAAoB;AAEpD,UAAM,kBAAkC,oBAAI,IAAI;AAEhD,UAAM,mCAAmC,CAAC,aAAyB;AAClE,cAAI,2BAAU,QAAQ,GAAG;AACxB,cAAM,SAAS,OAAO,SAAS,QAAQ;AACvC,YAAI,UAAU,OAAO,cAAc,QAAQ,OAAO,GAAG;AACpD,0BAAgB,IAAI,QAAQ;AAAA,QAC7B;AAAA,MACD;AACA,YAAM,iBAAiB,OAAO,2BAA2B,QAAQ;AACjE,iBAAW,WAAW,gBAAgB;AAErC,YAAI,iBAAiB,SAAS,OAAO,EAAG;AACxC,cAAM,aAAa,OAAO,SAAS,OAAO;AAC1C,YAAI,CAAC,WAAY;AACjB,cAAM,OAAO,OAAO,aAAa,UAAU;AAE3C,YAAI,CAAC,KAAK,QAAQ,UAAU,EAAG;AAE/B,cAAM,aAAa,OAAO,mBAAmB,OAAO;AACpD,YAAI,EAAE,cAAc,gBAAgB,SAAS,UAAU,GAAI;AAE3D,YAAI,OAAO,cAAc,YAAY,OAAO,GAAG;AAC9C,2CAAiC,OAAO;AACxC;AAAA,QACD;AACA,wBAAgB,IAAI,OAAO;AAAA,MAC5B;AAAA,IACD;AAEA,qCAAiC,KAAK,yBAAyB,KAAK,OAAO,iBAAiB,CAAC;AAE7F,WAAO;AAAA,EACR;AAAA,EAGU,2BAA2B;AACpC,WAAO,KAAK,OAAO,mBAAmB,KAAK,OAAO,kBAAkB,CAAC;AAAA,EACtE;AACD;AA1EO;AAyBI,gDAAV,uBAzBY;AA8BF,kDAAV,yBA9BY;AAuEF,wDAAV,+BAvEY;AAAN,2BAAM;",
4
+ "sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport { TLParentId, TLShapeId, isShapeId } from '@tldraw/tlschema'\nimport { Vec, VecLike } from '../../../primitives/Vec'\nimport type { Editor } from '../../Editor'\nimport { BoundsSnaps } from './BoundsSnaps'\nimport { HandleSnaps } from './HandleSnaps'\n\n/** @public */\nexport interface PointsSnapIndicator {\n\tid: string\n\ttype: 'points'\n\tpoints: VecLike[]\n}\n\n/** @public */\nexport interface GapsSnapIndicator {\n\tid: string\n\ttype: 'gaps'\n\tdirection: 'horizontal' | 'vertical'\n\tgaps: Array<{\n\t\tstartEdge: [VecLike, VecLike]\n\t\tendEdge: [VecLike, VecLike]\n\t}>\n}\n\n/** @public */\nexport type SnapIndicator = PointsSnapIndicator | GapsSnapIndicator\n\n/** @public */\nexport interface SnapData {\n\tnudge: Vec\n}\n\n/** @public */\nexport class SnapManager {\n\treadonly shapeBounds: BoundsSnaps\n\treadonly handles: HandleSnaps\n\n\tprivate _snapIndicators = atom<SnapIndicator[] | undefined>('snapLines', undefined)\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.shapeBounds = new BoundsSnaps(this)\n\t\tthis.handles = new HandleSnaps(this)\n\t}\n\n\tgetIndicators() {\n\t\treturn this._snapIndicators.get() ?? (EMPTY_ARRAY as SnapIndicator[])\n\t}\n\n\tclearIndicators() {\n\t\tif (this.getIndicators().length) {\n\t\t\tthis._snapIndicators.set(undefined)\n\t\t}\n\t}\n\n\tsetIndicators(indicators: SnapIndicator[]) {\n\t\tthis._snapIndicators.set(indicators)\n\t}\n\n\t@computed getSnapThreshold() {\n\t\treturn this.editor.options.snapThreshold / this.editor.getZoomLevel()\n\t}\n\n\t// TODO: make this an incremental derivation\n\t@computed getSnappableShapes(): Set<TLShapeId> {\n\t\tconst { editor } = this\n\t\tconst renderingBounds = editor.getViewportPageBounds()\n\t\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\n\t\tconst snappableShapes: Set<TLShapeId> = new Set()\n\n\t\tconst collectSnappableShapesFromParent = (parentId: TLParentId) => {\n\t\t\tif (isShapeId(parentId)) {\n\t\t\t\tconst parent = editor.getShape(parentId)\n\t\t\t\tif (parent && editor.isShapeOfType(parent, 'frame')) {\n\t\t\t\t\tsnappableShapes.add(parentId)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst sortedChildIds = editor.getSortedChildIdsForParent(parentId)\n\t\t\tfor (const childId of sortedChildIds) {\n\t\t\t\t// Skip any selected ids\n\t\t\t\tif (selectedShapeIds.includes(childId)) continue\n\t\t\t\tconst childShape = editor.getShape(childId)\n\t\t\t\tif (!childShape) continue\n\t\t\t\tconst util = editor.getShapeUtil(childShape)\n\t\t\t\t// Skip any shapes that don't allow snapping\n\t\t\t\tif (!util.canSnap(childShape)) continue\n\t\t\t\t// Only consider shapes if they're inside of the viewport page bounds\n\t\t\t\tconst pageBounds = editor.getShapePageBounds(childId)\n\t\t\t\tif (!(pageBounds && renderingBounds.includes(pageBounds))) continue\n\t\t\t\t// Snap to children of groups but not group itself\n\t\t\t\tif (editor.isShapeOfType(childShape, 'group')) {\n\t\t\t\t\tcollectSnappableShapesFromParent(childId)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsnappableShapes.add(childId)\n\t\t\t}\n\t\t}\n\n\t\tcollectSnappableShapesFromParent(this.getCurrentCommonAncestor() ?? editor.getCurrentPageId())\n\n\t\treturn snappableShapes\n\t}\n\n\t// This needs to be external from any expensive work\n\t@computed getCurrentCommonAncestor() {\n\t\treturn this.editor.findCommonAncestor(this.editor.getSelectedShapes())\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4C;AAC5C,sBAAiD;AAGjD,yBAA4B;AAC5B,yBAA4B;AAL5B;AA2DC,yBAAC,wBAKD,2BAAC,wBAyCD,iCAAC;AAvEK,MAAM,YAAY;AAAA,EAMxB,YAA4B,QAAgB;AAAhB;AANtB;AACN,wBAAS;AACT,wBAAS;AAET,wBAAQ,uBAAkB,mBAAkC,aAAa,MAAS;AAGjF,SAAK,cAAc,IAAI,+BAAY,IAAI;AACvC,SAAK,UAAU,IAAI,+BAAY,IAAI;AAAA,EACpC;AAAA,EAEA,gBAAgB;AACf,WAAO,KAAK,gBAAgB,IAAI,KAAM;AAAA,EACvC;AAAA,EAEA,kBAAkB;AACjB,QAAI,KAAK,cAAc,EAAE,QAAQ;AAChC,WAAK,gBAAgB,IAAI,MAAS;AAAA,IACnC;AAAA,EACD;AAAA,EAEA,cAAc,YAA6B;AAC1C,SAAK,gBAAgB,IAAI,UAAU;AAAA,EACpC;AAAA,EAEU,mBAAmB;AAC5B,WAAO,KAAK,OAAO,QAAQ,gBAAgB,KAAK,OAAO,aAAa;AAAA,EACrE;AAAA,EAGU,qBAAqC;AAC9C,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,kBAAkB,OAAO,sBAAsB;AACrD,UAAM,mBAAmB,OAAO,oBAAoB;AAEpD,UAAM,kBAAkC,oBAAI,IAAI;AAEhD,UAAM,mCAAmC,CAAC,aAAyB;AAClE,cAAI,2BAAU,QAAQ,GAAG;AACxB,cAAM,SAAS,OAAO,SAAS,QAAQ;AACvC,YAAI,UAAU,OAAO,cAAc,QAAQ,OAAO,GAAG;AACpD,0BAAgB,IAAI,QAAQ;AAAA,QAC7B;AAAA,MACD;AACA,YAAM,iBAAiB,OAAO,2BAA2B,QAAQ;AACjE,iBAAW,WAAW,gBAAgB;AAErC,YAAI,iBAAiB,SAAS,OAAO,EAAG;AACxC,cAAM,aAAa,OAAO,SAAS,OAAO;AAC1C,YAAI,CAAC,WAAY;AACjB,cAAM,OAAO,OAAO,aAAa,UAAU;AAE3C,YAAI,CAAC,KAAK,QAAQ,UAAU,EAAG;AAE/B,cAAM,aAAa,OAAO,mBAAmB,OAAO;AACpD,YAAI,EAAE,cAAc,gBAAgB,SAAS,UAAU,GAAI;AAE3D,YAAI,OAAO,cAAc,YAAY,OAAO,GAAG;AAC9C,2CAAiC,OAAO;AACxC;AAAA,QACD;AACA,wBAAgB,IAAI,OAAO;AAAA,MAC5B;AAAA,IACD;AAEA,qCAAiC,KAAK,yBAAyB,KAAK,OAAO,iBAAiB,CAAC;AAE7F,WAAO;AAAA,EACR;AAAA,EAGU,2BAA2B;AACpC,WAAO,KAAK,OAAO,mBAAmB,KAAK,OAAO,kBAAkB,CAAC;AAAA,EACtE;AACD;AA1EO;AAyBI,gDAAV,uBAzBY;AA8BF,kDAAV,yBA9BY;AAuEF,wDAAV,+BAvEY;AAAN,2BAAM;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var RBushIndex_exports = {};
30
+ __export(RBushIndex_exports, {
31
+ RBushIndex: () => RBushIndex
32
+ });
33
+ module.exports = __toCommonJS(RBushIndex_exports);
34
+ var import_rbush = __toESM(require("rbush"), 1);
35
+ var import_Box = require("../../../primitives/Box");
36
+ class TldrawRBush extends import_rbush.default {
37
+ }
38
+ class RBushIndex {
39
+ rBush;
40
+ elementsInTree;
41
+ constructor() {
42
+ this.rBush = new TldrawRBush();
43
+ this.elementsInTree = /* @__PURE__ */ new Map();
44
+ }
45
+ /**
46
+ * Search for shapes within the given bounds.
47
+ * Returns set of shape IDs that intersect with the bounds.
48
+ */
49
+ search(bounds) {
50
+ const results = this.rBush.search({
51
+ minX: bounds.minX,
52
+ minY: bounds.minY,
53
+ maxX: bounds.maxX,
54
+ maxY: bounds.maxY
55
+ });
56
+ return new Set(results.map((e) => e.id));
57
+ }
58
+ /**
59
+ * Insert or update a shape in the spatial index.
60
+ * If the shape already exists, it will be removed first to prevent duplicates.
61
+ */
62
+ upsert(id, bounds) {
63
+ const existing = this.elementsInTree.get(id);
64
+ if (existing) {
65
+ this.rBush.remove(existing);
66
+ }
67
+ const element = {
68
+ minX: bounds.minX,
69
+ minY: bounds.minY,
70
+ maxX: bounds.maxX,
71
+ maxY: bounds.maxY,
72
+ id
73
+ };
74
+ this.rBush.insert(element);
75
+ this.elementsInTree.set(id, element);
76
+ }
77
+ /**
78
+ * Remove a shape from the spatial index.
79
+ */
80
+ remove(id) {
81
+ const element = this.elementsInTree.get(id);
82
+ if (element) {
83
+ this.rBush.remove(element);
84
+ this.elementsInTree.delete(id);
85
+ }
86
+ }
87
+ /**
88
+ * Bulk load elements into the spatial index.
89
+ * More efficient than individual inserts for initial loading.
90
+ */
91
+ bulkLoad(elements) {
92
+ this.rBush.load(elements);
93
+ for (const element of elements) {
94
+ this.elementsInTree.set(element.id, element);
95
+ }
96
+ }
97
+ /**
98
+ * Clear all elements from the spatial index.
99
+ */
100
+ clear() {
101
+ this.rBush.clear();
102
+ this.elementsInTree.clear();
103
+ }
104
+ /**
105
+ * Check if a shape is in the spatial index.
106
+ */
107
+ has(id) {
108
+ return this.elementsInTree.has(id);
109
+ }
110
+ /**
111
+ * Get the number of elements in the spatial index.
112
+ */
113
+ getSize() {
114
+ return this.elementsInTree.size;
115
+ }
116
+ /**
117
+ * Get all shape IDs currently in the spatial index.
118
+ */
119
+ getAllShapeIds() {
120
+ return Array.from(this.elementsInTree.keys());
121
+ }
122
+ /**
123
+ * Get the bounds currently stored in the spatial index for a shape.
124
+ * Returns undefined if the shape is not in the index.
125
+ */
126
+ getBounds(id) {
127
+ const element = this.elementsInTree.get(id);
128
+ if (!element) return void 0;
129
+ return new import_Box.Box(
130
+ element.minX,
131
+ element.minY,
132
+ element.maxX - element.minX,
133
+ element.maxY - element.minY
134
+ );
135
+ }
136
+ /**
137
+ * Dispose of the spatial index.
138
+ * Clears all data structures to prevent memory leaks.
139
+ */
140
+ dispose() {
141
+ this.clear();
142
+ }
143
+ }
144
+ //# sourceMappingURL=RBushIndex.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts"],
4
+ "sourcesContent": ["import type { TLShapeId } from '@tldraw/tlschema'\nimport RBush from 'rbush'\nimport { Box } from '../../../primitives/Box'\n\n/**\n * Element stored in the R-tree spatial index.\n * Contains bounds (minX, minY, maxX, maxY) and shape ID.\n */\nexport interface SpatialElement {\n\tminX: number\n\tminY: number\n\tmaxX: number\n\tmaxY: number\n\tid: TLShapeId\n}\n\n/**\n * Custom RBush class for tldraw shapes.\n */\nclass TldrawRBush extends RBush<SpatialElement> {}\n\n/**\n * Wrapper around RBush R-tree for efficient spatial queries.\n * Maintains a map of elements currently in the tree for efficient updates.\n */\nexport class RBushIndex {\n\tprivate rBush: TldrawRBush\n\tprivate elementsInTree: Map<TLShapeId, SpatialElement>\n\n\tconstructor() {\n\t\tthis.rBush = new TldrawRBush()\n\t\tthis.elementsInTree = new Map()\n\t}\n\n\t/**\n\t * Search for shapes within the given bounds.\n\t * Returns set of shape IDs that intersect with the bounds.\n\t */\n\tsearch(bounds: Box): Set<TLShapeId> {\n\t\tconst results = this.rBush.search({\n\t\t\tminX: bounds.minX,\n\t\t\tminY: bounds.minY,\n\t\t\tmaxX: bounds.maxX,\n\t\t\tmaxY: bounds.maxY,\n\t\t})\n\t\treturn new Set(results.map((e: SpatialElement) => e.id))\n\t}\n\n\t/**\n\t * Insert or update a shape in the spatial index.\n\t * If the shape already exists, it will be removed first to prevent duplicates.\n\t */\n\tupsert(id: TLShapeId, bounds: Box): void {\n\t\t// Remove existing entry to prevent map-tree desync\n\t\tconst existing = this.elementsInTree.get(id)\n\t\tif (existing) {\n\t\t\tthis.rBush.remove(existing)\n\t\t}\n\n\t\tconst element: SpatialElement = {\n\t\t\tminX: bounds.minX,\n\t\t\tminY: bounds.minY,\n\t\t\tmaxX: bounds.maxX,\n\t\t\tmaxY: bounds.maxY,\n\t\t\tid,\n\t\t}\n\t\tthis.rBush.insert(element)\n\t\tthis.elementsInTree.set(id, element)\n\t}\n\n\t/**\n\t * Remove a shape from the spatial index.\n\t */\n\tremove(id: TLShapeId): void {\n\t\tconst element = this.elementsInTree.get(id)\n\t\tif (element) {\n\t\t\tthis.rBush.remove(element)\n\t\t\tthis.elementsInTree.delete(id)\n\t\t}\n\t}\n\n\t/**\n\t * Bulk load elements into the spatial index.\n\t * More efficient than individual inserts for initial loading.\n\t */\n\tbulkLoad(elements: SpatialElement[]): void {\n\t\tthis.rBush.load(elements)\n\t\tfor (const element of elements) {\n\t\t\tthis.elementsInTree.set(element.id, element)\n\t\t}\n\t}\n\n\t/**\n\t * Clear all elements from the spatial index.\n\t */\n\tclear(): void {\n\t\tthis.rBush.clear()\n\t\tthis.elementsInTree.clear()\n\t}\n\n\t/**\n\t * Check if a shape is in the spatial index.\n\t */\n\thas(id: TLShapeId): boolean {\n\t\treturn this.elementsInTree.has(id)\n\t}\n\n\t/**\n\t * Get the number of elements in the spatial index.\n\t */\n\tgetSize(): number {\n\t\treturn this.elementsInTree.size\n\t}\n\n\t/**\n\t * Get all shape IDs currently in the spatial index.\n\t */\n\tgetAllShapeIds(): TLShapeId[] {\n\t\treturn Array.from(this.elementsInTree.keys())\n\t}\n\n\t/**\n\t * Get the bounds currently stored in the spatial index for a shape.\n\t * Returns undefined if the shape is not in the index.\n\t */\n\tgetBounds(id: TLShapeId): Box | undefined {\n\t\tconst element = this.elementsInTree.get(id)\n\t\tif (!element) return undefined\n\t\treturn new Box(\n\t\t\telement.minX,\n\t\t\telement.minY,\n\t\t\telement.maxX - element.minX,\n\t\t\telement.maxY - element.minY\n\t\t)\n\t}\n\n\t/**\n\t * Dispose of the spatial index.\n\t * Clears all data structures to prevent memory leaks.\n\t */\n\tdispose(): void {\n\t\tthis.clear()\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAkB;AAClB,iBAAoB;AAiBpB,MAAM,oBAAoB,aAAAA,QAAsB;AAAC;AAM1C,MAAM,WAAW;AAAA,EACf;AAAA,EACA;AAAA,EAER,cAAc;AACb,SAAK,QAAQ,IAAI,YAAY;AAC7B,SAAK,iBAAiB,oBAAI,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,QAA6B;AACnC,UAAM,UAAU,KAAK,MAAM,OAAO;AAAA,MACjC,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,IACd,CAAC;AACD,WAAO,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAsB,EAAE,EAAE,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,IAAe,QAAmB;AAExC,UAAM,WAAW,KAAK,eAAe,IAAI,EAAE;AAC3C,QAAI,UAAU;AACb,WAAK,MAAM,OAAO,QAAQ;AAAA,IAC3B;AAEA,UAAM,UAA0B;AAAA,MAC/B,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb;AAAA,IACD;AACA,SAAK,MAAM,OAAO,OAAO;AACzB,SAAK,eAAe,IAAI,IAAI,OAAO;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,IAAqB;AAC3B,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE;AAC1C,QAAI,SAAS;AACZ,WAAK,MAAM,OAAO,OAAO;AACzB,WAAK,eAAe,OAAO,EAAE;AAAA,IAC9B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,UAAkC;AAC1C,SAAK,MAAM,KAAK,QAAQ;AACxB,eAAW,WAAW,UAAU;AAC/B,WAAK,eAAe,IAAI,QAAQ,IAAI,OAAO;AAAA,IAC5C;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACb,SAAK,MAAM,MAAM;AACjB,SAAK,eAAe,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,IAAwB;AAC3B,WAAO,KAAK,eAAe,IAAI,EAAE;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAkB;AACjB,WAAO,KAAK,eAAe;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA8B;AAC7B,WAAO,MAAM,KAAK,KAAK,eAAe,KAAK,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,IAAgC;AACzC,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE;AAC1C,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,IAAI;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ,OAAO,QAAQ;AAAA,MACvB,QAAQ,OAAO,QAAQ;AAAA,IACxB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAgB;AACf,SAAK,MAAM;AAAA,EACZ;AACD;",
6
+ "names": ["RBush"]
7
+ }
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var SpatialIndexManager_exports = {};
20
+ __export(SpatialIndexManager_exports, {
21
+ SpatialIndexManager: () => SpatialIndexManager
22
+ });
23
+ module.exports = __toCommonJS(SpatialIndexManager_exports);
24
+ var import_state = require("@tldraw/state");
25
+ var import_tlschema = require("@tldraw/tlschema");
26
+ var import_utils = require("@tldraw/utils");
27
+ var import_Box = require("../../../primitives/Box");
28
+ var import_RBushIndex = require("./RBushIndex");
29
+ class SpatialIndexManager {
30
+ constructor(editor) {
31
+ this.editor = editor;
32
+ this.rbush = new import_RBushIndex.RBushIndex();
33
+ this.spatialIndexComputed = this.createSpatialIndexComputed();
34
+ }
35
+ rbush;
36
+ spatialIndexComputed;
37
+ lastPageId = null;
38
+ createSpatialIndexComputed() {
39
+ const shapeHistory = this.editor.store.query.filterHistory("shape");
40
+ return (0, import_state.computed)("spatialIndex", (_prevValue, lastComputedEpoch) => {
41
+ if ((0, import_state.isUninitialized)(_prevValue)) {
42
+ return this.buildFromScratch(lastComputedEpoch);
43
+ }
44
+ const shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch);
45
+ if (shapeDiff === import_state.RESET_VALUE) {
46
+ return this.buildFromScratch(lastComputedEpoch);
47
+ }
48
+ const currentPageId = this.editor.getCurrentPageId();
49
+ if (this.lastPageId !== currentPageId) {
50
+ return this.buildFromScratch(lastComputedEpoch);
51
+ }
52
+ if (shapeDiff.length === 0) {
53
+ return lastComputedEpoch;
54
+ }
55
+ this.processIncrementalUpdate(shapeDiff);
56
+ return lastComputedEpoch;
57
+ });
58
+ }
59
+ buildFromScratch(epoch) {
60
+ this.rbush.clear();
61
+ this.lastPageId = this.editor.getCurrentPageId();
62
+ const shapes = this.editor.getCurrentPageShapes();
63
+ const elements = [];
64
+ for (const shape of shapes) {
65
+ const bounds = this.editor.getShapePageBounds(shape.id);
66
+ if (bounds) {
67
+ elements.push({
68
+ minX: bounds.minX,
69
+ minY: bounds.minY,
70
+ maxX: bounds.maxX,
71
+ maxY: bounds.maxY,
72
+ id: shape.id
73
+ });
74
+ }
75
+ }
76
+ this.rbush.bulkLoad(elements);
77
+ return epoch;
78
+ }
79
+ processIncrementalUpdate(shapeDiff) {
80
+ const processedShapeIds = /* @__PURE__ */ new Set();
81
+ for (const changes of shapeDiff) {
82
+ for (const shape of (0, import_utils.objectMapValues)(changes.added)) {
83
+ if ((0, import_tlschema.isShape)(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {
84
+ const bounds = this.editor.getShapePageBounds(shape.id);
85
+ if (bounds) {
86
+ this.rbush.upsert(shape.id, bounds);
87
+ }
88
+ processedShapeIds.add(shape.id);
89
+ }
90
+ }
91
+ for (const shape of (0, import_utils.objectMapValues)(changes.removed)) {
92
+ if ((0, import_tlschema.isShape)(shape)) {
93
+ this.rbush.remove(shape.id);
94
+ processedShapeIds.add(shape.id);
95
+ }
96
+ }
97
+ for (const [from, to] of (0, import_utils.objectMapValues)(changes.updated)) {
98
+ if (!(0, import_tlschema.isShape)(to)) continue;
99
+ processedShapeIds.add(to.id);
100
+ const wasOnPage = this.editor.getAncestorPageId(from) === this.lastPageId;
101
+ const isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId;
102
+ if (isOnPage) {
103
+ const bounds = this.editor.getShapePageBounds(to.id);
104
+ if (bounds) {
105
+ this.rbush.upsert(to.id, bounds);
106
+ }
107
+ } else if (wasOnPage) {
108
+ this.rbush.remove(to.id);
109
+ }
110
+ }
111
+ }
112
+ const allShapeIds = this.rbush.getAllShapeIds();
113
+ for (const shapeId of allShapeIds) {
114
+ if (processedShapeIds.has(shapeId)) continue;
115
+ const currentBounds = this.editor.getShapePageBounds(shapeId);
116
+ const indexedBounds = this.rbush.getBounds(shapeId);
117
+ if (!this.areBoundsEqual(currentBounds, indexedBounds)) {
118
+ if (currentBounds) {
119
+ this.rbush.upsert(shapeId, currentBounds);
120
+ } else {
121
+ this.rbush.remove(shapeId);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ areBoundsEqual(a, b) {
127
+ if (!a && !b) return true;
128
+ if (!a || !b) return false;
129
+ return a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
130
+ }
131
+ /**
132
+ * Get shape IDs within the given bounds.
133
+ * Optimized for viewport culling queries.
134
+ *
135
+ * Note: Results are unordered. If you need z-order, combine with sorted shapes:
136
+ * ```ts
137
+ * const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)
138
+ * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))
139
+ * ```
140
+ *
141
+ * @param bounds - The bounds to search within
142
+ * @returns Unordered set of shape IDs within the bounds
143
+ *
144
+ * @public
145
+ */
146
+ getShapeIdsInsideBounds(bounds) {
147
+ this.spatialIndexComputed.get();
148
+ return this.rbush.search(bounds);
149
+ }
150
+ /**
151
+ * Get shape IDs at a point (with optional margin).
152
+ * Creates a small bounding box around the point and searches the spatial index.
153
+ *
154
+ * Note: Results are unordered. If you need z-order, combine with sorted shapes:
155
+ * ```ts
156
+ * const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)
157
+ * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))
158
+ * ```
159
+ *
160
+ * @param point - The point to search at
161
+ * @param margin - The margin around the point to search (default: 0)
162
+ * @returns Unordered set of shape IDs that could potentially contain the point
163
+ *
164
+ * @public
165
+ */
166
+ getShapeIdsAtPoint(point, margin = 0) {
167
+ this.spatialIndexComputed.get();
168
+ return this.rbush.search(new import_Box.Box(point.x - margin, point.y - margin, margin * 2, margin * 2));
169
+ }
170
+ /**
171
+ * Dispose of the spatial index manager.
172
+ * Clears the R-tree to prevent memory leaks.
173
+ *
174
+ * @public
175
+ */
176
+ dispose() {
177
+ this.rbush.dispose();
178
+ this.lastPageId = null;
179
+ }
180
+ }
181
+ //# sourceMappingURL=SpatialIndexManager.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 type { TLRecord } from '@tldraw/tlschema'\nimport { TLPageId, TLShape, TLShapeId, isShape } 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\tconstructor(public readonly editor: Editor) {\n\t\tthis.rbush = new RBushIndex()\n\t\tthis.spatialIndexComputed = this.createSpatialIndexComputed()\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.buildFromScratch(lastComputedEpoch)\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.buildFromScratch(lastComputedEpoch)\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.buildFromScratch(lastComputedEpoch)\n\t\t\t}\n\n\t\t\t// No shape changes - index is already up to date\n\t\t\tif (shapeDiff.length === 0) {\n\t\t\t\treturn lastComputedEpoch\n\t\t\t}\n\n\t\t\t// Process incremental updates\n\t\t\tthis.processIncrementalUpdate(shapeDiff)\n\n\t\t\treturn lastComputedEpoch\n\t\t})\n\t}\n\n\tprivate buildFromScratch(epoch: number): number {\n\t\tthis.rbush.clear()\n\t\tthis.lastPageId = this.editor.getCurrentPageId()\n\n\t\tconst shapes = this.editor.getCurrentPageShapes()\n\t\tconst elements: SpatialElement[] = []\n\n\t\t// Collect all shape elements for bulk loading\n\t\tfor (const shape of shapes) {\n\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\tif (bounds) {\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\n\t\treturn epoch\n\t}\n\n\tprivate processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): void {\n\t\t// Track shapes we've already processed from the diff\n\t\tconst processedShapeIds = new Set<TLShapeId>()\n\n\t\t// 1. Process shape additions, removals, and updates from diff\n\t\tfor (const changes of shapeDiff) {\n\t\t\t// Handle additions (only for shapes on current page)\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) {\n\t\t\t\t\t\tthis.rbush.upsert(shape.id, bounds)\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\t// Handle removals\n\t\t\tfor (const shape of objectMapValues(changes.removed) as TLShape[]) {\n\t\t\t\tif (isShape(shape)) {\n\t\t\t\t\tthis.rbush.remove(shape.id)\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle updated shapes: page changes and bounds updates\n\t\t\tfor (const [from, 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 wasOnPage = this.editor.getAncestorPageId(from) === this.lastPageId\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) {\n\t\t\t\t\t\tthis.rbush.upsert(to.id, bounds)\n\t\t\t\t\t}\n\t\t\t\t} else if (wasOnPage) {\n\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 2. Check remaining shapes in index for bounds changes\n\t\t// This handles shapes with computed bounds (arrows bound to moved shapes, groups with moved children, etc.)\n\t\tconst allShapeIds = this.rbush.getAllShapeIds()\n\n\t\tfor (const shapeId of allShapeIds) {\n\t\t\tif (processedShapeIds.has(shapeId)) continue\n\n\t\t\tconst currentBounds = this.editor.getShapePageBounds(shapeId)\n\t\t\tconst indexedBounds = this.rbush.getBounds(shapeId)\n\n\t\t\tif (!this.areBoundsEqual(currentBounds, indexedBounds)) {\n\t\t\t\tif (currentBounds) {\n\t\t\t\t\tthis.rbush.upsert(shapeId, currentBounds)\n\t\t\t\t} else {\n\t\t\t\t\tthis.rbush.remove(shapeId)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate areBoundsEqual(a: Box | undefined, b: Box | undefined): 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
+ "names": []
7
+ }
@@ -68,7 +68,6 @@ __export(TickManager_exports, {
68
68
  });
69
69
  module.exports = __toCommonJS(TickManager_exports);
70
70
  var import_utils = require("@tldraw/utils");
71
- var import_Vec = require("../../../primitives/Vec");
72
71
  var _dispose_dec, _tick_dec, _init;
73
72
  const throttleToNextFrame = typeof process !== "undefined" && process.env.NODE_ENV === "test" ? (
74
73
  // 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
@@ -86,7 +85,6 @@ class TickManager {
86
85
  __publicField(this, "cancelRaf");
87
86
  __publicField(this, "isPaused", true);
88
87
  __publicField(this, "now", 0);
89
- __publicField(this, "prevPoint", new import_Vec.Vec());
90
88
  this.editor.disposables.add(this.dispose);
91
89
  this.start();
92
90
  }
@@ -103,7 +101,7 @@ class TickManager {
103
101
  const now = Date.now();
104
102
  const elapsed = now - this.now;
105
103
  this.now = now;
106
- this.updatePointerVelocity(elapsed);
104
+ this.editor.inputs.updatePointerVelocity(elapsed);
107
105
  this.editor.emit("frame", elapsed);
108
106
  this.editor.emit("tick", elapsed);
109
107
  this.cancelRaf = throttleToNextFrame(this.tick);
@@ -112,25 +110,6 @@ class TickManager {
112
110
  this.isPaused = true;
113
111
  this.cancelRaf?.();
114
112
  }
115
- updatePointerVelocity(elapsed) {
116
- const {
117
- prevPoint,
118
- editor: {
119
- inputs: { currentScreenPoint, pointerVelocity }
120
- }
121
- } = this;
122
- if (elapsed === 0) return;
123
- const delta = import_Vec.Vec.Sub(currentScreenPoint, prevPoint);
124
- this.prevPoint = currentScreenPoint.clone();
125
- const length = delta.len();
126
- const direction = length ? delta.div(length) : new import_Vec.Vec(0, 0);
127
- const next = pointerVelocity.clone().lrp(direction.mul(length / elapsed), 0.5);
128
- if (Math.abs(next.x) < 0.01) next.x = 0;
129
- if (Math.abs(next.y) < 0.01) next.y = 0;
130
- if (!pointerVelocity.equals(next)) {
131
- this.editor.inputs.pointerVelocity = next;
132
- }
133
- }
134
113
  }
135
114
  _init = __decoratorStart(null);
136
115
  __decorateElement(_init, 1, "tick", _tick_dec, TickManager);
@@ -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 { Vec } from '../../../primitives/Vec'\nimport { 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\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.updatePointerVelocity(elapsed)\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\tprivate prevPoint = new Vec()\n\n\tupdatePointerVelocity(elapsed: number) {\n\t\tconst {\n\t\t\tprevPoint,\n\t\t\teditor: {\n\t\t\t\tinputs: { currentScreenPoint, pointerVelocity },\n\t\t\t},\n\t\t} = this\n\n\t\tif (elapsed === 0) return\n\n\t\tconst delta = Vec.Sub(currentScreenPoint, prevPoint)\n\t\tthis.prevPoint = 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// consider adjusting this with an easing rather than a linear interpolation\n\t\tconst next = pointerVelocity.clone().lrp(direction.mul(length / elapsed), 0.5)\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.editor.inputs.pointerVelocity = next\n\t\t}\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,iBAAoB;AADpB;AAIA,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,aAAAA;AAmBH,aAAC,oBAiBD,gBAAC;AAlCK,MAAM,YAAY;AAAA,EACxB,YAAmB,QAAgB;AAAhB;AADb;AAMN;AACA,oCAAW;AACX,+BAAM;AAiCN,wBAAQ,aAAY,IAAI,eAAI;AAvC3B,SAAK,OAAO,YAAY,IAAI,KAAK,OAAO;AACxC,SAAK,MAAM;AAAA,EACZ;AAAA,EAMA,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,sBAAsB,OAAO;AAClC,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;AAAA,EAIA,sBAAsB,SAAiB;AACtC,UAAM;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,QACP,QAAQ,EAAE,oBAAoB,gBAAgB;AAAA,MAC/C;AAAA,IACD,IAAI;AAEJ,QAAI,YAAY,EAAG;AAEnB,UAAM,QAAQ,eAAI,IAAI,oBAAoB,SAAS;AACnD,SAAK,YAAY,mBAAmB,MAAM;AAE1C,UAAM,SAAS,MAAM,IAAI;AACzB,UAAM,YAAY,SAAS,MAAM,IAAI,MAAM,IAAI,IAAI,eAAI,GAAG,CAAC;AAG3D,UAAM,OAAO,gBAAgB,MAAM,EAAE,IAAI,UAAU,IAAI,SAAS,OAAO,GAAG,GAAG;AAG7E,QAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAM,MAAK,IAAI;AACtC,QAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAM,MAAK,IAAI;AAEtC,QAAI,CAAC,gBAAgB,OAAO,IAAI,GAAG;AAClC,WAAK,OAAO,OAAO,kBAAkB;AAAA,IACtC;AAAA,EACD;AACD;AAtEO;AAkBN,oCADA,WAjBY;AAmCZ,uCADA,cAlCY;AAAN,2BAAM;",
4
+ "sourcesContent": ["import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils'\nimport { 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.inputs.updatePointerVelocity(elapsed)\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;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAAlE;AAGA,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,aAAAA;AAoBH,aAAC,oBAiBD,gBAAC;AAlCK,MAAM,YAAY;AAAA,EACxB,YAAmB,QAAgB;AAAhB;AADb;AAMN;AACA,oCAAW;AACX,+BAAM;AANL,SAAK,OAAO,YAAY,IAAI,KAAK,OAAO;AACxC,SAAK,MAAM;AAAA,EACZ;AAAA,EAMA,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,OAAO,sBAAsB,OAAO;AAChD,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;AAxCO;AAkBN,oCADA,WAjBY;AAmCZ,uCADA,cAlCY;AAAN,2BAAM;",
6
6
  "names": ["_throttleToNextFrame"]
7
7
  }
@@ -95,7 +95,7 @@ class ShapeUtil {
95
95
  * @param shape - The shape.
96
96
  * @public
97
97
  */
98
- canSnap(_shape) {
98
+ canSnap(shape) {
99
99
  return true;
100
100
  }
101
101
  /**
@@ -104,7 +104,7 @@ class ShapeUtil {
104
104
  * @param shape - The shape.
105
105
  * @public
106
106
  */
107
- canTabTo(_shape) {
107
+ canTabTo(shape) {
108
108
  return true;
109
109
  }
110
110
  /**
@@ -112,7 +112,7 @@ class ShapeUtil {
112
112
  *
113
113
  * @public
114
114
  */
115
- canScroll(_shape) {
115
+ canScroll(shape) {
116
116
  return false;
117
117
  }
118
118
  /**
@@ -128,7 +128,7 @@ class ShapeUtil {
128
128
  *
129
129
  * @public
130
130
  */
131
- canEdit(_shape) {
131
+ canEdit(shape, info) {
132
132
  return false;
133
133
  }
134
134
  /**
@@ -136,7 +136,7 @@ class ShapeUtil {
136
136
  *
137
137
  * @public
138
138
  */
139
- canResize(_shape) {
139
+ canResize(shape) {
140
140
  return true;
141
141
  }
142
142
  /**
@@ -144,7 +144,7 @@ class ShapeUtil {
144
144
  *
145
145
  * @public
146
146
  */
147
- canResizeChildren(_shape) {
147
+ canResizeChildren(shape) {
148
148
  return true;
149
149
  }
150
150
  /**
@@ -152,7 +152,15 @@ class ShapeUtil {
152
152
  *
153
153
  * @public
154
154
  */
155
- canEditInReadonly(_shape) {
155
+ canEditInReadonly(shape) {
156
+ return false;
157
+ }
158
+ /**
159
+ * Whether the shape can be edited while locked or while an ancestor is locked.
160
+ *
161
+ * @public
162
+ */
163
+ canEditWhileLocked(shape) {
156
164
  return false;
157
165
  }
158
166
  /**
@@ -160,7 +168,7 @@ class ShapeUtil {
160
168
  *
161
169
  * @public
162
170
  */
163
- canCrop(_shape) {
171
+ canCrop(shape) {
164
172
  return false;
165
173
  }
166
174
  /**
@@ -172,7 +180,7 @@ class ShapeUtil {
172
180
  *
173
181
  * @public
174
182
  */
175
- canBeLaidOut(_shape, _info) {
183
+ canBeLaidOut(shape, info) {
176
184
  return true;
177
185
  }
178
186
  /**
@@ -182,7 +190,7 @@ class ShapeUtil {
182
190
  *
183
191
  * @param shape - The shape.
184
192
  */
185
- canCull(_shape) {
193
+ canCull(shape) {
186
194
  return true;
187
195
  }
188
196
  /**
@@ -194,7 +202,7 @@ class ShapeUtil {
194
202
  *
195
203
  * @internal
196
204
  */
197
- providesBackgroundForChildren(_shape) {
205
+ providesBackgroundForChildren(shape) {
198
206
  return false;
199
207
  }
200
208
  /**
@@ -202,7 +210,7 @@ class ShapeUtil {
202
210
  *
203
211
  * @public
204
212
  */
205
- hideResizeHandles(_shape) {
213
+ hideResizeHandles(shape) {
206
214
  return false;
207
215
  }
208
216
  /**
@@ -210,7 +218,7 @@ class ShapeUtil {
210
218
  *
211
219
  * @public
212
220
  */
213
- hideRotateHandle(_shape) {
221
+ hideRotateHandle(shape) {
214
222
  return false;
215
223
  }
216
224
  /**
@@ -218,7 +226,7 @@ class ShapeUtil {
218
226
  *
219
227
  * @public
220
228
  */
221
- hideSelectionBoundsBg(_shape) {
229
+ hideSelectionBoundsBg(shape) {
222
230
  return false;
223
231
  }
224
232
  /**
@@ -226,7 +234,7 @@ class ShapeUtil {
226
234
  *
227
235
  * @public
228
236
  */
229
- hideSelectionBoundsFg(_shape) {
237
+ hideSelectionBoundsFg(shape) {
230
238
  return false;
231
239
  }
232
240
  /**
@@ -234,7 +242,7 @@ class ShapeUtil {
234
242
  *
235
243
  * @public
236
244
  */
237
- isAspectRatioLocked(_shape) {
245
+ isAspectRatioLocked(shape) {
238
246
  return false;
239
247
  }
240
248
  /**
@@ -244,10 +252,10 @@ class ShapeUtil {
244
252
  * useful in cases like annotating on top of an image, where you usually want to avoid extra
245
253
  * padding around the image if you don't need it.
246
254
  *
247
- * @param _shape - The shape to check
255
+ * @param shape - The shape to check
248
256
  * @returns True if this shape should be treated as an export bounds container
249
257
  */
250
- isExportBoundsContainer(_shape) {
258
+ isExportBoundsContainer(shape) {
251
259
  return false;
252
260
  }
253
261
  /**
@@ -257,7 +265,7 @@ class ShapeUtil {
257
265
  * @param type - The shape type.
258
266
  * @public
259
267
  */
260
- canReceiveNewChildrenOfType(_shape, _type) {
268
+ canReceiveNewChildrenOfType(shape, _type) {
261
269
  return false;
262
270
  }
263
271
  /** @internal */
@@ -279,20 +287,20 @@ class ShapeUtil {
279
287
  * Get the geometry to use when snapping to this this shape in translate/resize operations. See
280
288
  * {@link BoundsSnapGeometry} for details.
281
289
  */
282
- getBoundsSnapGeometry(_shape) {
290
+ getBoundsSnapGeometry(shape) {
283
291
  return {};
284
292
  }
285
293
  /**
286
294
  * Get the geometry to use when snapping handles to this shape. See {@link HandleSnapGeometry}
287
295
  * for details.
288
296
  */
289
- getHandleSnapGeometry(_shape) {
297
+ getHandleSnapGeometry(shape) {
290
298
  return {};
291
299
  }
292
- getText(_shape) {
300
+ getText(shape) {
293
301
  return void 0;
294
302
  }
295
- getAriaDescriptor(_shape) {
303
+ getAriaDescriptor(shape) {
296
304
  return void 0;
297
305
  }
298
306
  }