@tldraw/editor 5.1.1 → 5.2.0-canary.019da1aa690a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +7 -1
  2. package/dist-cjs/index.d.ts +52 -50
  3. package/dist-cjs/index.js +4 -4
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/components/MenuClickCapture.js +8 -5
  6. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +4 -1
  8. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +3 -3
  9. package/dist-cjs/lib/components/default-components/DefaultLoadingScreen.js +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultLoadingScreen.js.map +2 -2
  11. package/dist-cjs/lib/components/default-components/DefaultShapeErrorFallback.js +1 -1
  12. package/dist-cjs/lib/components/default-components/DefaultShapeErrorFallback.js.map +3 -3
  13. package/dist-cjs/lib/components/default-components/DefaultSvgDefs.js +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultSvgDefs.js.map +2 -2
  15. package/dist-cjs/lib/editor/Editor.js +121 -55
  16. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  17. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +2 -2
  18. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  19. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +2 -2
  20. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  21. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +2 -2
  22. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
  23. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +8 -58
  24. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  25. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +3 -3
  26. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +1 -2
  28. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
  29. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +24 -2
  30. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
  31. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +14 -3
  32. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  33. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +4 -2
  34. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +2 -2
  35. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +7 -3
  36. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  37. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +0 -1
  38. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  39. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +15 -2
  40. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  41. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +79 -0
  42. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +7 -0
  43. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -0
  44. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  45. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  46. package/dist-cjs/lib/editor/types/event-types.js +0 -2
  47. package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
  48. package/dist-cjs/lib/hooks/useCanvasEvents.js +14 -7
  49. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  50. package/dist-cjs/lib/hooks/usePresence.js.map +2 -2
  51. package/dist-cjs/lib/license/LicenseProvider.js +3 -1
  52. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  53. package/dist-cjs/lib/primitives/utils.js +2 -2
  54. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  55. package/dist-cjs/lib/utils/dom.js +5 -3
  56. package/dist-cjs/lib/utils/dom.js.map +2 -2
  57. package/dist-cjs/lib/utils/getPointerInfo.js +2 -1
  58. package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
  59. package/dist-cjs/lib/utils/pointer.js +32 -0
  60. package/dist-cjs/lib/utils/pointer.js.map +7 -0
  61. package/dist-cjs/version.js +3 -3
  62. package/dist-cjs/version.js.map +1 -1
  63. package/dist-esm/index.d.mts +52 -50
  64. package/dist-esm/index.mjs +5 -7
  65. package/dist-esm/index.mjs.map +2 -2
  66. package/dist-esm/lib/components/MenuClickCapture.mjs +8 -5
  67. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  68. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +4 -1
  69. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +3 -3
  70. package/dist-esm/lib/components/default-components/DefaultLoadingScreen.mjs +2 -2
  71. package/dist-esm/lib/components/default-components/DefaultLoadingScreen.mjs.map +2 -2
  72. package/dist-esm/lib/components/default-components/DefaultShapeErrorFallback.mjs +1 -1
  73. package/dist-esm/lib/components/default-components/DefaultShapeErrorFallback.mjs.map +3 -3
  74. package/dist-esm/lib/components/default-components/DefaultSvgDefs.mjs +2 -2
  75. package/dist-esm/lib/components/default-components/DefaultSvgDefs.mjs.map +2 -2
  76. package/dist-esm/lib/editor/Editor.mjs +121 -55
  77. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  78. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +2 -2
  79. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  80. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +2 -2
  81. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  82. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +2 -2
  83. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
  84. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +8 -58
  85. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  86. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +3 -3
  87. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  88. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +1 -2
  89. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
  90. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +24 -2
  91. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
  92. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +14 -3
  93. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  94. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +4 -2
  95. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +2 -2
  96. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -3
  97. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  98. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +0 -1
  99. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  100. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +15 -2
  101. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  102. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +59 -0
  103. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +7 -0
  104. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -0
  105. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  106. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  107. package/dist-esm/lib/editor/types/event-types.mjs +0 -2
  108. package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
  109. package/dist-esm/lib/hooks/useCanvasEvents.mjs +14 -7
  110. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  111. package/dist-esm/lib/hooks/usePresence.mjs.map +2 -2
  112. package/dist-esm/lib/license/LicenseProvider.mjs +3 -1
  113. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  114. package/dist-esm/lib/primitives/utils.mjs +2 -2
  115. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  116. package/dist-esm/lib/utils/dom.mjs +5 -3
  117. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  118. package/dist-esm/lib/utils/getPointerInfo.mjs +2 -1
  119. package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
  120. package/dist-esm/lib/utils/pointer.mjs +12 -0
  121. package/dist-esm/lib/utils/pointer.mjs.map +7 -0
  122. package/dist-esm/version.mjs +3 -3
  123. package/dist-esm/version.mjs.map +1 -1
  124. package/editor.css +5 -3
  125. package/package.json +11 -8
  126. package/src/index.ts +2 -5
  127. package/src/lib/components/MenuClickCapture.tsx +8 -4
  128. package/src/lib/components/default-components/DefaultErrorFallback.tsx +4 -1
  129. package/src/lib/components/default-components/DefaultLoadingScreen.tsx +1 -1
  130. package/src/lib/components/default-components/DefaultShapeErrorFallback.tsx +4 -3
  131. package/src/lib/components/default-components/DefaultSvgDefs.tsx +1 -1
  132. package/src/lib/editor/Editor.ts +168 -72
  133. package/src/lib/editor/derivations/bindingsIndex.ts +1 -1
  134. package/src/lib/editor/derivations/parentsToChildren.ts +1 -1
  135. package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +1 -1
  136. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +54 -74
  137. package/src/lib/editor/managers/ClickManager/ClickManager.ts +15 -65
  138. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +43 -16
  139. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +8 -5
  140. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +4 -4
  141. package/src/lib/editor/managers/FocusManager/FocusManager.ts +1 -2
  142. package/src/lib/editor/managers/FontManager/FontManager.test.ts +13 -9
  143. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +32 -0
  144. package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +34 -4
  145. package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +61 -0
  146. package/src/lib/editor/managers/InputsManager/InputsManager.ts +16 -4
  147. package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +9 -2
  148. package/src/lib/editor/managers/TextManager/TextManager.test.ts +16 -14
  149. package/src/lib/editor/managers/TextManager/TextManager.ts +17 -2
  150. package/src/lib/editor/managers/TickManager/TickManager.test.ts +0 -40
  151. package/src/lib/editor/managers/TickManager/TickManager.ts +0 -1
  152. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +12 -2
  153. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +27 -2
  154. package/src/lib/editor/overlays/strokeShapeIndicators.ts +86 -0
  155. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -0
  156. package/src/lib/editor/tools/StateNode.ts +0 -2
  157. package/src/lib/editor/types/event-types.ts +2 -6
  158. package/src/lib/hooks/useCanvasEvents.ts +19 -12
  159. package/src/lib/hooks/usePresence.ts +2 -2
  160. package/src/lib/license/LicenseProvider.tsx +3 -1
  161. package/src/lib/primitives/utils.ts +1 -1
  162. package/src/lib/utils/dom.ts +5 -3
  163. package/src/lib/utils/getPointerInfo.ts +2 -1
  164. package/src/lib/utils/pointer.test.ts +48 -0
  165. package/src/lib/utils/pointer.ts +18 -0
  166. package/src/version.ts +3 -3
  167. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +0 -161
  168. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +0 -7
  169. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +0 -141
  170. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +0 -7
  171. package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +0 -216
@@ -20,7 +20,7 @@ function fromScratch(bindingsQuery) {
20
20
  }
21
21
  return shapesToBindings;
22
22
  }
23
- const bindingsIndex = (editor) => {
23
+ function bindingsIndex(editor) {
24
24
  const { store } = editor;
25
25
  const bindingsHistory = store.query.filterHistory("binding");
26
26
  const bindingsQuery = store.query.records("binding");
@@ -81,7 +81,7 @@ const bindingsIndex = (editor) => {
81
81
  }
82
82
  return nextValue ?? lastValue;
83
83
  });
84
- };
84
+ }
85
85
  export {
86
86
  bindingsIndex
87
87
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/editor/derivations/bindingsIndex.ts"],
4
- "sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport { TLBinding, TLShapeId } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport type { Editor } from '../Editor'\n\ntype TLBindingsIndex = Map<TLShapeId, TLBinding[]>\n\nfunction fromScratch(bindingsQuery: Computed<TLBinding[], unknown>) {\n\tconst allBindings = bindingsQuery.get() as TLBinding[]\n\n\tconst shapesToBindings: TLBindingsIndex = new Map()\n\n\tfor (const binding of allBindings) {\n\t\tconst { fromId, toId } = binding\n\t\tconst bindingsForFromShape = shapesToBindings.get(fromId)\n\t\tif (!bindingsForFromShape) {\n\t\t\tshapesToBindings.set(fromId, [binding])\n\t\t} else {\n\t\t\tbindingsForFromShape.push(binding)\n\t\t}\n\t\tconst bindingsForToShape = shapesToBindings.get(toId)\n\t\tif (!bindingsForToShape) {\n\t\t\tshapesToBindings.set(toId, [binding])\n\t\t} else {\n\t\t\tbindingsForToShape.push(binding)\n\t\t}\n\t}\n\n\treturn shapesToBindings\n}\n\nexport const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {\n\tconst { store } = editor\n\tconst bindingsHistory = store.query.filterHistory('binding')\n\tconst bindingsQuery = store.query.records('binding')\n\n\treturn computed<TLBindingsIndex>('arrowBindingsIndex', (_lastValue, lastComputedEpoch) => {\n\t\tif (isUninitialized(_lastValue)) {\n\t\t\treturn fromScratch(bindingsQuery)\n\t\t}\n\n\t\tconst lastValue = _lastValue\n\n\t\tconst diff = bindingsHistory.getDiffSince(lastComputedEpoch)\n\n\t\tif (diff === RESET_VALUE) {\n\t\t\treturn fromScratch(bindingsQuery)\n\t\t}\n\n\t\tlet nextValue: TLBindingsIndex | undefined = undefined\n\n\t\tfunction removingBinding(binding: TLBinding) {\n\t\t\tnextValue ??= new Map(lastValue)\n\t\t\tconst prevFrom = nextValue.get(binding.fromId)\n\t\t\tconst nextFrom = prevFrom?.filter((b) => b.id !== binding.id)\n\t\t\tif (!nextFrom?.length) {\n\t\t\t\tnextValue.delete(binding.fromId)\n\t\t\t} else {\n\t\t\t\tnextValue.set(binding.fromId, nextFrom)\n\t\t\t}\n\t\t\tconst prevTo = nextValue.get(binding.toId)\n\t\t\tconst nextTo = prevTo?.filter((b) => b.id !== binding.id)\n\t\t\tif (!nextTo?.length) {\n\t\t\t\tnextValue.delete(binding.toId)\n\t\t\t} else {\n\t\t\t\tnextValue.set(binding.toId, nextTo)\n\t\t\t}\n\t\t}\n\n\t\tfunction ensureNewArray(shapeId: TLShapeId) {\n\t\t\tnextValue ??= new Map(lastValue)\n\n\t\t\tlet result = nextValue.get(shapeId)\n\t\t\tif (!result) {\n\t\t\t\tresult = []\n\t\t\t\tnextValue.set(shapeId, result)\n\t\t\t} else if (result === lastValue.get(shapeId)) {\n\t\t\t\tresult = result.slice(0)\n\t\t\t\tnextValue.set(shapeId, result)\n\t\t\t}\n\t\t\treturn result\n\t\t}\n\n\t\tfunction addBinding(binding: TLBinding) {\n\t\t\tensureNewArray(binding.fromId).push(binding)\n\t\t\tensureNewArray(binding.toId).push(binding)\n\t\t}\n\n\t\tfor (const changes of diff) {\n\t\t\tfor (const newBinding of objectMapValues(changes.added)) {\n\t\t\t\taddBinding(newBinding)\n\t\t\t}\n\n\t\t\tfor (const [prev, next] of objectMapValues(changes.updated)) {\n\t\t\t\tremovingBinding(prev)\n\t\t\t\taddBinding(next)\n\t\t\t}\n\n\t\t\tfor (const prev of objectMapValues(changes.removed)) {\n\t\t\t\tremovingBinding(prev)\n\t\t\t}\n\t\t}\n\n\t\t// TODO: add diff entries if we need them\n\t\treturn nextValue ?? lastValue\n\t})\n}\n"],
5
- "mappings": "AAAA,SAAmB,aAAa,UAAU,uBAAuB;AAEjE,SAAS,uBAAuB;AAKhC,SAAS,YAAY,eAA+C;AACnE,QAAM,cAAc,cAAc,IAAI;AAEtC,QAAM,mBAAoC,oBAAI,IAAI;AAElD,aAAW,WAAW,aAAa;AAClC,UAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,UAAM,uBAAuB,iBAAiB,IAAI,MAAM;AACxD,QAAI,CAAC,sBAAsB;AAC1B,uBAAiB,IAAI,QAAQ,CAAC,OAAO,CAAC;AAAA,IACvC,OAAO;AACN,2BAAqB,KAAK,OAAO;AAAA,IAClC;AACA,UAAM,qBAAqB,iBAAiB,IAAI,IAAI;AACpD,QAAI,CAAC,oBAAoB;AACxB,uBAAiB,IAAI,MAAM,CAAC,OAAO,CAAC;AAAA,IACrC,OAAO;AACN,yBAAmB,KAAK,OAAO;AAAA,IAChC;AAAA,EACD;AAEA,SAAO;AACR;AAEO,MAAM,gBAAgB,CAAC,WAA8C;AAC3E,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,kBAAkB,MAAM,MAAM,cAAc,SAAS;AAC3D,QAAM,gBAAgB,MAAM,MAAM,QAAQ,SAAS;AAEnD,SAAO,SAA0B,sBAAsB,CAAC,YAAY,sBAAsB;AACzF,QAAI,gBAAgB,UAAU,GAAG;AAChC,aAAO,YAAY,aAAa;AAAA,IACjC;AAEA,UAAM,YAAY;AAElB,UAAM,OAAO,gBAAgB,aAAa,iBAAiB;AAE3D,QAAI,SAAS,aAAa;AACzB,aAAO,YAAY,aAAa;AAAA,IACjC;AAEA,QAAI,YAAyC;AAE7C,aAAS,gBAAgB,SAAoB;AAC5C,oBAAc,IAAI,IAAI,SAAS;AAC/B,YAAM,WAAW,UAAU,IAAI,QAAQ,MAAM;AAC7C,YAAM,WAAW,UAAU,OAAO,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AAC5D,UAAI,CAAC,UAAU,QAAQ;AACtB,kBAAU,OAAO,QAAQ,MAAM;AAAA,MAChC,OAAO;AACN,kBAAU,IAAI,QAAQ,QAAQ,QAAQ;AAAA,MACvC;AACA,YAAM,SAAS,UAAU,IAAI,QAAQ,IAAI;AACzC,YAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AACxD,UAAI,CAAC,QAAQ,QAAQ;AACpB,kBAAU,OAAO,QAAQ,IAAI;AAAA,MAC9B,OAAO;AACN,kBAAU,IAAI,QAAQ,MAAM,MAAM;AAAA,MACnC;AAAA,IACD;AAEA,aAAS,eAAe,SAAoB;AAC3C,oBAAc,IAAI,IAAI,SAAS;AAE/B,UAAI,SAAS,UAAU,IAAI,OAAO;AAClC,UAAI,CAAC,QAAQ;AACZ,iBAAS,CAAC;AACV,kBAAU,IAAI,SAAS,MAAM;AAAA,MAC9B,WAAW,WAAW,UAAU,IAAI,OAAO,GAAG;AAC7C,iBAAS,OAAO,MAAM,CAAC;AACvB,kBAAU,IAAI,SAAS,MAAM;AAAA,MAC9B;AACA,aAAO;AAAA,IACR;AAEA,aAAS,WAAW,SAAoB;AACvC,qBAAe,QAAQ,MAAM,EAAE,KAAK,OAAO;AAC3C,qBAAe,QAAQ,IAAI,EAAE,KAAK,OAAO;AAAA,IAC1C;AAEA,eAAW,WAAW,MAAM;AAC3B,iBAAW,cAAc,gBAAgB,QAAQ,KAAK,GAAG;AACxD,mBAAW,UAAU;AAAA,MACtB;AAEA,iBAAW,CAAC,MAAM,IAAI,KAAK,gBAAgB,QAAQ,OAAO,GAAG;AAC5D,wBAAgB,IAAI;AACpB,mBAAW,IAAI;AAAA,MAChB;AAEA,iBAAW,QAAQ,gBAAgB,QAAQ,OAAO,GAAG;AACpD,wBAAgB,IAAI;AAAA,MACrB;AAAA,IACD;AAGA,WAAO,aAAa;AAAA,EACrB,CAAC;AACF;",
4
+ "sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport { TLBinding, TLShapeId } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport type { Editor } from '../Editor'\n\ntype TLBindingsIndex = Map<TLShapeId, TLBinding[]>\n\nfunction fromScratch(bindingsQuery: Computed<TLBinding[], unknown>) {\n\tconst allBindings = bindingsQuery.get() as TLBinding[]\n\n\tconst shapesToBindings: TLBindingsIndex = new Map()\n\n\tfor (const binding of allBindings) {\n\t\tconst { fromId, toId } = binding\n\t\tconst bindingsForFromShape = shapesToBindings.get(fromId)\n\t\tif (!bindingsForFromShape) {\n\t\t\tshapesToBindings.set(fromId, [binding])\n\t\t} else {\n\t\t\tbindingsForFromShape.push(binding)\n\t\t}\n\t\tconst bindingsForToShape = shapesToBindings.get(toId)\n\t\tif (!bindingsForToShape) {\n\t\t\tshapesToBindings.set(toId, [binding])\n\t\t} else {\n\t\t\tbindingsForToShape.push(binding)\n\t\t}\n\t}\n\n\treturn shapesToBindings\n}\n\nexport function bindingsIndex(editor: Editor): Computed<TLBindingsIndex> {\n\tconst { store } = editor\n\tconst bindingsHistory = store.query.filterHistory('binding')\n\tconst bindingsQuery = store.query.records('binding')\n\n\treturn computed<TLBindingsIndex>('arrowBindingsIndex', (_lastValue, lastComputedEpoch) => {\n\t\tif (isUninitialized(_lastValue)) {\n\t\t\treturn fromScratch(bindingsQuery)\n\t\t}\n\n\t\tconst lastValue = _lastValue\n\n\t\tconst diff = bindingsHistory.getDiffSince(lastComputedEpoch)\n\n\t\tif (diff === RESET_VALUE) {\n\t\t\treturn fromScratch(bindingsQuery)\n\t\t}\n\n\t\tlet nextValue: TLBindingsIndex | undefined = undefined\n\n\t\tfunction removingBinding(binding: TLBinding) {\n\t\t\tnextValue ??= new Map(lastValue)\n\t\t\tconst prevFrom = nextValue.get(binding.fromId)\n\t\t\tconst nextFrom = prevFrom?.filter((b) => b.id !== binding.id)\n\t\t\tif (!nextFrom?.length) {\n\t\t\t\tnextValue.delete(binding.fromId)\n\t\t\t} else {\n\t\t\t\tnextValue.set(binding.fromId, nextFrom)\n\t\t\t}\n\t\t\tconst prevTo = nextValue.get(binding.toId)\n\t\t\tconst nextTo = prevTo?.filter((b) => b.id !== binding.id)\n\t\t\tif (!nextTo?.length) {\n\t\t\t\tnextValue.delete(binding.toId)\n\t\t\t} else {\n\t\t\t\tnextValue.set(binding.toId, nextTo)\n\t\t\t}\n\t\t}\n\n\t\tfunction ensureNewArray(shapeId: TLShapeId) {\n\t\t\tnextValue ??= new Map(lastValue)\n\n\t\t\tlet result = nextValue.get(shapeId)\n\t\t\tif (!result) {\n\t\t\t\tresult = []\n\t\t\t\tnextValue.set(shapeId, result)\n\t\t\t} else if (result === lastValue.get(shapeId)) {\n\t\t\t\tresult = result.slice(0)\n\t\t\t\tnextValue.set(shapeId, result)\n\t\t\t}\n\t\t\treturn result\n\t\t}\n\n\t\tfunction addBinding(binding: TLBinding) {\n\t\t\tensureNewArray(binding.fromId).push(binding)\n\t\t\tensureNewArray(binding.toId).push(binding)\n\t\t}\n\n\t\tfor (const changes of diff) {\n\t\t\tfor (const newBinding of objectMapValues(changes.added)) {\n\t\t\t\taddBinding(newBinding)\n\t\t\t}\n\n\t\t\tfor (const [prev, next] of objectMapValues(changes.updated)) {\n\t\t\t\tremovingBinding(prev)\n\t\t\t\taddBinding(next)\n\t\t\t}\n\n\t\t\tfor (const prev of objectMapValues(changes.removed)) {\n\t\t\t\tremovingBinding(prev)\n\t\t\t}\n\t\t}\n\n\t\t// TODO: add diff entries if we need them\n\t\treturn nextValue ?? lastValue\n\t})\n}\n"],
5
+ "mappings": "AAAA,SAAmB,aAAa,UAAU,uBAAuB;AAEjE,SAAS,uBAAuB;AAKhC,SAAS,YAAY,eAA+C;AACnE,QAAM,cAAc,cAAc,IAAI;AAEtC,QAAM,mBAAoC,oBAAI,IAAI;AAElD,aAAW,WAAW,aAAa;AAClC,UAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,UAAM,uBAAuB,iBAAiB,IAAI,MAAM;AACxD,QAAI,CAAC,sBAAsB;AAC1B,uBAAiB,IAAI,QAAQ,CAAC,OAAO,CAAC;AAAA,IACvC,OAAO;AACN,2BAAqB,KAAK,OAAO;AAAA,IAClC;AACA,UAAM,qBAAqB,iBAAiB,IAAI,IAAI;AACpD,QAAI,CAAC,oBAAoB;AACxB,uBAAiB,IAAI,MAAM,CAAC,OAAO,CAAC;AAAA,IACrC,OAAO;AACN,yBAAmB,KAAK,OAAO;AAAA,IAChC;AAAA,EACD;AAEA,SAAO;AACR;AAEO,SAAS,cAAc,QAA2C;AACxE,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,kBAAkB,MAAM,MAAM,cAAc,SAAS;AAC3D,QAAM,gBAAgB,MAAM,MAAM,QAAQ,SAAS;AAEnD,SAAO,SAA0B,sBAAsB,CAAC,YAAY,sBAAsB;AACzF,QAAI,gBAAgB,UAAU,GAAG;AAChC,aAAO,YAAY,aAAa;AAAA,IACjC;AAEA,UAAM,YAAY;AAElB,UAAM,OAAO,gBAAgB,aAAa,iBAAiB;AAE3D,QAAI,SAAS,aAAa;AACzB,aAAO,YAAY,aAAa;AAAA,IACjC;AAEA,QAAI,YAAyC;AAE7C,aAAS,gBAAgB,SAAoB;AAC5C,oBAAc,IAAI,IAAI,SAAS;AAC/B,YAAM,WAAW,UAAU,IAAI,QAAQ,MAAM;AAC7C,YAAM,WAAW,UAAU,OAAO,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AAC5D,UAAI,CAAC,UAAU,QAAQ;AACtB,kBAAU,OAAO,QAAQ,MAAM;AAAA,MAChC,OAAO;AACN,kBAAU,IAAI,QAAQ,QAAQ,QAAQ;AAAA,MACvC;AACA,YAAM,SAAS,UAAU,IAAI,QAAQ,IAAI;AACzC,YAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AACxD,UAAI,CAAC,QAAQ,QAAQ;AACpB,kBAAU,OAAO,QAAQ,IAAI;AAAA,MAC9B,OAAO;AACN,kBAAU,IAAI,QAAQ,MAAM,MAAM;AAAA,MACnC;AAAA,IACD;AAEA,aAAS,eAAe,SAAoB;AAC3C,oBAAc,IAAI,IAAI,SAAS;AAE/B,UAAI,SAAS,UAAU,IAAI,OAAO;AAClC,UAAI,CAAC,QAAQ;AACZ,iBAAS,CAAC;AACV,kBAAU,IAAI,SAAS,MAAM;AAAA,MAC9B,WAAW,WAAW,UAAU,IAAI,OAAO,GAAG;AAC7C,iBAAS,OAAO,MAAM,CAAC;AACvB,kBAAU,IAAI,SAAS,MAAM;AAAA,MAC9B;AACA,aAAO;AAAA,IACR;AAEA,aAAS,WAAW,SAAoB;AACvC,qBAAe,QAAQ,MAAM,EAAE,KAAK,OAAO;AAC3C,qBAAe,QAAQ,IAAI,EAAE,KAAK,OAAO;AAAA,IAC1C;AAEA,eAAW,WAAW,MAAM;AAC3B,iBAAW,cAAc,gBAAgB,QAAQ,KAAK,GAAG;AACxD,mBAAW,UAAU;AAAA,MACtB;AAEA,iBAAW,CAAC,MAAM,IAAI,KAAK,gBAAgB,QAAQ,OAAO,GAAG;AAC5D,wBAAgB,IAAI;AACpB,mBAAW,IAAI;AAAA,MAChB;AAEA,iBAAW,QAAQ,gBAAgB,QAAQ,OAAO,GAAG;AACpD,wBAAgB,IAAI;AAAA,MACrB;AAAA,IACD;AAGA,WAAO,aAAa;AAAA,EACrB,CAAC;AACF;",
6
6
  "names": []
7
7
  }
@@ -11,7 +11,7 @@ function fromScratch(shapeIdsQuery, store) {
11
11
  });
12
12
  return result;
13
13
  }
14
- const parentsToChildren = (store) => {
14
+ function parentsToChildren(store) {
15
15
  const shapeIdsQuery = store.query.ids("shape");
16
16
  const shapeHistory = store.query.filterHistory("shape");
17
17
  return computed(
@@ -85,7 +85,7 @@ const parentsToChildren = (store) => {
85
85
  return newValue ?? lastValue;
86
86
  }
87
87
  );
88
- };
88
+ }
89
89
  export {
90
90
  parentsToChildren
91
91
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/editor/derivations/parentsToChildren.ts"],
4
- "sourcesContent": ["import { Computed, computed, isUninitialized, RESET_VALUE } from '@tldraw/state'\nimport { CollectionDiff, RecordsDiff } from '@tldraw/store'\nimport { isShape, TLParentId, TLRecord, TLShape, TLShapeId, TLStore } from '@tldraw/tlschema'\nimport { sortByIndex } from '@tldraw/utils'\n\ntype ParentShapeIdsToChildShapeIds = Record<TLParentId, TLShapeId[]>\n\nfunction fromScratch(\n\tshapeIdsQuery: Computed<Set<TLShapeId>, CollectionDiff<TLShapeId>>,\n\tstore: TLStore\n) {\n\tconst result: ParentShapeIdsToChildShapeIds = {}\n\tconst shapeIds = shapeIdsQuery.get()\n\tconst sortedShapes = Array.from(shapeIds, (id) => store.get(id)!).sort(sortByIndex)\n\n\t// Populate the result object with an array for each parent.\n\tsortedShapes.forEach((shape) => {\n\t\tresult[shape.parentId] ??= []\n\t\tresult[shape.parentId].push(shape.id)\n\t})\n\n\treturn result\n}\n\nexport const parentsToChildren = (store: TLStore) => {\n\tconst shapeIdsQuery = store.query.ids<'shape'>('shape')\n\tconst shapeHistory = store.query.filterHistory('shape')\n\n\treturn computed<ParentShapeIdsToChildShapeIds>(\n\t\t'parentsToChildrenWithIndexes',\n\t\t(lastValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(lastValue)) {\n\t\t\t\treturn fromScratch(shapeIdsQuery, store)\n\t\t\t}\n\n\t\t\tconst diff = shapeHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (diff === RESET_VALUE) {\n\t\t\t\treturn fromScratch(shapeIdsQuery, store)\n\t\t\t}\n\n\t\t\tif (diff.length === 0) return lastValue\n\n\t\t\tlet newValue: Record<TLParentId, TLShapeId[]> | null = null\n\n\t\t\tconst ensureNewArray = (parentId: TLParentId) => {\n\t\t\t\tif (!newValue) {\n\t\t\t\t\tnewValue = { ...lastValue }\n\t\t\t\t}\n\t\t\t\tif (!newValue[parentId]) {\n\t\t\t\t\tnewValue[parentId] = []\n\t\t\t\t} else if (newValue[parentId] === lastValue[parentId]) {\n\t\t\t\t\tnewValue[parentId] = [...newValue[parentId]!]\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst toSort = new Set<TLShapeId[]>()\n\n\t\t\tlet changes: RecordsDiff<TLRecord>\n\n\t\t\tfor (let i = 0, n = diff.length; i < n; i++) {\n\t\t\t\tchanges = diff[i]\n\n\t\t\t\t// Iterate through the added shapes, add them to the new value and mark them for sorting\n\t\t\t\tfor (const record of Object.values(changes.added)) {\n\t\t\t\t\tif (!isShape(record)) continue\n\t\t\t\t\tensureNewArray(record.parentId)\n\t\t\t\t\tnewValue![record.parentId].push(record.id)\n\t\t\t\t\ttoSort.add(newValue![record.parentId])\n\t\t\t\t}\n\n\t\t\t\t// Iterate through the updated shapes, add them to their parents in the new value and mark them for sorting\n\t\t\t\tfor (const [from, to] of Object.values(changes.updated)) {\n\t\t\t\t\tif (!isShape(to)) continue\n\t\t\t\t\tif (!isShape(from)) continue\n\n\t\t\t\t\tif (from.parentId !== to.parentId) {\n\t\t\t\t\t\t// If the parents have changed, remove the new value from the old parent and add it to the new parent\n\t\t\t\t\t\tensureNewArray(from.parentId)\n\t\t\t\t\t\tensureNewArray(to.parentId)\n\t\t\t\t\t\tnewValue![from.parentId].splice(newValue![from.parentId].indexOf(to.id), 1)\n\t\t\t\t\t\tnewValue![to.parentId].push(to.id)\n\t\t\t\t\t\ttoSort.add(newValue![to.parentId])\n\t\t\t\t\t} else if (from.index !== to.index) {\n\t\t\t\t\t\t// If the parent is the same but the index has changed (e.g. if they've been reordered), update the parent's array at the new index\n\t\t\t\t\t\tensureNewArray(to.parentId)\n\t\t\t\t\t\tconst idx = newValue![to.parentId].indexOf(to.id)\n\t\t\t\t\t\tnewValue![to.parentId][idx] = to.id\n\t\t\t\t\t\ttoSort.add(newValue![to.parentId])\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Iterate through the removed shapes, remove them from their parents in new value\n\t\t\t\tfor (const record of Object.values(changes.removed)) {\n\t\t\t\t\tif (!isShape(record)) continue\n\t\t\t\t\tensureNewArray(record.parentId)\n\t\t\t\t\tnewValue![record.parentId].splice(newValue![record.parentId].indexOf(record.id), 1)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Sort the arrays that have been marked for sorting (in-place to avoid intermediate arrays)\n\t\t\tfor (const arr of toSort) {\n\t\t\t\t// Filter out any deleted shapes in-place\n\t\t\t\tlet writeIdx = 0\n\t\t\t\tfor (let readIdx = 0; readIdx < arr.length; readIdx++) {\n\t\t\t\t\tif (store.get(arr[readIdx])) {\n\t\t\t\t\t\tarr[writeIdx++] = arr[readIdx]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tarr.length = writeIdx\n\n\t\t\t\t// Sort in-place by index\n\t\t\t\tarr.sort((a, b) => {\n\t\t\t\t\tconst shapeA = store.get(a) as TLShape\n\t\t\t\t\tconst shapeB = store.get(b) as TLShape\n\t\t\t\t\treturn sortByIndex(shapeA, shapeB)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn newValue ?? lastValue\n\t\t}\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Computed, computed, isUninitialized, RESET_VALUE } from '@tldraw/state'\nimport { CollectionDiff, RecordsDiff } from '@tldraw/store'\nimport { isShape, TLParentId, TLRecord, TLShape, TLShapeId, TLStore } from '@tldraw/tlschema'\nimport { sortByIndex } from '@tldraw/utils'\n\ntype ParentShapeIdsToChildShapeIds = Record<TLParentId, TLShapeId[]>\n\nfunction fromScratch(\n\tshapeIdsQuery: Computed<Set<TLShapeId>, CollectionDiff<TLShapeId>>,\n\tstore: TLStore\n) {\n\tconst result: ParentShapeIdsToChildShapeIds = {}\n\tconst shapeIds = shapeIdsQuery.get()\n\tconst sortedShapes = Array.from(shapeIds, (id) => store.get(id)!).sort(sortByIndex)\n\n\t// Populate the result object with an array for each parent.\n\tsortedShapes.forEach((shape) => {\n\t\tresult[shape.parentId] ??= []\n\t\tresult[shape.parentId].push(shape.id)\n\t})\n\n\treturn result\n}\n\nexport function parentsToChildren(store: TLStore) {\n\tconst shapeIdsQuery = store.query.ids<'shape'>('shape')\n\tconst shapeHistory = store.query.filterHistory('shape')\n\n\treturn computed<ParentShapeIdsToChildShapeIds>(\n\t\t'parentsToChildrenWithIndexes',\n\t\t(lastValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(lastValue)) {\n\t\t\t\treturn fromScratch(shapeIdsQuery, store)\n\t\t\t}\n\n\t\t\tconst diff = shapeHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (diff === RESET_VALUE) {\n\t\t\t\treturn fromScratch(shapeIdsQuery, store)\n\t\t\t}\n\n\t\t\tif (diff.length === 0) return lastValue\n\n\t\t\tlet newValue: Record<TLParentId, TLShapeId[]> | null = null\n\n\t\t\tconst ensureNewArray = (parentId: TLParentId) => {\n\t\t\t\tif (!newValue) {\n\t\t\t\t\tnewValue = { ...lastValue }\n\t\t\t\t}\n\t\t\t\tif (!newValue[parentId]) {\n\t\t\t\t\tnewValue[parentId] = []\n\t\t\t\t} else if (newValue[parentId] === lastValue[parentId]) {\n\t\t\t\t\tnewValue[parentId] = [...newValue[parentId]!]\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst toSort = new Set<TLShapeId[]>()\n\n\t\t\tlet changes: RecordsDiff<TLRecord>\n\n\t\t\tfor (let i = 0, n = diff.length; i < n; i++) {\n\t\t\t\tchanges = diff[i]\n\n\t\t\t\t// Iterate through the added shapes, add them to the new value and mark them for sorting\n\t\t\t\tfor (const record of Object.values(changes.added)) {\n\t\t\t\t\tif (!isShape(record)) continue\n\t\t\t\t\tensureNewArray(record.parentId)\n\t\t\t\t\tnewValue![record.parentId].push(record.id)\n\t\t\t\t\ttoSort.add(newValue![record.parentId])\n\t\t\t\t}\n\n\t\t\t\t// Iterate through the updated shapes, add them to their parents in the new value and mark them for sorting\n\t\t\t\tfor (const [from, to] of Object.values(changes.updated)) {\n\t\t\t\t\tif (!isShape(to)) continue\n\t\t\t\t\tif (!isShape(from)) continue\n\n\t\t\t\t\tif (from.parentId !== to.parentId) {\n\t\t\t\t\t\t// If the parents have changed, remove the new value from the old parent and add it to the new parent\n\t\t\t\t\t\tensureNewArray(from.parentId)\n\t\t\t\t\t\tensureNewArray(to.parentId)\n\t\t\t\t\t\tnewValue![from.parentId].splice(newValue![from.parentId].indexOf(to.id), 1)\n\t\t\t\t\t\tnewValue![to.parentId].push(to.id)\n\t\t\t\t\t\ttoSort.add(newValue![to.parentId])\n\t\t\t\t\t} else if (from.index !== to.index) {\n\t\t\t\t\t\t// If the parent is the same but the index has changed (e.g. if they've been reordered), update the parent's array at the new index\n\t\t\t\t\t\tensureNewArray(to.parentId)\n\t\t\t\t\t\tconst idx = newValue![to.parentId].indexOf(to.id)\n\t\t\t\t\t\tnewValue![to.parentId][idx] = to.id\n\t\t\t\t\t\ttoSort.add(newValue![to.parentId])\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Iterate through the removed shapes, remove them from their parents in new value\n\t\t\t\tfor (const record of Object.values(changes.removed)) {\n\t\t\t\t\tif (!isShape(record)) continue\n\t\t\t\t\tensureNewArray(record.parentId)\n\t\t\t\t\tnewValue![record.parentId].splice(newValue![record.parentId].indexOf(record.id), 1)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Sort the arrays that have been marked for sorting (in-place to avoid intermediate arrays)\n\t\t\tfor (const arr of toSort) {\n\t\t\t\t// Filter out any deleted shapes in-place\n\t\t\t\tlet writeIdx = 0\n\t\t\t\tfor (let readIdx = 0; readIdx < arr.length; readIdx++) {\n\t\t\t\t\tif (store.get(arr[readIdx])) {\n\t\t\t\t\t\tarr[writeIdx++] = arr[readIdx]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tarr.length = writeIdx\n\n\t\t\t\t// Sort in-place by index\n\t\t\t\tarr.sort((a, b) => {\n\t\t\t\t\tconst shapeA = store.get(a) as TLShape\n\t\t\t\t\tconst shapeB = store.get(b) as TLShape\n\t\t\t\t\treturn sortByIndex(shapeA, shapeB)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn newValue ?? lastValue\n\t\t}\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,7 +13,7 @@ const isShapeInPage = (store, pageId, shape) => {
13
13
  }
14
14
  return shape.parentId === pageId;
15
15
  };
16
- const deriveShapeIdsInCurrentPage = (store, getCurrentPageId) => {
16
+ function deriveShapeIdsInCurrentPage(store, getCurrentPageId) {
17
17
  const shapesIndex = store.query.ids("shape");
18
18
  let lastPageId = null;
19
19
  function fromScratch() {
@@ -65,7 +65,7 @@ const deriveShapeIdsInCurrentPage = (store, getCurrentPageId) => {
65
65
  }
66
66
  return withDiff(result.value, result.diff);
67
67
  });
68
- };
68
+ }
69
69
  export {
70
70
  deriveShapeIdsInCurrentPage
71
71
  };
@@ -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 const 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\treturn new Set(\n\t\t\t[...shapesIndex.get()].filter((id) => isShapeInPage(store, currentPageId, store.get(id)!))\n\t\t)\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,MAAM,8BAA8B,CAAC,OAAgB,qBAAqC;AAChG,QAAM,cAAc,MAAM,MAAM,IAAI,OAAO;AAC3C,MAAI,aAA8B;AAClC,WAAS,cAAc;AACtB,UAAM,gBAAgB,iBAAiB;AACvC,iBAAa;AACb,WAAO,IAAI;AAAA,MACV,CAAC,GAAG,YAAY,IAAI,CAAC,EAAE,OAAO,CAAC,OAAO,cAAc,OAAO,eAAe,MAAM,IAAI,EAAE,CAAE,CAAC;AAAA,IAC1F;AAAA,EACD;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;",
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\treturn new Set(\n\t\t\t[...shapesIndex.get()].filter((id) => isShapeInPage(store, currentPageId, store.get(id)!))\n\t\t)\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,WAAO,IAAI;AAAA,MACV,CAAC,GAAG,YAAY,IAAI,CAAC,EAAE,OAAO,CAAC,OAAO,cAAc,OAAO,eAAe,MAAM,IAAI,EAAE,CAAE,CAAC;AAAA,IAC1F;AAAA,EACD;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
  }
@@ -20,6 +20,7 @@ class ClickManager {
20
20
  _clickTimeout;
21
21
  _clickScreenPoint;
22
22
  _previousScreenPoint;
23
+ _isPressingWhilePending = false;
23
24
  _getClickTimeout(state, id = uniqueId()) {
24
25
  this._clickId = id;
25
26
  clearTimeout(this._clickTimeout);
@@ -27,30 +28,12 @@ class ClickManager {
27
28
  () => {
28
29
  if (this._clickState === state && this._clickId === id) {
29
30
  switch (this._clickState) {
30
- case "pendingTriple": {
31
- this.editor.dispatch({
32
- ...this.lastPointerInfo,
33
- type: "click",
34
- name: "double_click",
35
- phase: "settle"
36
- });
37
- break;
38
- }
39
- case "pendingQuadruple": {
40
- this.editor.dispatch({
41
- ...this.lastPointerInfo,
42
- type: "click",
43
- name: "triple_click",
44
- phase: "settle"
45
- });
46
- break;
47
- }
48
31
  case "pendingOverflow": {
49
32
  this.editor.dispatch({
50
33
  ...this.lastPointerInfo,
51
34
  type: "click",
52
- name: "quadruple_click",
53
- phase: "settle"
35
+ name: "double_click",
36
+ phase: this._isPressingWhilePending ? "settle-down" : "settle-up"
54
37
  });
55
38
  break;
56
39
  }
@@ -84,6 +67,7 @@ class ClickManager {
84
67
  case "pointer_down": {
85
68
  if (!this._clickState) return info;
86
69
  this._clickScreenPoint = Vec.From(info.point);
70
+ this._isPressingWhilePending = true;
87
71
  if (this._previousScreenPoint && Vec.Dist2(this._previousScreenPoint, this._clickScreenPoint) > MAX_CLICK_DISTANCE ** 2) {
88
72
  this._clickState = "idle";
89
73
  }
@@ -91,32 +75,12 @@ class ClickManager {
91
75
  this.lastPointerInfo = info;
92
76
  switch (this._clickState) {
93
77
  case "pendingDouble": {
94
- this._clickState = "pendingTriple";
95
- this._clickTimeout = this._getClickTimeout(this._clickState);
96
- return {
97
- ...info,
98
- type: "click",
99
- name: "double_click",
100
- phase: "down"
101
- };
102
- }
103
- case "pendingTriple": {
104
- this._clickState = "pendingQuadruple";
105
- this._clickTimeout = this._getClickTimeout(this._clickState);
106
- return {
107
- ...info,
108
- type: "click",
109
- name: "triple_click",
110
- phase: "down"
111
- };
112
- }
113
- case "pendingQuadruple": {
114
78
  this._clickState = "pendingOverflow";
115
79
  this._clickTimeout = this._getClickTimeout(this._clickState);
116
80
  return {
117
81
  ...info,
118
82
  type: "click",
119
- name: "quadruple_click",
83
+ name: "double_click",
120
84
  phase: "down"
121
85
  };
122
86
  }
@@ -137,28 +101,13 @@ class ClickManager {
137
101
  case "pointer_up": {
138
102
  if (!this._clickState) return info;
139
103
  this._clickScreenPoint = Vec.From(info.point);
104
+ this._isPressingWhilePending = false;
140
105
  switch (this._clickState) {
141
- case "pendingTriple": {
142
- return {
143
- ...this.lastPointerInfo,
144
- type: "click",
145
- name: "double_click",
146
- phase: "up"
147
- };
148
- }
149
- case "pendingQuadruple": {
150
- return {
151
- ...this.lastPointerInfo,
152
- type: "click",
153
- name: "triple_click",
154
- phase: "up"
155
- };
156
- }
157
106
  case "pendingOverflow": {
158
107
  return {
159
108
  ...this.lastPointerInfo,
160
109
  type: "click",
161
- name: "quadruple_click",
110
+ name: "double_click",
162
111
  phase: "up"
163
112
  };
164
113
  }
@@ -179,6 +128,7 @@ class ClickManager {
179
128
  cancelDoubleClickTimeout() {
180
129
  this._clickTimeout = clearTimeout(this._clickTimeout);
181
130
  this._clickState = "idle";
131
+ this._isPressingWhilePending = false;
182
132
  }
183
133
  }
184
134
  __decorateClass([
@@ -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 =\n\t| 'idle'\n\t| 'pendingDouble'\n\t| 'pendingTriple'\n\t| 'pendingQuadruple'\n\t| 'pendingOverflow'\n\t| '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\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 'pendingTriple': {\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: 'settle',\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\tcase 'pendingQuadruple': {\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: 'triple_click',\n\t\t\t\t\t\t\t\tphase: 'settle',\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\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: 'quadruple_click',\n\t\t\t\t\t\t\t\tphase: 'settle',\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\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 = 'pendingTriple'\n\t\t\t\t\t\tthis._clickTimeout = this._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 'pendingTriple': {\n\t\t\t\t\t\tthis._clickState = 'pendingQuadruple'\n\t\t\t\t\t\tthis._clickTimeout = this._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: 'triple_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 'pendingQuadruple': {\n\t\t\t\t\t\tthis._clickState = 'pendingOverflow'\n\t\t\t\t\t\tthis._clickTimeout = this._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: 'quadruple_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._clickTimeout = this._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\t\t\t\tthis._clickScreenPoint = Vec.From(info.point)\n\n\t\t\t\tswitch (this._clickState) {\n\t\t\t\t\tcase 'pendingTriple': {\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\tcase 'pendingQuadruple': {\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: 'triple_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\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: 'quadruple_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\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, this.editor.inputs.getCurrentScreenPoint()) >\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}\n}\n"],
5
- "mappings": "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",
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._clickTimeout = this._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._clickTimeout = this._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\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, this.editor.inputs.getCurrentScreenPoint()) >\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
  }
@@ -30,7 +30,7 @@ class CollaboratorsManager {
30
30
  _visibilityClock = atom("collaboratorVisibilityClock", Date.now());
31
31
  _getCollaboratorsQuery() {
32
32
  return this.editor.store.query.records("instance_presence", () => ({
33
- userId: { neq: this.editor.user.getId() }
33
+ userId: { neq: this.editor.user.getRecordId() }
34
34
  }));
35
35
  }
36
36
  getCollaborators() {
@@ -58,10 +58,10 @@ class CollaboratorsManager {
58
58
  const collaborators = this.getCollaborators();
59
59
  if (!collaborators.length) return EMPTY_ARRAY;
60
60
  const { followingUserId, highlightedUserIds } = this.editor.getInstanceState();
61
- const currentUserId = this.editor.user.getId();
61
+ const currentUserId = this.editor.user.getRecordId();
62
62
  return collaborators.filter((presence) => {
63
63
  const { lastActivityTimestamp, userId, chatMessage } = presence;
64
- const elapsed = Math.max(0, now - (lastActivityTimestamp ?? now));
64
+ const elapsed = lastActivityTimestamp ? Math.max(0, now - lastActivityTimestamp) : 0;
65
65
  if (elapsed > collaboratorInactiveTimeoutMs) {
66
66
  return followingUserId === userId || highlightedUserIds.includes(userId);
67
67
  }
@@ -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 { maxBy } 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.getId() },\n\t\t}))\n\t}\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\n\tgetCollaborators(): TLInstancePresence[] {\n\t\tconst allPresenceRecords = this._getCollaboratorsQuery().get()\n\t\tif (!allPresenceRecords.length) return EMPTY_ARRAY\n\t\tconst userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()\n\t\treturn userIds.map((id) => {\n\t\t\tconst latestPresence = maxBy(\n\t\t\t\tallPresenceRecords.filter((c) => c.userId === id),\n\t\t\t\t(p) => p.lastActivityTimestamp ?? 0\n\t\t\t)\n\t\t\treturn latestPresence!\n\t\t})\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\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\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.getId()\n\n\t\treturn collaborators.filter((presence) => {\n\t\t\tconst { lastActivityTimestamp, userId, chatMessage } = presence\n\n\t\t\t// Treat a missing `lastActivityTimestamp` as \"active right now\" (elapsed = 0)\n\t\t\t// so newly-joined peers aren't immediately classified as idle/inactive.\n\t\t\tconst elapsed = Math.max(0, now - (lastActivityTimestamp ?? now))\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\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": ";;;;;;;;;;AAAA,SAAS,aAAa,MAAM,gBAAgB;AAE5C,SAAS,aAAa;AAYf,MAAM,qBAAqB;AAAA,EACjC,YAA6B,QAAgB;AAAhB;AAAA,EAAiB;AAAA,EAAjB;AAAA,EAErB,0BAA0B;AAAA,EAE1B,wBAAwB;AAC/B,QAAI,KAAK,wBAAyB;AAClC,SAAK,0BAA0B;AAI/B,SAAK,OAAO,OAAO,YAAY,MAAM;AACpC,WAAK,iBAAiB,IAAI,KAAK,IAAI,CAAC;AAAA,IACrC,GAAG,KAAK,OAAO,QAAQ,2BAA2B;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMiB,mBAAmB,KAAK,+BAA+B,KAAK,IAAI,CAAC;AAAA,EAG1E,yBAAyB;AAChC,WAAO,KAAK,OAAO,MAAM,MAAM,QAAQ,qBAAqB,OAAO;AAAA,MAClE,QAAQ,EAAE,KAAK,KAAK,OAAO,KAAK,MAAM,EAAE;AAAA,IACzC,EAAE;AAAA,EACH;AAAA,EAOA,mBAAyC;AACxC,UAAM,qBAAqB,KAAK,uBAAuB,EAAE,IAAI;AAC7D,QAAI,CAAC,mBAAmB,OAAQ,QAAO;AACvC,UAAM,UAAU,CAAC,GAAG,IAAI,IAAI,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK;AAC3E,WAAO,QAAQ,IAAI,CAAC,OAAO;AAC1B,YAAM,iBAAiB;AAAA,QACtB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE;AAAA,QAChD,CAAC,MAAM,EAAE,yBAAyB;AAAA,MACnC;AACA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAOA,gCAAsD;AACrD,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AACnD,WAAO,KAAK,iBAAiB,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,aAAa;AAAA,EAC/E;AAAA,EAUA,0BAAgD;AAC/C,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,EAAE,+BAA+B,0BAA0B,IAAI,OAAO;AAE5E,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB,IAAI;AAC1B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAgB,KAAK,iBAAiB;AAC5C,QAAI,CAAC,cAAc,OAAQ,QAAO;AAElC,UAAM,EAAE,iBAAiB,mBAAmB,IAAI,KAAK,OAAO,iBAAiB;AAC7E,UAAM,gBAAgB,KAAK,OAAO,KAAK,MAAM;AAE7C,WAAO,cAAc,OAAO,CAAC,aAAa;AACzC,YAAM,EAAE,uBAAuB,QAAQ,YAAY,IAAI;AAIvD,YAAM,UAAU,KAAK,IAAI,GAAG,OAAO,yBAAyB,IAAI;AAEhE,UAAI,UAAU,+BAA+B;AAE5C,eAAO,oBAAoB,UAAU,mBAAmB,SAAS,MAAM;AAAA,MACxE;AAEA,UAAI,UAAU,2BAA2B;AAExC,YAAI,SAAS,oBAAoB,eAAe;AAC/C,iBAAO,CAAC,EAAE,eAAe,mBAAmB,SAAS,MAAM;AAAA,QAC5D;AAAA,MACD;AAGA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAOA,uCAA6D;AAC5D,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AACnD,WAAO,KAAK,wBAAwB,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,aAAa;AAAA,EACtF;AACD;AAxFS;AAAA,EADP;AAAA,GAtBW,qBAuBJ;AAWR;AAAA,EADC;AAAA,GAjCW,qBAkCZ;AAkBA;AAAA,EADC;AAAA,GAnDW,qBAoDZ;AAaA;AAAA,EADC;AAAA,GAhEW,qBAiEZ;AA0CA;AAAA,EADC;AAAA,GA1GW,qBA2GZ;",
4
+ "sourcesContent": ["import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'\nimport type { TLInstancePresence } from '@tldraw/tlschema'\nimport { maxBy } 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/**\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\n\tgetCollaborators(): TLInstancePresence[] {\n\t\tconst allPresenceRecords = this._getCollaboratorsQuery().get()\n\t\tif (!allPresenceRecords.length) return EMPTY_ARRAY\n\t\tconst userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()\n\t\treturn userIds.map((id) => {\n\t\t\tconst latestPresence = maxBy(\n\t\t\t\tallPresenceRecords.filter((c) => c.userId === id),\n\t\t\t\t(p) => p.lastActivityTimestamp ?? 0\n\t\t\t)\n\t\t\treturn latestPresence!\n\t\t})\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\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\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\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": ";;;;;;;;;;AAAA,SAAS,aAAa,MAAM,gBAAgB;AAE5C,SAAS,aAAa;AAYf,MAAM,qBAAqB;AAAA,EACjC,YAA6B,QAAgB;AAAhB;AAAA,EAAiB;AAAA,EAAjB;AAAA,EAErB,0BAA0B;AAAA,EAE1B,wBAAwB;AAC/B,QAAI,KAAK,wBAAyB;AAClC,SAAK,0BAA0B;AAI/B,SAAK,OAAO,OAAO,YAAY,MAAM;AACpC,WAAK,iBAAiB,IAAI,KAAK,IAAI,CAAC;AAAA,IACrC,GAAG,KAAK,OAAO,QAAQ,2BAA2B;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMiB,mBAAmB,KAAK,+BAA+B,KAAK,IAAI,CAAC;AAAA,EAG1E,yBAAyB;AAChC,WAAO,KAAK,OAAO,MAAM,MAAM,QAAQ,qBAAqB,OAAO;AAAA,MAClE,QAAQ,EAAE,KAAK,KAAK,OAAO,KAAK,YAAY,EAAE;AAAA,IAC/C,EAAE;AAAA,EACH;AAAA,EAOA,mBAAyC;AACxC,UAAM,qBAAqB,KAAK,uBAAuB,EAAE,IAAI;AAC7D,QAAI,CAAC,mBAAmB,OAAQ,QAAO;AACvC,UAAM,UAAU,CAAC,GAAG,IAAI,IAAI,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK;AAC3E,WAAO,QAAQ,IAAI,CAAC,OAAO;AAC1B,YAAM,iBAAiB;AAAA,QACtB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE;AAAA,QAChD,CAAC,MAAM,EAAE,yBAAyB;AAAA,MACnC;AACA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAOA,gCAAsD;AACrD,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AACnD,WAAO,KAAK,iBAAiB,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,aAAa;AAAA,EAC/E;AAAA,EAUA,0BAAgD;AAC/C,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,EAAE,+BAA+B,0BAA0B,IAAI,OAAO;AAE5E,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB,IAAI;AAC1B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAgB,KAAK,iBAAiB;AAC5C,QAAI,CAAC,cAAc,OAAQ,QAAO;AAElC,UAAM,EAAE,iBAAiB,mBAAmB,IAAI,KAAK,OAAO,iBAAiB;AAC7E,UAAM,gBAAgB,KAAK,OAAO,KAAK,YAAY;AAEnD,WAAO,cAAc,OAAO,CAAC,aAAa;AACzC,YAAM,EAAE,uBAAuB,QAAQ,YAAY,IAAI;AAOvD,YAAM,UAAU,wBAAwB,KAAK,IAAI,GAAG,MAAM,qBAAqB,IAAI;AAEnF,UAAI,UAAU,+BAA+B;AAE5C,eAAO,oBAAoB,UAAU,mBAAmB,SAAS,MAAM;AAAA,MACxE;AAEA,UAAI,UAAU,2BAA2B;AAExC,YAAI,SAAS,oBAAoB,eAAe;AAC/C,iBAAO,CAAC,EAAE,eAAe,mBAAmB,SAAS,MAAM;AAAA,QAC5D;AAAA,MACD;AAGA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAOA,uCAA6D;AAC5D,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AACnD,WAAO,KAAK,wBAAwB,EAAE,OAAO,CAAC,MAAM,EAAE,kBAAkB,aAAa;AAAA,EACtF;AACD;AA3FS;AAAA,EADP;AAAA,GAtBW,qBAuBJ;AAWR;AAAA,EADC;AAAA,GAjCW,qBAkCZ;AAkBA;AAAA,EADC;AAAA,GAnDW,qBAoDZ;AAaA;AAAA,EADC;AAAA,GAhEW,qBAiEZ;AA6CA;AAAA,EADC;AAAA,GA7GW,qBA8GZ;",
6
6
  "names": []
7
7
  }
@@ -53,8 +53,7 @@ class FocusManager {
53
53
  handleKeyDown(keyEvent) {
54
54
  const container = this.editor.getContainer();
55
55
  const activeEl = container.ownerDocument.activeElement;
56
- if (this.editor.isIn("select.editing_shape") && !activeEl?.closest(".tlui-contextual-toolbar"))
57
- return;
56
+ if (this.editor.getEditingShapeId() && !activeEl?.closest(".tlui-contextual-toolbar")) return;
58
57
  if (activeEl === container && this.editor.getSelectedShapeIds().length > 0) return;
59
58
  if (["Tab", "ArrowUp", "ArrowDown"].includes(keyEvent.key)) {
60
59
  container.classList.remove("tl-container__no-focus-ring");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/FocusManager/FocusManager.ts"],
4
- "sourcesContent": ["import { bind } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * A manager for ensuring correct focus across the editor.\n * It will listen for changes in the instance state to make sure the\n * container is focused when the editor is focused.\n * Also, it will make sure that the focus is on things like text\n * labels when the editor is in editing mode.\n *\n * @internal\n */\nexport class FocusManager {\n\tprivate disposeSideEffectListener?: () => void\n\n\tconstructor(\n\t\tpublic editor: Editor,\n\t\tautoFocus?: boolean\n\t) {\n\t\tthis.disposeSideEffectListener = editor.sideEffects.registerAfterChangeHandler(\n\t\t\t'instance',\n\t\t\t(prev, next) => {\n\t\t\t\tif (prev.isFocused !== next.isFocused) {\n\t\t\t\t\tthis.updateContainerClass()\n\t\t\t\t}\n\t\t\t}\n\t\t)\n\n\t\tconst currentFocusState = editor.getInstanceState().isFocused\n\t\tif (autoFocus !== currentFocusState) {\n\t\t\teditor.updateInstanceState({ isFocused: !!autoFocus })\n\t\t}\n\t\tthis.updateContainerClass()\n\n\t\tconst body = editor.getContainerDocument().body\n\t\tbody.addEventListener('keydown', this.handleKeyDown)\n\t\tbody.addEventListener('mousedown', this.handleMouseDown)\n\t}\n\n\t/**\n\t * The editor's focus state and the container's focus state\n\t * are not necessarily always in sync. For that reason we\n\t * can't rely on the css `:focus` or `:focus-within` selectors to style the\n\t * editor when it is in focus.\n\t *\n\t * For that reason we synchronize the editor's focus state with a\n\t * special class on the container: tl-container__focused\n\t */\n\tprivate updateContainerClass() {\n\t\tconst container = this.editor.getContainer()\n\t\tconst instanceState = this.editor.getInstanceState()\n\n\t\tif (instanceState.isFocused) {\n\t\t\tcontainer.classList.add('tl-container__focused')\n\t\t} else {\n\t\t\tcontainer.classList.remove('tl-container__focused')\n\t\t}\n\t\tcontainer.classList.add('tl-container__no-focus-ring')\n\t}\n\n\t@bind private handleKeyDown(keyEvent: KeyboardEvent) {\n\t\tconst container = this.editor.getContainer()\n\t\tconst activeEl = container.ownerDocument.activeElement\n\t\t// Edit mode should remove the focus ring, however if the active element's\n\t\t// parent is the contextual toolbar, then allow it.\n\t\tif (this.editor.isIn('select.editing_shape') && !activeEl?.closest('.tlui-contextual-toolbar'))\n\t\t\treturn\n\t\tif (activeEl === container && this.editor.getSelectedShapeIds().length > 0) return\n\t\tif (['Tab', 'ArrowUp', 'ArrowDown'].includes(keyEvent.key)) {\n\t\t\tcontainer.classList.remove('tl-container__no-focus-ring')\n\t\t}\n\t}\n\n\t@bind private handleMouseDown() {\n\t\tconst container = this.editor.getContainer()\n\t\tcontainer.classList.add('tl-container__no-focus-ring')\n\t}\n\n\tfocus() {\n\t\tthis.editor.getContainer().focus()\n\t}\n\n\tblur({ blurContainer = true } = {}) {\n\t\tthis.editor.complete() // stop any interaction\n\t\tif (blurContainer) this.editor.getContainer().blur() // blur the container\n\t}\n\n\tdispose() {\n\t\tconst body = this.editor.getContainerDocument().body\n\t\tbody.removeEventListener('keydown', this.handleKeyDown)\n\t\tbody.removeEventListener('mousedown', this.handleMouseDown)\n\t\tthis.disposeSideEffectListener?.()\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;AAAA,SAAS,YAAY;AAYd,MAAM,aAAa;AAAA,EAGzB,YACQ,QACP,WACC;AAFM;AAGP,SAAK,4BAA4B,OAAO,YAAY;AAAA,MACnD;AAAA,MACA,CAAC,MAAM,SAAS;AACf,YAAI,KAAK,cAAc,KAAK,WAAW;AACtC,eAAK,qBAAqB;AAAA,QAC3B;AAAA,MACD;AAAA,IACD;AAEA,UAAM,oBAAoB,OAAO,iBAAiB,EAAE;AACpD,QAAI,cAAc,mBAAmB;AACpC,aAAO,oBAAoB,EAAE,WAAW,CAAC,CAAC,UAAU,CAAC;AAAA,IACtD;AACA,SAAK,qBAAqB;AAE1B,UAAM,OAAO,OAAO,qBAAqB,EAAE;AAC3C,SAAK,iBAAiB,WAAW,KAAK,aAAa;AACnD,SAAK,iBAAiB,aAAa,KAAK,eAAe;AAAA,EACxD;AAAA,EArBQ;AAAA,EAHA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCA,uBAAuB;AAC9B,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AAEnD,QAAI,cAAc,WAAW;AAC5B,gBAAU,UAAU,IAAI,uBAAuB;AAAA,IAChD,OAAO;AACN,gBAAU,UAAU,OAAO,uBAAuB;AAAA,IACnD;AACA,cAAU,UAAU,IAAI,6BAA6B;AAAA,EACtD;AAAA,EAEc,cAAc,UAAyB;AACpD,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,WAAW,UAAU,cAAc;AAGzC,QAAI,KAAK,OAAO,KAAK,sBAAsB,KAAK,CAAC,UAAU,QAAQ,0BAA0B;AAC5F;AACD,QAAI,aAAa,aAAa,KAAK,OAAO,oBAAoB,EAAE,SAAS,EAAG;AAC5E,QAAI,CAAC,OAAO,WAAW,WAAW,EAAE,SAAS,SAAS,GAAG,GAAG;AAC3D,gBAAU,UAAU,OAAO,6BAA6B;AAAA,IACzD;AAAA,EACD;AAAA,EAEc,kBAAkB;AAC/B,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,cAAU,UAAU,IAAI,6BAA6B;AAAA,EACtD;AAAA,EAEA,QAAQ;AACP,SAAK,OAAO,aAAa,EAAE,MAAM;AAAA,EAClC;AAAA,EAEA,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG;AACnC,SAAK,OAAO,SAAS;AACrB,QAAI,cAAe,MAAK,OAAO,aAAa,EAAE,KAAK;AAAA,EACpD;AAAA,EAEA,UAAU;AACT,UAAM,OAAO,KAAK,OAAO,qBAAqB,EAAE;AAChD,SAAK,oBAAoB,WAAW,KAAK,aAAa;AACtD,SAAK,oBAAoB,aAAa,KAAK,eAAe;AAC1D,SAAK,4BAA4B;AAAA,EAClC;AACD;AAjCe;AAAA,EAAb;AAAA,GAhDW,aAgDE;AAaA;AAAA,EAAb;AAAA,GA7DW,aA6DE;",
4
+ "sourcesContent": ["import { bind } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * A manager for ensuring correct focus across the editor.\n * It will listen for changes in the instance state to make sure the\n * container is focused when the editor is focused.\n * Also, it will make sure that the focus is on things like text\n * labels when the editor is in editing mode.\n *\n * @internal\n */\nexport class FocusManager {\n\tprivate disposeSideEffectListener?: () => void\n\n\tconstructor(\n\t\tpublic editor: Editor,\n\t\tautoFocus?: boolean\n\t) {\n\t\tthis.disposeSideEffectListener = editor.sideEffects.registerAfterChangeHandler(\n\t\t\t'instance',\n\t\t\t(prev, next) => {\n\t\t\t\tif (prev.isFocused !== next.isFocused) {\n\t\t\t\t\tthis.updateContainerClass()\n\t\t\t\t}\n\t\t\t}\n\t\t)\n\n\t\tconst currentFocusState = editor.getInstanceState().isFocused\n\t\tif (autoFocus !== currentFocusState) {\n\t\t\teditor.updateInstanceState({ isFocused: !!autoFocus })\n\t\t}\n\t\tthis.updateContainerClass()\n\n\t\tconst body = editor.getContainerDocument().body\n\t\tbody.addEventListener('keydown', this.handleKeyDown)\n\t\tbody.addEventListener('mousedown', this.handleMouseDown)\n\t}\n\n\t/**\n\t * The editor's focus state and the container's focus state\n\t * are not necessarily always in sync. For that reason we\n\t * can't rely on the css `:focus` or `:focus-within` selectors to style the\n\t * editor when it is in focus.\n\t *\n\t * For that reason we synchronize the editor's focus state with a\n\t * special class on the container: tl-container__focused\n\t */\n\tprivate updateContainerClass() {\n\t\tconst container = this.editor.getContainer()\n\t\tconst instanceState = this.editor.getInstanceState()\n\n\t\tif (instanceState.isFocused) {\n\t\t\tcontainer.classList.add('tl-container__focused')\n\t\t} else {\n\t\t\tcontainer.classList.remove('tl-container__focused')\n\t\t}\n\t\tcontainer.classList.add('tl-container__no-focus-ring')\n\t}\n\n\t@bind private handleKeyDown(keyEvent: KeyboardEvent) {\n\t\tconst container = this.editor.getContainer()\n\t\tconst activeEl = container.ownerDocument.activeElement\n\t\t// Edit mode should remove the focus ring, however if the active element's\n\t\t// parent is the contextual toolbar, then allow it.\n\t\tif (this.editor.getEditingShapeId() && !activeEl?.closest('.tlui-contextual-toolbar')) return\n\t\tif (activeEl === container && this.editor.getSelectedShapeIds().length > 0) return\n\t\tif (['Tab', 'ArrowUp', 'ArrowDown'].includes(keyEvent.key)) {\n\t\t\tcontainer.classList.remove('tl-container__no-focus-ring')\n\t\t}\n\t}\n\n\t@bind private handleMouseDown() {\n\t\tconst container = this.editor.getContainer()\n\t\tcontainer.classList.add('tl-container__no-focus-ring')\n\t}\n\n\tfocus() {\n\t\tthis.editor.getContainer().focus()\n\t}\n\n\tblur({ blurContainer = true } = {}) {\n\t\tthis.editor.complete() // stop any interaction\n\t\tif (blurContainer) this.editor.getContainer().blur() // blur the container\n\t}\n\n\tdispose() {\n\t\tconst body = this.editor.getContainerDocument().body\n\t\tbody.removeEventListener('keydown', this.handleKeyDown)\n\t\tbody.removeEventListener('mousedown', this.handleMouseDown)\n\t\tthis.disposeSideEffectListener?.()\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;AAAA,SAAS,YAAY;AAYd,MAAM,aAAa;AAAA,EAGzB,YACQ,QACP,WACC;AAFM;AAGP,SAAK,4BAA4B,OAAO,YAAY;AAAA,MACnD;AAAA,MACA,CAAC,MAAM,SAAS;AACf,YAAI,KAAK,cAAc,KAAK,WAAW;AACtC,eAAK,qBAAqB;AAAA,QAC3B;AAAA,MACD;AAAA,IACD;AAEA,UAAM,oBAAoB,OAAO,iBAAiB,EAAE;AACpD,QAAI,cAAc,mBAAmB;AACpC,aAAO,oBAAoB,EAAE,WAAW,CAAC,CAAC,UAAU,CAAC;AAAA,IACtD;AACA,SAAK,qBAAqB;AAE1B,UAAM,OAAO,OAAO,qBAAqB,EAAE;AAC3C,SAAK,iBAAiB,WAAW,KAAK,aAAa;AACnD,SAAK,iBAAiB,aAAa,KAAK,eAAe;AAAA,EACxD;AAAA,EArBQ;AAAA,EAHA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCA,uBAAuB;AAC9B,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,gBAAgB,KAAK,OAAO,iBAAiB;AAEnD,QAAI,cAAc,WAAW;AAC5B,gBAAU,UAAU,IAAI,uBAAuB;AAAA,IAChD,OAAO;AACN,gBAAU,UAAU,OAAO,uBAAuB;AAAA,IACnD;AACA,cAAU,UAAU,IAAI,6BAA6B;AAAA,EACtD;AAAA,EAEc,cAAc,UAAyB;AACpD,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,WAAW,UAAU,cAAc;AAGzC,QAAI,KAAK,OAAO,kBAAkB,KAAK,CAAC,UAAU,QAAQ,0BAA0B,EAAG;AACvF,QAAI,aAAa,aAAa,KAAK,OAAO,oBAAoB,EAAE,SAAS,EAAG;AAC5E,QAAI,CAAC,OAAO,WAAW,WAAW,EAAE,SAAS,SAAS,GAAG,GAAG;AAC3D,gBAAU,UAAU,OAAO,6BAA6B;AAAA,IACzD;AAAA,EACD;AAAA,EAEc,kBAAkB;AAC/B,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,cAAU,UAAU,IAAI,6BAA6B;AAAA,EACtD;AAAA,EAEA,QAAQ;AACP,SAAK,OAAO,aAAa,EAAE,MAAM;AAAA,EAClC;AAAA,EAEA,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG;AACnC,SAAK,OAAO,SAAS;AACrB,QAAI,cAAe,MAAK,OAAO,aAAa,EAAE,KAAK;AAAA,EACpD;AAAA,EAEA,UAAU;AACT,UAAM,OAAO,KAAK,OAAO,qBAAqB,EAAE;AAChD,SAAK,oBAAoB,WAAW,KAAK,aAAa;AACtD,SAAK,oBAAoB,aAAa,KAAK,eAAe;AAC1D,SAAK,4BAA4B;AAAA,EAClC;AACD;AAhCe;AAAA,EAAb;AAAA,GAhDW,aAgDE;AAYA;AAAA,EAAb;AAAA,GA5DW,aA4DE;",
6
6
  "names": []
7
7
  }
@@ -35,7 +35,9 @@ class HistoryManager {
35
35
  switch (this.state) {
36
36
  case HistoryRecorderState.Recording:
37
37
  this.pendingDiff.apply(entry.changes);
38
- this.stacks.update(({ undos }) => ({ undos, redos: stack() }));
38
+ if (this.stacks.get().redos.length > 0) {
39
+ this.stacks.update(({ undos }) => ({ undos, redos: stack() }));
40
+ }
39
41
  break;
40
42
  case HistoryRecorderState.RecordingPreserveRedoStack:
41
43
  this.pendingDiff.apply(entry.changes);
@@ -62,6 +64,12 @@ class HistoryManager {
62
64
  return this.stacks.get().redos.length;
63
65
  }
64
66
  /** @internal */
67
+ _isReplaying = false;
68
+ /** @internal */
69
+ isReplaying() {
70
+ return this._isReplaying;
71
+ }
72
+ /** @internal */
65
73
  _isInBatch = false;
66
74
  batch(fn, opts) {
67
75
  const previousState = this.state;
@@ -90,7 +98,9 @@ class HistoryManager {
90
98
  // History
91
99
  _undo({ pushToRedoStack, toMark = void 0 }) {
92
100
  const previousState = this.state;
101
+ const previousIsReplaying = this._isReplaying;
93
102
  this.state = HistoryRecorderState.Paused;
103
+ this._isReplaying = true;
94
104
  try {
95
105
  let { undos, redos } = this.stacks.get();
96
106
  const pendingDiff = this.pendingDiff.clear();
@@ -144,6 +154,7 @@ class HistoryManager {
144
154
  this.store.ensureStoreIsUsable();
145
155
  this.stacks.set({ undos, redos });
146
156
  } finally {
157
+ this._isReplaying = previousIsReplaying;
147
158
  this.state = previousState;
148
159
  }
149
160
  return this;
@@ -154,7 +165,9 @@ class HistoryManager {
154
165
  }
155
166
  redo() {
156
167
  const previousState = this.state;
168
+ const previousIsReplaying = this._isReplaying;
157
169
  this.state = HistoryRecorderState.Paused;
170
+ this._isReplaying = true;
158
171
  try {
159
172
  this.flushPendingDiff();
160
173
  let { undos, redos } = this.stacks.get();
@@ -180,6 +193,7 @@ class HistoryManager {
180
193
  this.store.ensureStoreIsUsable();
181
194
  this.stacks.set({ undos, redos });
182
195
  } finally {
196
+ this._isReplaying = previousIsReplaying;
183
197
  this.state = previousState;
184
198
  }
185
199
  return this;
@@ -277,12 +291,20 @@ class PendingDiff {
277
291
  }
278
292
  apply(diff) {
279
293
  squashRecordDiffsMutable(this.diff, [diff]);
280
- this.isEmptyAtom.set(isRecordsDiffEmpty(this.diff));
294
+ if (hasAnyKey(diff.added) || hasAnyKey(diff.removed)) {
295
+ this.isEmptyAtom.set(isRecordsDiffEmpty(this.diff));
296
+ } else if (this.isEmptyAtom.__unsafe__getWithoutCapture()) {
297
+ this.isEmptyAtom.set(!hasAnyKey(diff.updated));
298
+ }
281
299
  }
282
300
  debug() {
283
301
  return { diff: this.diff, isEmpty: this.isEmpty() };
284
302
  }
285
303
  }
304
+ function hasAnyKey(obj) {
305
+ for (const _ in obj) return true;
306
+ return false;
307
+ }
286
308
  function stack() {
287
309
  return EMPTY_STACK_ITEM;
288
310
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/HistoryManager/HistoryManager.ts"],
4
- "sourcesContent": ["import { atom, EMPTY_ARRAY, transact } from '@tldraw/state'\nimport {\n\tcreateEmptyRecordsDiff,\n\tisRecordsDiffEmpty,\n\tRecordsDiff,\n\treverseRecordsDiff,\n\tsquashRecordDiffsMutable,\n\tStore,\n\tUnknownRecord,\n} from '@tldraw/store'\nimport { exhaustiveSwitchError, noop } from '@tldraw/utils'\nimport { TLHistoryBatchOptions, TLHistoryEntry } from '../../types/history-types'\n\nconst HistoryRecorderState = {\n\tRecording: 'recording',\n\tRecordingPreserveRedoStack: 'recordingPreserveRedoStack',\n\tPaused: 'paused',\n} as const\ntype HistoryRecorderState = (typeof HistoryRecorderState)[keyof typeof HistoryRecorderState]\n\n/** @public */\nexport class HistoryManager<R extends UnknownRecord> {\n\tprivate readonly store: Store<R>\n\n\treadonly dispose: () => void\n\n\tprivate state: HistoryRecorderState = HistoryRecorderState.Recording\n\tprivate readonly pendingDiff = new PendingDiff<R>()\n\tprivate stacks = atom(\n\t\t'HistoryManager.stacks',\n\t\t{\n\t\t\tundos: stack<TLHistoryEntry<R>>(),\n\t\t\tredos: stack<TLHistoryEntry<R>>(),\n\t\t},\n\t\t{\n\t\t\tisEqual: (a, b) => a.undos === b.undos && a.redos === b.redos,\n\t\t}\n\t)\n\n\tprivate readonly annotateError: (error: unknown) => void\n\n\tconstructor(opts: { store: Store<R>; annotateError?(error: unknown): void }) {\n\t\tthis.store = opts.store\n\t\tthis.annotateError = opts.annotateError ?? noop\n\t\tthis.dispose = this.store.addHistoryInterceptor((entry, source) => {\n\t\t\tif (source !== 'user') return\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase HistoryRecorderState.Recording:\n\t\t\t\t\tthis.pendingDiff.apply(entry.changes)\n\t\t\t\t\tthis.stacks.update(({ undos }) => ({ undos, redos: stack() }))\n\t\t\t\t\tbreak\n\t\t\t\tcase HistoryRecorderState.RecordingPreserveRedoStack:\n\t\t\t\t\tthis.pendingDiff.apply(entry.changes)\n\t\t\t\t\tbreak\n\t\t\t\tcase HistoryRecorderState.Paused:\n\t\t\t\t\tbreak\n\t\t\t\tdefault:\n\t\t\t\t\texhaustiveSwitchError(this.state)\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate flushPendingDiff() {\n\t\tif (this.pendingDiff.isEmpty()) return\n\n\t\tconst diff = this.pendingDiff.clear()\n\t\tthis.stacks.update(({ undos, redos }) => ({\n\t\t\tundos: undos.push({ type: 'diff', diff }),\n\t\t\tredos,\n\t\t}))\n\t}\n\n\tgetNumUndos() {\n\t\treturn this.stacks.get().undos.length + (this.pendingDiff.isEmpty() ? 0 : 1)\n\t}\n\n\tgetNumRedos() {\n\t\treturn this.stacks.get().redos.length\n\t}\n\n\t/** @internal */\n\t_isInBatch = false\n\n\tbatch(fn: () => void, opts?: TLHistoryBatchOptions) {\n\t\tconst previousState = this.state\n\n\t\t// we move to the new state only if we haven't explicitly paused\n\t\tif (previousState !== HistoryRecorderState.Paused && opts?.history) {\n\t\t\tthis.state = modeToState[opts.history]\n\t\t}\n\n\t\ttry {\n\t\t\tif (this._isInBatch) {\n\t\t\t\ttransact(fn)\n\t\t\t\treturn this\n\t\t\t}\n\n\t\t\tthis._isInBatch = true\n\t\t\ttry {\n\t\t\t\ttransact(fn)\n\t\t\t} catch (error) {\n\t\t\t\tthis.annotateError(error)\n\t\t\t\tthrow error\n\t\t\t} finally {\n\t\t\t\tthis._isInBatch = false\n\t\t\t}\n\n\t\t\treturn this\n\t\t} finally {\n\t\t\tthis.state = previousState\n\t\t}\n\t}\n\n\t// History\n\t_undo({ pushToRedoStack, toMark = undefined }: { pushToRedoStack: boolean; toMark?: string }) {\n\t\tconst previousState = this.state\n\t\tthis.state = HistoryRecorderState.Paused\n\t\ttry {\n\t\t\tlet { undos, redos } = this.stacks.get()\n\n\t\t\t// start by collecting the pending diff (everything since the last mark).\n\t\t\t// we'll accumulate the diff to undo in this variable so we can apply it atomically.\n\t\t\tconst pendingDiff = this.pendingDiff.clear()\n\t\t\tconst isPendingDiffEmpty = isRecordsDiffEmpty(pendingDiff)\n\t\t\tconst diffToUndo = reverseRecordsDiff(pendingDiff)\n\n\t\t\tif (pushToRedoStack && !isPendingDiffEmpty) {\n\t\t\t\tredos = redos.push({ type: 'diff', diff: pendingDiff })\n\t\t\t}\n\n\t\t\tlet didFindMark = false\n\t\t\tif (isPendingDiffEmpty) {\n\t\t\t\t// if nothing has happened since the last mark, pop any intermediate marks off the stack\n\t\t\t\twhile (undos.head?.type === 'stop') {\n\t\t\t\t\tconst mark = undos.head\n\t\t\t\t\tundos = undos.tail\n\t\t\t\t\tif (pushToRedoStack) {\n\t\t\t\t\t\tredos = redos.push(mark)\n\t\t\t\t\t}\n\t\t\t\t\tif (mark.id === toMark) {\n\t\t\t\t\t\tdidFindMark = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!didFindMark) {\n\t\t\t\tloop: while (undos.head) {\n\t\t\t\t\tconst undo = undos.head\n\t\t\t\t\tundos = undos.tail\n\n\t\t\t\t\tif (pushToRedoStack) {\n\t\t\t\t\t\tredos = redos.push(undo)\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (undo.type) {\n\t\t\t\t\t\tcase 'diff':\n\t\t\t\t\t\t\tsquashRecordDiffsMutable(diffToUndo, [reverseRecordsDiff(undo.diff)])\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\t\tif (!toMark) break loop\n\t\t\t\t\t\t\tif (undo.id === toMark) {\n\t\t\t\t\t\t\t\tdidFindMark = true\n\t\t\t\t\t\t\t\tbreak loop\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\texhaustiveSwitchError(undo)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!didFindMark && toMark) {\n\t\t\t\t// we didn't find the mark we were looking for \u2014 restore state and bail\n\t\t\t\tthis.pendingDiff.restore(pendingDiff)\n\t\t\t\treturn this\n\t\t\t}\n\n\t\t\tthis.store.applyDiff(diffToUndo, { ignoreEphemeralKeys: true })\n\t\t\tthis.store.ensureStoreIsUsable()\n\t\t\tthis.stacks.set({ undos, redos })\n\t\t} finally {\n\t\t\tthis.state = previousState\n\t\t}\n\n\t\treturn this\n\t}\n\n\tundo() {\n\t\tthis._undo({ pushToRedoStack: true })\n\n\t\treturn this\n\t}\n\n\tredo() {\n\t\tconst previousState = this.state\n\t\tthis.state = HistoryRecorderState.Paused\n\t\ttry {\n\t\t\tthis.flushPendingDiff()\n\n\t\t\tlet { undos, redos } = this.stacks.get()\n\t\t\tif (redos.length === 0) {\n\t\t\t\treturn this\n\t\t\t}\n\n\t\t\t// ignore any intermediate marks - this should take us to the first `diff` entry\n\t\t\twhile (redos.head?.type === 'stop') {\n\t\t\t\tundos = undos.push(redos.head)\n\t\t\t\tredos = redos.tail\n\t\t\t}\n\n\t\t\t// accumulate diffs to be redone so they can be applied atomically\n\t\t\tconst diffToRedo = createEmptyRecordsDiff<R>()\n\n\t\t\twhile (redos.head) {\n\t\t\t\tconst redo = redos.head\n\t\t\t\tundos = undos.push(redo)\n\t\t\t\tredos = redos.tail\n\n\t\t\t\tif (redo.type === 'diff') {\n\t\t\t\t\tsquashRecordDiffsMutable(diffToRedo, [redo.diff])\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.store.applyDiff(diffToRedo, { ignoreEphemeralKeys: true })\n\t\t\tthis.store.ensureStoreIsUsable()\n\t\t\tthis.stacks.set({ undos, redos })\n\t\t} finally {\n\t\t\tthis.state = previousState\n\t\t}\n\n\t\treturn this\n\t}\n\n\tbail() {\n\t\tthis._undo({ pushToRedoStack: false })\n\n\t\treturn this\n\t}\n\n\tbailToMark(id: string) {\n\t\tif (id) {\n\t\t\tthis._undo({ pushToRedoStack: false, toMark: id })\n\t\t}\n\n\t\treturn this\n\t}\n\n\tsquashToMark(id: string) {\n\t\t// remove marks between head and the mark\n\n\t\tlet top = this.stacks.get().undos\n\t\tconst popped: Array<RecordsDiff<R>> = []\n\n\t\twhile (top.head && !(top.head.type === 'stop' && top.head.id === id)) {\n\t\t\tif (top.head.type === 'diff') {\n\t\t\t\tpopped.push(top.head.diff)\n\t\t\t}\n\t\t\ttop = top.tail\n\t\t}\n\n\t\tif (!top.head || top.head?.id !== id) {\n\t\t\tconsole.error('Could not find mark to squash to: ', id)\n\t\t\treturn this\n\t\t}\n\t\tif (popped.length === 0) {\n\t\t\treturn this\n\t\t}\n\n\t\tconst diff = createEmptyRecordsDiff<R>()\n\t\tsquashRecordDiffsMutable(diff, popped.reverse())\n\n\t\tthis.stacks.update(({ redos }) => ({\n\t\t\tundos: top.push({\n\t\t\t\ttype: 'diff',\n\t\t\t\tdiff,\n\t\t\t}),\n\t\t\tredos,\n\t\t}))\n\n\t\treturn this\n\t}\n\n\t/** @internal */\n\t_mark(id: string) {\n\t\ttransact(() => {\n\t\t\tthis.flushPendingDiff()\n\t\t\tthis.stacks.update(({ undos, redos }) => ({ undos: undos.push({ type: 'stop', id }), redos }))\n\t\t})\n\t}\n\n\tclear() {\n\t\tthis.stacks.set({ undos: stack(), redos: stack() })\n\t\tthis.pendingDiff.clear()\n\t}\n\n\t/** @internal */\n\tgetMarkIdMatching(idSubstring: string) {\n\t\tlet top = this.stacks.get().undos\n\t\twhile (top.head) {\n\t\t\tif (top.head.type === 'stop' && top.head.id.includes(idSubstring)) {\n\t\t\t\treturn top.head.id\n\t\t\t}\n\t\t\ttop = top.tail\n\t\t}\n\t\treturn null\n\t}\n\n\t/** @internal */\n\tdebug() {\n\t\tconst { undos, redos } = this.stacks.get()\n\t\treturn {\n\t\t\tundos: stackToArray(undos),\n\t\t\tredos: stackToArray(redos),\n\t\t\tpendingDiff: this.pendingDiff.debug(),\n\t\t\tstate: this.state as string,\n\t\t}\n\t}\n}\n\nconst modeToState = {\n\trecord: HistoryRecorderState.Recording,\n\t'record-preserveRedoStack': HistoryRecorderState.RecordingPreserveRedoStack,\n\tignore: HistoryRecorderState.Paused,\n} as const\n\nclass PendingDiff<R extends UnknownRecord> {\n\tprivate diff = createEmptyRecordsDiff<R>()\n\tprivate isEmptyAtom = atom('PendingDiff.isEmpty', true)\n\n\tclear() {\n\t\tconst diff = this.diff\n\t\tthis.diff = createEmptyRecordsDiff<R>()\n\t\tthis.isEmptyAtom.set(true)\n\t\treturn diff\n\t}\n\n\trestore(diff: RecordsDiff<R>) {\n\t\tthis.diff = diff\n\t\tthis.isEmptyAtom.set(isRecordsDiffEmpty(diff))\n\t}\n\n\tisEmpty() {\n\t\treturn this.isEmptyAtom.get()\n\t}\n\n\tapply(diff: RecordsDiff<R>) {\n\t\tsquashRecordDiffsMutable(this.diff, [diff])\n\t\tthis.isEmptyAtom.set(isRecordsDiffEmpty(this.diff))\n\t}\n\n\tdebug() {\n\t\treturn { diff: this.diff, isEmpty: this.isEmpty() }\n\t}\n}\n\ntype Stack<T> = StackItem<T> | EmptyStackItem<T>\n\nfunction stack<T>(): Stack<T> {\n\treturn EMPTY_STACK_ITEM as any\n}\n\nclass EmptyStackItem<T> {\n\treadonly length = 0\n\treadonly head = null\n\treadonly tail: Stack<T> = this\n\n\tpush(head: T): Stack<T> {\n\t\treturn new StackItem<T>(head, this)\n\t}\n}\n\nconst EMPTY_STACK_ITEM = new EmptyStackItem()\n\nclass StackItem<T> {\n\tlength: number\n\tconstructor(\n\t\tpublic readonly head: T,\n\t\tpublic readonly tail: Stack<T>\n\t) {\n\t\tthis.length = tail.length + 1\n\t}\n\n\tpush(head: T): Stack<T> {\n\t\treturn new StackItem(head, this)\n\t}\n}\n\nfunction stackToArray<T>(stack: Stack<T>) {\n\tif (!stack.length) {\n\t\treturn EMPTY_ARRAY\n\t}\n\tconst arr: T[] = []\n\twhile (stack.length) {\n\t\tarr.push(stack.head!)\n\t\tstack = stack.tail\n\t}\n\treturn arr\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { atom, EMPTY_ARRAY, transact } from '@tldraw/state'\nimport {\n\tcreateEmptyRecordsDiff,\n\tisRecordsDiffEmpty,\n\tRecordsDiff,\n\treverseRecordsDiff,\n\tsquashRecordDiffsMutable,\n\tStore,\n\tUnknownRecord,\n} from '@tldraw/store'\nimport { exhaustiveSwitchError, noop } from '@tldraw/utils'\nimport { TLHistoryBatchOptions, TLHistoryEntry } from '../../types/history-types'\n\nconst HistoryRecorderState = {\n\tRecording: 'recording',\n\tRecordingPreserveRedoStack: 'recordingPreserveRedoStack',\n\tPaused: 'paused',\n} as const\ntype HistoryRecorderState = (typeof HistoryRecorderState)[keyof typeof HistoryRecorderState]\n\n/** @public */\nexport class HistoryManager<R extends UnknownRecord> {\n\tprivate readonly store: Store<R>\n\n\treadonly dispose: () => void\n\n\tprivate state: HistoryRecorderState = HistoryRecorderState.Recording\n\tprivate readonly pendingDiff = new PendingDiff<R>()\n\tprivate stacks = atom(\n\t\t'HistoryManager.stacks',\n\t\t{\n\t\t\tundos: stack<TLHistoryEntry<R>>(),\n\t\t\tredos: stack<TLHistoryEntry<R>>(),\n\t\t},\n\t\t{\n\t\t\tisEqual: (a, b) => a.undos === b.undos && a.redos === b.redos,\n\t\t}\n\t)\n\n\tprivate readonly annotateError: (error: unknown) => void\n\n\tconstructor(opts: { store: Store<R>; annotateError?(error: unknown): void }) {\n\t\tthis.store = opts.store\n\t\tthis.annotateError = opts.annotateError ?? noop\n\t\tthis.dispose = this.store.addHistoryInterceptor((entry, source) => {\n\t\t\tif (source !== 'user') return\n\n\t\t\tswitch (this.state) {\n\t\t\t\tcase HistoryRecorderState.Recording:\n\t\t\t\t\tthis.pendingDiff.apply(entry.changes)\n\t\t\t\t\t// this interceptor runs for every store change, so skip the update when the\n\t\t\t\t\t// redo stack is already empty\n\t\t\t\t\tif (this.stacks.get().redos.length > 0) {\n\t\t\t\t\t\tthis.stacks.update(({ undos }) => ({ undos, redos: stack() }))\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\tcase HistoryRecorderState.RecordingPreserveRedoStack:\n\t\t\t\t\tthis.pendingDiff.apply(entry.changes)\n\t\t\t\t\tbreak\n\t\t\t\tcase HistoryRecorderState.Paused:\n\t\t\t\t\tbreak\n\t\t\t\tdefault:\n\t\t\t\t\texhaustiveSwitchError(this.state)\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate flushPendingDiff() {\n\t\tif (this.pendingDiff.isEmpty()) return\n\n\t\tconst diff = this.pendingDiff.clear()\n\t\tthis.stacks.update(({ undos, redos }) => ({\n\t\t\tundos: undos.push({ type: 'diff', diff }),\n\t\t\tredos,\n\t\t}))\n\t}\n\n\tgetNumUndos() {\n\t\treturn this.stacks.get().undos.length + (this.pendingDiff.isEmpty() ? 0 : 1)\n\t}\n\n\tgetNumRedos() {\n\t\treturn this.stacks.get().redos.length\n\t}\n\n\t/** @internal */\n\tprivate _isReplaying = false\n\n\t/** @internal */\n\tisReplaying() {\n\t\treturn this._isReplaying\n\t}\n\n\t/** @internal */\n\t_isInBatch = false\n\n\tbatch(fn: () => void, opts?: TLHistoryBatchOptions) {\n\t\tconst previousState = this.state\n\n\t\t// we move to the new state only if we haven't explicitly paused\n\t\tif (previousState !== HistoryRecorderState.Paused && opts?.history) {\n\t\t\tthis.state = modeToState[opts.history]\n\t\t}\n\n\t\ttry {\n\t\t\tif (this._isInBatch) {\n\t\t\t\ttransact(fn)\n\t\t\t\treturn this\n\t\t\t}\n\n\t\t\tthis._isInBatch = true\n\t\t\ttry {\n\t\t\t\ttransact(fn)\n\t\t\t} catch (error) {\n\t\t\t\tthis.annotateError(error)\n\t\t\t\tthrow error\n\t\t\t} finally {\n\t\t\t\tthis._isInBatch = false\n\t\t\t}\n\n\t\t\treturn this\n\t\t} finally {\n\t\t\tthis.state = previousState\n\t\t}\n\t}\n\n\t// History\n\t_undo({ pushToRedoStack, toMark = undefined }: { pushToRedoStack: boolean; toMark?: string }) {\n\t\tconst previousState = this.state\n\t\tconst previousIsReplaying = this._isReplaying\n\t\tthis.state = HistoryRecorderState.Paused\n\t\tthis._isReplaying = true\n\t\ttry {\n\t\t\tlet { undos, redos } = this.stacks.get()\n\n\t\t\t// start by collecting the pending diff (everything since the last mark).\n\t\t\t// we'll accumulate the diff to undo in this variable so we can apply it atomically.\n\t\t\tconst pendingDiff = this.pendingDiff.clear()\n\t\t\tconst isPendingDiffEmpty = isRecordsDiffEmpty(pendingDiff)\n\t\t\tconst diffToUndo = reverseRecordsDiff(pendingDiff)\n\n\t\t\tif (pushToRedoStack && !isPendingDiffEmpty) {\n\t\t\t\tredos = redos.push({ type: 'diff', diff: pendingDiff })\n\t\t\t}\n\n\t\t\tlet didFindMark = false\n\t\t\tif (isPendingDiffEmpty) {\n\t\t\t\t// if nothing has happened since the last mark, pop any intermediate marks off the stack\n\t\t\t\twhile (undos.head?.type === 'stop') {\n\t\t\t\t\tconst mark = undos.head\n\t\t\t\t\tundos = undos.tail\n\t\t\t\t\tif (pushToRedoStack) {\n\t\t\t\t\t\tredos = redos.push(mark)\n\t\t\t\t\t}\n\t\t\t\t\tif (mark.id === toMark) {\n\t\t\t\t\t\tdidFindMark = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!didFindMark) {\n\t\t\t\tloop: while (undos.head) {\n\t\t\t\t\tconst undo = undos.head\n\t\t\t\t\tundos = undos.tail\n\n\t\t\t\t\tif (pushToRedoStack) {\n\t\t\t\t\t\tredos = redos.push(undo)\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (undo.type) {\n\t\t\t\t\t\tcase 'diff':\n\t\t\t\t\t\t\tsquashRecordDiffsMutable(diffToUndo, [reverseRecordsDiff(undo.diff)])\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase 'stop':\n\t\t\t\t\t\t\tif (!toMark) break loop\n\t\t\t\t\t\t\tif (undo.id === toMark) {\n\t\t\t\t\t\t\t\tdidFindMark = true\n\t\t\t\t\t\t\t\tbreak loop\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\texhaustiveSwitchError(undo)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!didFindMark && toMark) {\n\t\t\t\t// we didn't find the mark we were looking for \u2014 restore state and bail\n\t\t\t\tthis.pendingDiff.restore(pendingDiff)\n\t\t\t\treturn this\n\t\t\t}\n\t\t\tthis.store.applyDiff(diffToUndo, { ignoreEphemeralKeys: true })\n\t\t\tthis.store.ensureStoreIsUsable()\n\t\t\tthis.stacks.set({ undos, redos })\n\t\t} finally {\n\t\t\tthis._isReplaying = previousIsReplaying\n\t\t\tthis.state = previousState\n\t\t}\n\n\t\treturn this\n\t}\n\n\tundo() {\n\t\tthis._undo({ pushToRedoStack: true })\n\n\t\treturn this\n\t}\n\n\tredo() {\n\t\tconst previousState = this.state\n\t\tconst previousIsReplaying = this._isReplaying\n\t\tthis.state = HistoryRecorderState.Paused\n\t\tthis._isReplaying = true\n\t\ttry {\n\t\t\tthis.flushPendingDiff()\n\n\t\t\tlet { undos, redos } = this.stacks.get()\n\t\t\tif (redos.length === 0) {\n\t\t\t\treturn this\n\t\t\t}\n\n\t\t\t// ignore any intermediate marks - this should take us to the first `diff` entry\n\t\t\twhile (redos.head?.type === 'stop') {\n\t\t\t\tundos = undos.push(redos.head)\n\t\t\t\tredos = redos.tail\n\t\t\t}\n\n\t\t\t// accumulate diffs to be redone so they can be applied atomically\n\t\t\tconst diffToRedo = createEmptyRecordsDiff<R>()\n\n\t\t\twhile (redos.head) {\n\t\t\t\tconst redo = redos.head\n\t\t\t\tundos = undos.push(redo)\n\t\t\t\tredos = redos.tail\n\n\t\t\t\tif (redo.type === 'diff') {\n\t\t\t\t\tsquashRecordDiffsMutable(diffToRedo, [redo.diff])\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.store.applyDiff(diffToRedo, { ignoreEphemeralKeys: true })\n\t\t\tthis.store.ensureStoreIsUsable()\n\t\t\tthis.stacks.set({ undos, redos })\n\t\t} finally {\n\t\t\tthis._isReplaying = previousIsReplaying\n\t\t\tthis.state = previousState\n\t\t}\n\n\t\treturn this\n\t}\n\n\tbail() {\n\t\tthis._undo({ pushToRedoStack: false })\n\n\t\treturn this\n\t}\n\n\tbailToMark(id: string) {\n\t\tif (id) {\n\t\t\tthis._undo({ pushToRedoStack: false, toMark: id })\n\t\t}\n\n\t\treturn this\n\t}\n\n\tsquashToMark(id: string) {\n\t\t// remove marks between head and the mark\n\n\t\tlet top = this.stacks.get().undos\n\t\tconst popped: Array<RecordsDiff<R>> = []\n\n\t\twhile (top.head && !(top.head.type === 'stop' && top.head.id === id)) {\n\t\t\tif (top.head.type === 'diff') {\n\t\t\t\tpopped.push(top.head.diff)\n\t\t\t}\n\t\t\ttop = top.tail\n\t\t}\n\n\t\tif (!top.head || top.head?.id !== id) {\n\t\t\tconsole.error('Could not find mark to squash to: ', id)\n\t\t\treturn this\n\t\t}\n\t\tif (popped.length === 0) {\n\t\t\treturn this\n\t\t}\n\n\t\tconst diff = createEmptyRecordsDiff<R>()\n\t\tsquashRecordDiffsMutable(diff, popped.reverse())\n\n\t\tthis.stacks.update(({ redos }) => ({\n\t\t\tundos: top.push({\n\t\t\t\ttype: 'diff',\n\t\t\t\tdiff,\n\t\t\t}),\n\t\t\tredos,\n\t\t}))\n\n\t\treturn this\n\t}\n\n\t/** @internal */\n\t_mark(id: string) {\n\t\ttransact(() => {\n\t\t\tthis.flushPendingDiff()\n\t\t\tthis.stacks.update(({ undos, redos }) => ({ undos: undos.push({ type: 'stop', id }), redos }))\n\t\t})\n\t}\n\n\tclear() {\n\t\tthis.stacks.set({ undos: stack(), redos: stack() })\n\t\tthis.pendingDiff.clear()\n\t}\n\n\t/** @internal */\n\tgetMarkIdMatching(idSubstring: string) {\n\t\tlet top = this.stacks.get().undos\n\t\twhile (top.head) {\n\t\t\tif (top.head.type === 'stop' && top.head.id.includes(idSubstring)) {\n\t\t\t\treturn top.head.id\n\t\t\t}\n\t\t\ttop = top.tail\n\t\t}\n\t\treturn null\n\t}\n\n\t/** @internal */\n\tdebug() {\n\t\tconst { undos, redos } = this.stacks.get()\n\t\treturn {\n\t\t\tundos: stackToArray(undos),\n\t\t\tredos: stackToArray(redos),\n\t\t\tpendingDiff: this.pendingDiff.debug(),\n\t\t\tstate: this.state as string,\n\t\t}\n\t}\n}\n\nconst modeToState = {\n\trecord: HistoryRecorderState.Recording,\n\t'record-preserveRedoStack': HistoryRecorderState.RecordingPreserveRedoStack,\n\tignore: HistoryRecorderState.Paused,\n} as const\n\nclass PendingDiff<R extends UnknownRecord> {\n\tprivate diff = createEmptyRecordsDiff<R>()\n\tprivate isEmptyAtom = atom('PendingDiff.isEmpty', true)\n\n\tclear() {\n\t\tconst diff = this.diff\n\t\tthis.diff = createEmptyRecordsDiff<R>()\n\t\tthis.isEmptyAtom.set(true)\n\t\treturn diff\n\t}\n\n\trestore(diff: RecordsDiff<R>) {\n\t\tthis.diff = diff\n\t\tthis.isEmptyAtom.set(isRecordsDiffEmpty(diff))\n\t}\n\n\tisEmpty() {\n\t\treturn this.isEmptyAtom.get()\n\t}\n\n\tapply(diff: RecordsDiff<R>) {\n\t\tsquashRecordDiffsMutable(this.diff, [diff])\n\t\t// Recomputing emptiness from the accumulated diff is O(N) (for-in over a\n\t\t// dictionary-mode object pays an O(N) key-collection prologue), and this runs on\n\t\t// every history interceptor call \u2014 e.g. every input tick while resizing many\n\t\t// shapes. Updates can never cancel out existing entries during a squash, so the\n\t\t// full recompute is only needed when the incoming diff adds or removes records.\n\t\tif (hasAnyKey(diff.added) || hasAnyKey(diff.removed)) {\n\t\t\tthis.isEmptyAtom.set(isRecordsDiffEmpty(this.diff))\n\t\t} else if (this.isEmptyAtom.__unsafe__getWithoutCapture()) {\n\t\t\tthis.isEmptyAtom.set(!hasAnyKey(diff.updated))\n\t\t}\n\t}\n\n\tdebug() {\n\t\treturn { diff: this.diff, isEmpty: this.isEmpty() }\n\t}\n}\n\nfunction hasAnyKey(obj: object) {\n\tfor (const _ in obj) return true\n\treturn false\n}\n\ntype Stack<T> = StackItem<T> | EmptyStackItem<T>\n\nfunction stack<T>(): Stack<T> {\n\treturn EMPTY_STACK_ITEM as any\n}\n\nclass EmptyStackItem<T> {\n\treadonly length = 0\n\treadonly head = null\n\treadonly tail: Stack<T> = this\n\n\tpush(head: T): Stack<T> {\n\t\treturn new StackItem<T>(head, this)\n\t}\n}\n\nconst EMPTY_STACK_ITEM = new EmptyStackItem()\n\nclass StackItem<T> {\n\tlength: number\n\tconstructor(\n\t\tpublic readonly head: T,\n\t\tpublic readonly tail: Stack<T>\n\t) {\n\t\tthis.length = tail.length + 1\n\t}\n\n\tpush(head: T): Stack<T> {\n\t\treturn new StackItem(head, this)\n\t}\n}\n\nfunction stackToArray<T>(stack: Stack<T>) {\n\tif (!stack.length) {\n\t\treturn EMPTY_ARRAY\n\t}\n\tconst arr: T[] = []\n\twhile (stack.length) {\n\t\tarr.push(stack.head!)\n\t\tstack = stack.tail\n\t}\n\treturn arr\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["stack"]
7
7
  }