tldraw 5.5.2 → 5.6.0-canary.035b741dc331

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 (122) hide show
  1. package/DOCS.md +42 -37
  2. package/RELEASE_NOTES.md +8 -36
  3. package/dist-cjs/index.d.ts +6 -7
  4. package/dist-cjs/index.js +1 -1
  5. package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js +1 -1
  6. package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js.map +2 -2
  7. package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js +24 -16
  8. package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js.map +2 -2
  9. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +3 -6
  10. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  11. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +2 -8
  12. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
  13. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js +1 -3
  14. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
  15. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +2 -1
  16. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
  17. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +2 -2
  18. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
  19. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +32 -62
  20. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +3 -3
  21. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js +0 -4
  22. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
  23. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +18 -20
  24. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
  25. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +2 -2
  26. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  27. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +1 -2
  28. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  29. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +0 -2
  30. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  31. package/dist-cjs/lib/tools/SelectTool/selectHelpers.js +5 -0
  32. package/dist-cjs/lib/tools/SelectTool/selectHelpers.js.map +2 -2
  33. package/dist-cjs/lib/tools/selection-logic/selectOnCanvasPointerUp.js +2 -1
  34. package/dist-cjs/lib/tools/selection-logic/selectOnCanvasPointerUp.js.map +2 -2
  35. package/dist-cjs/lib/tools/selection-logic/updateHoveredShapeId.js.map +2 -2
  36. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js +3 -3
  37. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js.map +2 -2
  38. package/dist-cjs/lib/ui/context/actions.js +3 -4
  39. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  40. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  41. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  42. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +1 -0
  43. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  44. package/dist-cjs/lib/ui/version.js +3 -3
  45. package/dist-cjs/lib/ui/version.js.map +1 -1
  46. package/dist-esm/index.d.mts +6 -7
  47. package/dist-esm/index.mjs +1 -1
  48. package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs +1 -1
  49. package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs.map +2 -2
  50. package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs +24 -16
  51. package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs.map +2 -2
  52. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +3 -6
  53. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  54. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +2 -8
  55. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
  56. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs +1 -3
  57. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
  58. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +2 -2
  59. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
  60. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +2 -2
  61. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
  62. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +32 -62
  63. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +3 -3
  64. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs +0 -4
  65. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
  66. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +18 -20
  67. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
  68. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +2 -3
  69. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  70. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +1 -2
  71. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  72. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +0 -2
  73. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  74. package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs +8 -1
  75. package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs.map +2 -2
  76. package/dist-esm/lib/tools/selection-logic/selectOnCanvasPointerUp.mjs +2 -1
  77. package/dist-esm/lib/tools/selection-logic/selectOnCanvasPointerUp.mjs.map +2 -2
  78. package/dist-esm/lib/tools/selection-logic/updateHoveredShapeId.mjs.map +2 -2
  79. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs +3 -3
  80. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs.map +2 -2
  81. package/dist-esm/lib/ui/context/actions.mjs +3 -4
  82. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  83. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  84. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +1 -0
  85. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  86. package/dist-esm/lib/ui/version.mjs +3 -3
  87. package/dist-esm/lib/ui/version.mjs.map +1 -1
  88. package/package.json +17 -23
  89. package/src/lib/overlays/ShapeIndicatorOverlayUtil.ts +1 -1
  90. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +43 -0
  91. package/src/lib/shapes/arrow/toolStates/Idle.tsx +29 -17
  92. package/src/lib/tools/EraserTool/childStates/Erasing.ts +3 -8
  93. package/src/lib/tools/SelectTool/DragAndDropManager.ts +2 -9
  94. package/src/lib/tools/SelectTool/childStates/Brushing.ts +2 -4
  95. package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +2 -2
  96. package/src/lib/tools/SelectTool/childStates/EditingShape.ts +2 -2
  97. package/src/lib/tools/SelectTool/childStates/Idle.ts +35 -62
  98. package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +0 -8
  99. package/src/lib/tools/SelectTool/childStates/PointingShape.ts +21 -25
  100. package/src/lib/tools/SelectTool/childStates/Resizing.ts +2 -6
  101. package/src/lib/tools/SelectTool/childStates/Rotating.ts +1 -4
  102. package/src/lib/tools/SelectTool/childStates/Translating.ts +0 -2
  103. package/src/lib/tools/SelectTool/selectHelpers.ts +14 -0
  104. package/src/lib/tools/selection-logic/selectOnCanvasPointerUp.ts +2 -1
  105. package/src/lib/tools/selection-logic/updateHoveredShapeId.ts +2 -5
  106. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.tsx +6 -6
  107. package/src/lib/ui/context/actions.tsx +3 -4
  108. package/src/lib/ui/context/events.tsx +3 -0
  109. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +1 -0
  110. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +1 -0
  111. package/src/lib/ui/version.ts +3 -3
  112. package/src/test/EraserTool.test.ts +42 -1
  113. package/src/test/ScribbleManager.test.ts +25 -0
  114. package/src/test/SelectTool.test.ts +31 -0
  115. package/src/test/commands/duplicatePage.test.ts +41 -1
  116. package/src/test/resizeBox.test.ts +25 -0
  117. package/src/test/resizing.test.ts +71 -0
  118. package/src/test/richTextToolbarEvents.test.tsx +46 -0
  119. package/src/test/shapeutils.test.ts +58 -1
  120. package/src/test/testutils/renderTldrawComponent.tsx +3 -3
  121. package/src/test/ui/keyboardShortcuts.test.tsx +26 -0
  122. package/tldraw.css +0 -4
package/DOCS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tldraw Documentation
2
2
 
3
- Version: `5.5.2`
3
+ Version: `5.6.0-canary.035b741dc331`
4
4
 
5
5
  This file is generated during package publishing from the tldraw docs content for this exact package version.
6
6
 
@@ -6781,6 +6781,8 @@ Mount the layer through the `InFrontOfTheCanvas` component slot so it sits above
6781
6781
 
6782
6782
  `commentToolOverrides` puts the comment tool in Quick Actions with the `C` shortcut. With the tool active, clicking empty canvas starts a thread anchored to that point, and clicking a shape anchors a comment to the shape. You can also turn on region comments, where dragging the tool out covers an area. Either way the click only opens a composer: nothing is written to the store until the comment is posted.
6783
6783
 
6784
+ To start a comment from somewhere other than the tool, such as a context menu item, call `startCommentAt(editor, point)` with a page point. It enters the comment tool and opens the composer there, anchored the same way a click would be.
6785
+
6784
6786
  Once a comment thread exists, its pin is the handle for everything else.
6785
6787
 
6786
6788
  | Input | Result |
@@ -7318,19 +7320,19 @@ This is the same question the client's [`canModifyComment`](#who-can-edit-and-de
7318
7320
 
7319
7321
  `CanvasComments` is one way to assemble the parts, and every part it uses is exported. You can rebuild it, or build something quite different, from the same pieces.
7320
7322
 
7321
- | Export | Description |
7322
- | ---------------------------------------- | ----------------------------------------------------------- |
7323
- | `CommentTool`, `commentToolOverrides` | Placement: the tool state machine and its toolbar entry. |
7324
- | `CommentPin`, `CountBadge` | The pin marker and the clustered-count badge. |
7325
- | `CommentThread`, `CommentCard`, `Byline` | A thread and its comments. |
7326
- | `CommentComposer`, `SendButton` | The composer and its send control. |
7327
- | `CommentsList`, `CommentListItem` | A list of threads and one row of it. |
7328
- | `EmptyState`, `sortSidebarRows` | The list's empty state, and the sidebar's ordering. |
7329
- | `Avatar`, `Mention`, `MentionList` | Author avatars and the mention picker. |
7330
- | `Reaction`, `Reactions` | A reaction pill and a comment's row of them. |
7331
- | `anchorPagePoint`, `shapeAnchorAt` | Anchor math: page position from an anchor, and the reverse. |
7332
- | `editComment`, `deleteComment` | The write verbs. See [Writing comments](#writing-comments). |
7333
- | `registerCommentAnchorLifecycle` | Keeps shape-anchored threads alive across shape deletion. |
7323
+ | Export | Description |
7324
+ | ------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
7325
+ | `CommentTool`, `commentToolOverrides`, `startCommentAt` | Placement: the tool state machine, its toolbar entry, and starting a comment at a point. |
7326
+ | `CommentPin`, `CountBadge` | The pin marker and the clustered-count badge. |
7327
+ | `CommentThread`, `CommentCard`, `Byline` | A thread and its comments. |
7328
+ | `CommentComposer`, `SendButton` | The composer and its send control. |
7329
+ | `CommentsList`, `CommentListItem` | A list of threads and one row of it. |
7330
+ | `EmptyState`, `sortSidebarRows` | The list's empty state, and the sidebar's ordering. |
7331
+ | `Avatar`, `Mention`, `MentionList` | Author avatars and the mention picker. |
7332
+ | `Reaction`, `Reactions` | A reaction pill and a comment's row of them. |
7333
+ | `anchorPagePoint`, `shapeAnchorAt` | Anchor math: page position from an anchor, and the reverse. |
7334
+ | `editComment`, `deleteComment` | The write verbs. See [Writing comments](#writing-comments). |
7335
+ | `registerCommentAnchorLifecycle` | Keeps shape-anchored threads alive across shape deletion. |
7334
7336
 
7335
7337
  The presentational components take plain props and know nothing about the editor. The canvas layer, the tool, and the hooks build on them. `CanvasComments` registers the anchor lifecycle for you, so call `registerCommentAnchorLifecycle` yourself only if you're replacing the layer wholesale.
7336
7338
 
@@ -7840,31 +7842,34 @@ The `type` determines the visual appearance—like `'default'`, `'grab'`, or `'n
7840
7842
 
7841
7843
  tldraw supports these cursor types:
7842
7844
 
7843
- | Type | Description |
7844
- | ------------- | ---------------------------------------------- |
7845
- | `default` | Standard pointer arrow |
7846
- | `pointer` | Hand indicating clickable element |
7847
- | `cross` | Crosshair for precise positioning |
7848
- | `comment` | Speech bubble for placing comments |
7849
- | `grab` | Open hand for draggable content |
7850
- | `grabbing` | Closed hand while dragging |
7851
- | `text` | I-beam for text editing |
7852
- | `move` | Four-way arrow for moving elements |
7853
- | `zoom-in` | Magnifying glass with plus |
7854
- | `zoom-out` | Magnifying glass with minus |
7855
- | `ew-resize` | Horizontal resize (east-west) |
7856
- | `ns-resize` | Vertical resize (north-south) |
7857
- | `nesw-resize` | Diagonal resize (northeast-southwest) |
7858
- | `nwse-resize` | Diagonal resize (northwest-southeast) |
7859
- | `nesw-rotate` | Rotation handle (northeast-southwest position) |
7860
- | `nwse-rotate` | Rotation handle (northwest-southeast position) |
7861
- | `senw-rotate` | Rotation handle (southeast-northwest position) |
7862
- | `swne-rotate` | Rotation handle (southwest-northeast position) |
7863
- | `none` | Hidden cursor |
7845
+ | Type | Description |
7846
+ | --------------- | --------------------------------------------------------- |
7847
+ | `default` | Standard pointer arrow |
7848
+ | `pointer` | Hand indicating clickable element |
7849
+ | `cross` | Crosshair for precise positioning |
7850
+ | `comment` | Speech bubble for placing comments |
7851
+ | `grab` | Open hand for draggable content |
7852
+ | `grabbing` | Closed hand while dragging |
7853
+ | `text` | I-beam for text editing |
7854
+ | `move` | Four-way arrow for moving elements |
7855
+ | `zoom-in` | Magnifying glass with plus |
7856
+ | `zoom-out` | Magnifying glass with minus |
7857
+ | `ew-resize` | Horizontal resize (east-west) |
7858
+ | `ns-resize` | Vertical resize (north-south) |
7859
+ | `nesw-resize` | Diagonal resize (northeast-southwest) |
7860
+ | `nwse-resize` | Diagonal resize (northwest-southeast) |
7861
+ | `nesw-rotate` | Rotation handle (northeast-southwest position) |
7862
+ | `nwse-rotate` | Rotation handle (northwest-southeast position) |
7863
+ | `senw-rotate` | Rotation handle (southeast-northwest position) |
7864
+ | `swne-rotate` | Rotation handle (southwest-northeast position) |
7865
+ | `none` | Hidden cursor |
7866
+ | `rotate` | Static rotation cursor (falls back to `pointer`) |
7867
+ | `resize-edge` | Static edge resize cursor (falls back to `ew-resize`) |
7868
+ | `resize-corner` | Static corner resize cursor (falls back to `nesw-resize`) |
7864
7869
 
7865
7870
  Static cursors like `default`, `pointer`, and `grab` are prerendered SVG cursors defined as CSS custom properties in tldraw's stylesheet (`--tl-cursor-default`, `--tl-cursor-grab`, and so on). Dynamic cursors like the resize and rotate types are generated at runtime as SVGs with the current rotation applied. The canvas reads the result from the `--tl-cursor` variable.
7866
7871
 
7867
- > `TLCursorType` also includes `resize-edge`, `resize-corner`, and `rotate` for schema compatibility. The default cursor rendering doesn't support them, so don't pass them to `setCursor`.
7872
+ The `rotate`, `resize-edge`, and `resize-corner` types are static too: they map to `--tl-cursor-rotate`, `--tl-cursor-resize-edge`, and `--tl-cursor-resize-corner`, which default to the browser's `pointer`, `ew-resize`, and `nesw-resize` cursors and ignore the cursor rotation. Use the direction-specific `*-resize` and `*-rotate` types when you want the rotated SVG cursors.
7868
7873
 
7869
7874
  #### Setting the cursor
7870
7875
 
@@ -20984,7 +20989,7 @@ The tldraw SDK is a product of a company. See [tldraw.dev](https://tldraw.dev/co
20984
20989
 
20985
20990
  ### Dependencies
20986
20991
 
20987
- {/* This page is generated by `yarn license-report --prod`. Do not edit it by hand. */}
20992
+ {/* This page is generated by `pnpm license-report --prod`. Do not edit it by hand. */}
20988
20993
 
20989
20994
  The tldraw SDK itself is published under the [tldraw license](https://tldraw.dev/legal/tldraw-license). It also depends on the third-party packages listed below, each under its own license.
20990
20995
 
package/RELEASE_NOTES.md CHANGED
@@ -1,39 +1,9 @@
1
1
  # Releases
2
2
 
3
- Version: `5.5.2`
3
+ Version: `5.6.0-canary.035b741dc331`
4
4
 
5
5
  This file is generated during package publishing from the tldraw release notes content for this exact package version.
6
6
 
7
- ## v5.5.2
8
-
9
- So far this release adds a `ThrottledFunction` type to `@tldraw/utils`, thins the hinted shape outline, and fixes bugs in resizing, page duplication, local persistence, the store, and sync.
10
-
11
- ### API changes
12
-
13
- - Add a `ThrottledFunction<T>` type to `@tldraw/utils` for annotating the result of `throttle`. `throttle` now returns this type, with the same runtime behavior. ([#10927](https://github.com/tldraw/tldraw/pull/10927))
14
-
15
- ### Improvements
16
-
17
- - Draw the hinted outline around shapes, such as a frame you're dragging a shape into, at 2px instead of 2.5px. ([#10952](https://github.com/tldraw/tldraw/pull/10952))
18
- - Speed up `store.query.index()` and `store.query.ids()` updates by no longer allocating for each change. ([#10228](https://github.com/tldraw/tldraw/pull/10228))
19
-
20
- ### Bug fixes
21
-
22
- - Fix three resize issues: flipping a rotated selection gave its children the wrong rotation, shapes not aligned with the selection received `onResizeStart` and `onResizeEnd` more than once, and `resizeBox` slid the fixed edge when a shape hit its `maxWidth` or `maxHeight`. ([#10351](https://github.com/tldraw/tldraw/pull/10351))
23
- - Fix duplicating a page moving its shapes when they were off screen, and starting the new page with the current page's camera instead of the duplicated page's. ([#10465](https://github.com/tldraw/tldraw/pull/10465))
24
- - Fix the arrow tool's precise mode carrying over when you move to a different target shape. ([#10244](https://github.com/tldraw/tldraw/pull/10244))
25
- - Fix holding Cmd/Ctrl+Shift+Arrow switching from selecting a parent or child to selecting the adjacent shape once the key starts repeating. ([#10184](https://github.com/tldraw/tldraw/pull/10184))
26
- - Fix `editor.setCursor` throwing for the rotate and resize cursor types, and hex cursor colors breaking SVG cursors. ([#10350](https://github.com/tldraw/tldraw/pull/10350))
27
- - Fix edits being lost when an editor remounts with the same `persistenceKey` while the previous one is still saving. ([#10752](https://github.com/tldraw/tldraw/pull/10752))
28
- - Fix store changes made in the last frame before a listener is removed never reaching that listener. ([#10802](https://github.com/tldraw/tldraw/pull/10802))
29
- - Fix a reaction that writes to the store re-running on every unrelated store change. ([#10236](https://github.com/tldraw/tldraw/pull/10236))
30
- - Fix undo restoring an in-between state after a single `put` updated the same record twice. ([#10203](https://github.com/tldraw/tldraw/pull/10203))
31
- - Fix `migrateStoreSnapshot` throwing on a malformed persisted schema instead of returning a migration error. ([#10220](https://github.com/tldraw/tldraw/pull/10220))
32
- - Fix migrations rerunning on records when a persisted schema's sequence version isn't a number. ([#10917](https://github.com/tldraw/tldraw/pull/10917))
33
- - Fix a resumed sync session on a hibernating host skipping the version checks made on first connect, so after a server redeploy a client could be sent changes its schema couldn't handle. It's now rejected with `SERVER_TOO_OLD` or `CLIENT_TOO_OLD`, as on connect. ([#10122](https://github.com/tldraw/tldraw/pull/10122))
34
- - Fix a record field set to `undefined` getting the sync session kicked with `INVALID_RECORD`, and ignore `__proto__` keys in incoming changes. ([#10118](https://github.com/tldraw/tldraw/pull/10118))
35
- - Fix older sync clients (protocol version 7) silently falling out of sync when the server sent them text-append changes they don't support. ([#10095](https://github.com/tldraw/tldraw/pull/10095))
36
-
37
7
  ## v5.5.0
38
8
 
39
9
  [View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v5.5.0)
@@ -992,7 +962,7 @@ Note shapes now track and display a "first edited by" attribution label in the b
992
962
  <details>
993
963
  <summary>Migration guide</summary>
994
964
 
995
- **`useTldrawUser` has been removed.** Previously it bundled user *preferences* (color, color scheme) and user *identity* (id, name) into a single object. These are now separate concerns:
965
+ **`useTldrawUser` has been removed.** Previously it bundled user _preferences_ (color, color scheme) and user _identity_ (id, name) into a single object. These are now separate concerns:
996
966
 
997
967
  - **Preferences** (color, color scheme, snap mode, etc.) are managed via `TLUserPreferences`. Set them with the editor's user-preferences API directly.
998
968
  - **Identity** for attribution comes from a `TLUserStore` provider passed to the new `users` prop on `<Tldraw>`.
@@ -1628,7 +1598,7 @@ See the [Custom Shapes Guide](https://tldraw.dev/docs/shapes#Custom-shapes) and
1628
1598
  **Use `as const` on the static `type` field.** TypeScript may widen `static override type = 'my-shape'` to `string`, which fails the new constraint. Add `as const` to keep it a string literal:
1629
1599
 
1630
1600
  ```ts
1631
- class MyShapeUtil extends ShapeUtil`<MyShape>` {
1601
+ class MyShapeUtil extends (ShapeUtil`<MyShape>`) {
1632
1602
  static override type = 'my-shape' as const
1633
1603
  // ...
1634
1604
  }
@@ -1870,9 +1840,9 @@ We've upgraded TipTap from v2 to v3. If you've done any customization to our sta
1870
1840
  <details>
1871
1841
  <summary>Migration guide</summary>
1872
1842
 
1873
- Starting in v4.2, tldraw bundles TipTap v3 as a transitive dependency. If your project pins `@tiptap/*` v2 packages directly, upgrade them to v3 *before* trying to fix any code-level errors — leaving v2 in place produces a dual install that surfaces as confusing type errors.
1843
+ Starting in v4.2, tldraw bundles TipTap v3 as a transitive dependency. If your project pins `@tiptap/*` v2 packages directly, upgrade them to v3 _before_ trying to fix any code-level errors — leaving v2 in place produces a dual install that surfaces as confusing type errors.
1874
1844
 
1875
- **Dual-install symptom.** Deeply-nested errors like *"Property 'getCharacterCount' is missing in type 'Editor' but required in type 'Editor'"*, with the two `Editor` types resolving to different paths (one under `node_modules/@tiptap/core`, the other under `node_modules/@tldraw/editor/node_modules/@tiptap/core`), are not API changes — they are the v2/v3 dual install. The `Editor` classes are structurally similar but nominally different. Finishing the v3 upgrade collapses the duplicate and these errors disappear without code changes.
1845
+ **Dual-install symptom.** Deeply-nested errors like _"Property 'getCharacterCount' is missing in type 'Editor' but required in type 'Editor'"_, with the two `Editor` types resolving to different paths (one under `node_modules/@tiptap/core`, the other under `node_modules/@tldraw/editor/node_modules/@tiptap/core`), are not API changes — they are the v2/v3 dual install. The `Editor` classes are structurally similar but nominally different. Finishing the v3 upgrade collapses the duplicate and these errors disappear without code changes.
1876
1846
 
1877
1847
  **Code-level changes that affect tldraw users:**
1878
1848
 
@@ -1893,7 +1863,9 @@ Starting in v4.2, tldraw bundles TipTap v3 as a transitive dependency. If your p
1893
1863
  ```ts
1894
1864
  import { EditorEvents } from '@tiptap/core'
1895
1865
  // ...
1896
- onTransaction: (props: EditorEvents['transaction']) => { /* ... */ }
1866
+ onTransaction: (props: EditorEvents['transaction']) => {
1867
+ /* ... */
1868
+ }
1897
1869
  ```
1898
1870
 
1899
1871
  - **Custom chained commands** still register via `declare module '@tiptap/core'` augmentation in v3 — the augmentation target is the same.
@@ -26,10 +26,9 @@ import { getSchema } from '@tiptap/core';
26
26
  import { Group2d } from '@tldraw/editor';
27
27
  import { HandleSnapGeometry } from '@tldraw/editor';
28
28
  import { HTMLAttributes } from 'react';
29
- import { IndexKey } from '@tldraw/utils';
30
- import { IndexKey as IndexKey_2 } from '@tldraw/editor';
29
+ import { IndexKey } from '@tldraw/editor';
31
30
  import { JSONContent } from '@tiptap/core';
32
- import { JsonObject } from '@tldraw/utils';
31
+ import { JsonObject } from '@tldraw/editor';
33
32
  import { JSX } from 'react/jsx-runtime';
34
33
  import { JSXElementConstructor } from 'react';
35
34
  import { LANGUAGES } from '@tldraw/editor';
@@ -174,7 +173,7 @@ import { TLUserId } from '@tldraw/editor';
174
173
  import { TLVideoAsset } from '@tldraw/editor';
175
174
  import { TLVideoShape } from '@tldraw/editor';
176
175
  import { UnknownRecord } from '@tldraw/editor';
177
- import { Validator } from '@tldraw/validate';
176
+ import { Validator } from '@tldraw/editor';
178
177
  import { Vec } from '@tldraw/editor';
179
178
  import { VecLike } from '@tldraw/editor';
180
179
  import { VecModel } from '@tldraw/editor';
@@ -3065,7 +3064,7 @@ export declare class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
3065
3064
  toSvg(shape: TLNoteShape, ctx: SvgExportContext): JSX.Element;
3066
3065
  onBeforeCreate(next: TLNoteShape): {
3067
3066
  id: TLShapeId;
3068
- index: IndexKey_2;
3067
+ index: IndexKey;
3069
3068
  isLocked: boolean;
3070
3069
  meta: JsonObject;
3071
3070
  opacity: TLOpacityType;
@@ -5336,7 +5335,7 @@ export declare interface TLUiEventMap {
5336
5335
  'video-replace': null;
5337
5336
  'open-kbd-shortcuts': null;
5338
5337
  'rich-text': {
5339
- operation: 'bold' | 'bulletList' | 'heading' | 'link-edit' | 'link-remove' | 'link-visit' | 'link' | 'strike';
5338
+ operation: 'bold' | 'bulletList' | 'code' | 'heading' | 'highlight' | 'italic' | 'link-edit' | 'link-remove' | 'link-visit' | 'link' | 'strike';
5340
5339
  };
5341
5340
  edit: null;
5342
5341
  'copy-hovered-styles': null;
@@ -5802,7 +5801,7 @@ export declare interface TLUiTranslation {
5802
5801
  export declare type TLUiTranslationContextType = TLUiTranslation;
5803
5802
 
5804
5803
  /** @public */
5805
- export declare type TLUiTranslationKey = 'a11y.adjust-shape-styles' | 'a11y.enlarge-shape' | 'a11y.enter-leave-container' | 'a11y.move-shape-faster' | 'a11y.move-shape' | 'a11y.multiple-shapes' | 'a11y.open-context-menu' | 'a11y.open-keyboard-shortcuts' | 'a11y.pan-camera' | 'a11y.repeat-shape' | 'a11y.rotate-shape-ccw-fine' | 'a11y.rotate-shape-ccw' | 'a11y.rotate-shape-cw-fine' | 'a11y.rotate-shape-cw' | 'a11y.select-shape-direction' | 'a11y.select-shape' | 'a11y.shape-image' | 'a11y.shape-index' | 'a11y.shape-video' | 'a11y.shrink-shape' | 'a11y.skip-to-main-content' | 'a11y.status' | 'action.align-bottom' | 'action.align-center-horizontal.short' | 'action.align-center-horizontal' | 'action.align-center-vertical.short' | 'action.align-center-vertical' | 'action.align-left' | 'action.align-right' | 'action.align-top' | 'action.back-to-content' | 'action.bring-forward' | 'action.bring-to-front' | 'action.convert-to-bookmark' | 'action.convert-to-embed' | 'action.copy-as-json.short' | 'action.copy-as-json' | 'action.copy-as-png.short' | 'action.copy-as-png' | 'action.copy-as-svg.short' | 'action.copy-as-svg' | 'action.copy-hovered-styles' | 'action.copy' | 'action.cut' | 'action.delete' | 'action.distribute-horizontal.short' | 'action.distribute-horizontal' | 'action.distribute-vertical.short' | 'action.distribute-vertical' | 'action.download-original' | 'action.duplicate' | 'action.edit-link' | 'action.enhanced-a11y-mode.menu' | 'action.enhanced-a11y-mode' | 'action.exit-pen-mode' | 'action.export-all-as-png.short' | 'action.export-all-as-png' | 'action.export-all-as-svg.short' | 'action.export-all-as-svg' | 'action.export-as-png.short' | 'action.export-as-png' | 'action.export-as-svg.short' | 'action.export-as-svg' | 'action.fit-frame-to-content' | 'action.flatten-to-image' | 'action.flip-horizontal.short' | 'action.flip-horizontal' | 'action.flip-vertical.short' | 'action.flip-vertical' | 'action.fork-project-on-tldraw' | 'action.fork-project' | 'action.frame-selection' | 'action.group' | 'action.insert-embed' | 'action.insert-media' | 'action.leave-shared-project' | 'action.new-project' | 'action.new-shared-project' | 'action.open-cursor-chat' | 'action.open-embed-link' | 'action.open-file' | 'action.open-kbd-shortcuts' | 'action.pack' | 'action.paste-error-description' | 'action.paste-error-title' | 'action.paste' | 'action.print' | 'action.redo' | 'action.remove-frame' | 'action.rename' | 'action.rotate-ccw' | 'action.rotate-cw' | 'action.save-copy' | 'action.select-all' | 'action.select-none' | 'action.select-zoom-tool' | 'action.send-backward' | 'action.send-to-back' | 'action.share-project' | 'action.stack-horizontal.short' | 'action.stack-horizontal' | 'action.stack-vertical.short' | 'action.stack-vertical' | 'action.stop-following' | 'action.stretch-horizontal.short' | 'action.stretch-horizontal' | 'action.stretch-vertical.short' | 'action.stretch-vertical' | 'action.toggle-auto-none' | 'action.toggle-auto-pan' | 'action.toggle-auto-size' | 'action.toggle-auto-zoom' | 'action.toggle-dark-mode.menu' | 'action.toggle-dark-mode' | 'action.toggle-debug-mode.menu' | 'action.toggle-debug-mode' | 'action.toggle-dynamic-size-mode.menu' | 'action.toggle-dynamic-size-mode' | 'action.toggle-edge-scrolling.menu' | 'action.toggle-edge-scrolling' | 'action.toggle-focus-mode.menu' | 'action.toggle-focus-mode' | 'action.toggle-grid.menu' | 'action.toggle-grid' | 'action.toggle-invert-zoom.menu' | 'action.toggle-invert-zoom' | 'action.toggle-keyboard-shortcuts.menu' | 'action.toggle-keyboard-shortcuts' | 'action.toggle-lock' | 'action.toggle-mouse' | 'action.toggle-paste-at-cursor.menu' | 'action.toggle-paste-at-cursor' | 'action.toggle-reduce-motion.menu' | 'action.toggle-reduce-motion' | 'action.toggle-snap-mode.menu' | 'action.toggle-snap-mode' | 'action.toggle-tool-lock.menu' | 'action.toggle-tool-lock' | 'action.toggle-trackpad' | 'action.toggle-transparent.context-menu' | 'action.toggle-transparent.menu' | 'action.toggle-transparent' | 'action.toggle-wrap-mode.menu' | 'action.toggle-wrap-mode' | 'action.undo' | 'action.ungroup' | 'action.unlock-all' | 'action.zoom-in' | 'action.zoom-out' | 'action.zoom-quick' | 'action.zoom-to-100' | 'action.zoom-to-fit' | 'action.zoom-to-selection' | 'actions-menu.title' | 'align-style.end' | 'align-style.justify' | 'align-style.middle' | 'align-style.start' | 'app.loading' | 'arrow-kind-style.arc' | 'arrow-kind-style.elbow' | 'arrowheadEnd-style.arrow' | 'arrowheadEnd-style.bar' | 'arrowheadEnd-style.diamond' | 'arrowheadEnd-style.dot' | 'arrowheadEnd-style.inverted' | 'arrowheadEnd-style.none' | 'arrowheadEnd-style.pipe' | 'arrowheadEnd-style.square' | 'arrowheadEnd-style.triangle' | 'arrowheadStart-style.arrow' | 'arrowheadStart-style.bar' | 'arrowheadStart-style.diamond' | 'arrowheadStart-style.dot' | 'arrowheadStart-style.inverted' | 'arrowheadStart-style.none' | 'arrowheadStart-style.pipe' | 'arrowheadStart-style.square' | 'arrowheadStart-style.triangle' | 'assets.files.amount-too-many' | 'assets.files.maximum-size' | 'assets.files.size-too-big' | 'assets.files.type-not-allowed' | 'assets.files.upload-failed' | 'assets.url.failed' | 'color-style.black' | 'color-style.blue' | 'color-style.green' | 'color-style.grey' | 'color-style.light-blue' | 'color-style.light-green' | 'color-style.light-red' | 'color-style.light-violet' | 'color-style.orange' | 'color-style.red' | 'color-style.violet' | 'color-style.white' | 'color-style.yellow' | 'comments.add-placeholder' | 'comments.add-reaction' | 'comments.cluster-label' | 'comments.copy-link' | 'comments.delete-comment' | 'comments.delete' | 'comments.dismiss' | 'comments.edit-comment' | 'comments.edit-placeholder' | 'comments.edit' | 'comments.empty-filtered' | 'comments.empty' | 'comments.filter' | 'comments.hide' | 'comments.link-copied' | 'comments.mention-no-matches' | 'comments.mention-you' | 'comments.more-options' | 'comments.only-my-comments' | 'comments.only-unread' | 'comments.pin-label-resolved' | 'comments.pin-label' | 'comments.preview-more' | 'comments.reacted-1' | 'comments.reacted-2' | 'comments.reacted-3' | 'comments.reacted-more-one' | 'comments.reacted-more' | 'comments.reopen' | 'comments.replies-one' | 'comments.replies' | 'comments.reply-placeholder' | 'comments.resolve' | 'comments.resolved-by' | 'comments.resolved' | 'comments.save' | 'comments.send' | 'comments.show-all-pages' | 'comments.show-resolved' | 'comments.show' | 'comments.stack-label' | 'comments.thread-title' | 'comments.title' | 'context-menu.arrange' | 'context-menu.copy-as' | 'context-menu.edit' | 'context-menu.export-all-as' | 'context-menu.export-as' | 'context-menu.move-to-page' | 'context-menu.reorder' | 'context-menu.title' | 'context.pages.new-page' | 'cursor-chat.type-to-chat' | 'dash-style.dashed' | 'dash-style.dotted' | 'dash-style.draw' | 'dash-style.solid' | 'document-name-menu.copy-link' | 'document.default-name' | 'edit-link-dialog.cancel' | 'edit-link-dialog.clear' | 'edit-link-dialog.detail' | 'edit-link-dialog.external-link' | 'edit-link-dialog.invalid-url' | 'edit-link-dialog.save' | 'edit-link-dialog.title' | 'edit-link-dialog.url' | 'embed-dialog.back' | 'embed-dialog.cancel' | 'embed-dialog.create' | 'embed-dialog.instruction' | 'embed-dialog.invalid-url' | 'embed-dialog.title' | 'embed-dialog.url' | 'file-system.confirm-clear.cancel' | 'file-system.confirm-clear.continue' | 'file-system.confirm-clear.description' | 'file-system.confirm-clear.dont-show-again' | 'file-system.confirm-clear.title' | 'file-system.confirm-open.cancel' | 'file-system.confirm-open.description' | 'file-system.confirm-open.dont-show-again' | 'file-system.confirm-open.open' | 'file-system.confirm-open.title' | 'file-system.file-open-error.file-format-version-too-new' | 'file-system.file-open-error.generic-corrupted-file' | 'file-system.file-open-error.not-a-tldraw-file' | 'file-system.file-open-error.title' | 'file-system.shared-document-file-open-error.description' | 'file-system.shared-document-file-open-error.title' | 'fill-style.fill' | 'fill-style.lined-fill' | 'fill-style.none' | 'fill-style.pattern' | 'fill-style.semi' | 'fill-style.solid' | 'focus-mode.toggle-focus-mode' | 'font-style.draw' | 'font-style.mono' | 'font-style.sans' | 'font-style.serif' | 'geo-style.arrow-down' | 'geo-style.arrow-left' | 'geo-style.arrow-right' | 'geo-style.arrow-up' | 'geo-style.check-box' | 'geo-style.cloud' | 'geo-style.diamond' | 'geo-style.ellipse' | 'geo-style.heart' | 'geo-style.hexagon' | 'geo-style.octagon' | 'geo-style.oval' | 'geo-style.pentagon' | 'geo-style.rectangle' | 'geo-style.rhombus-2' | 'geo-style.rhombus' | 'geo-style.star' | 'geo-style.trapezoid' | 'geo-style.triangle' | 'geo-style.x-box' | 'handle.crop.bottom-left' | 'handle.crop.bottom-right' | 'handle.crop.bottom' | 'handle.crop.left' | 'handle.crop.right' | 'handle.crop.top-left' | 'handle.crop.top-right' | 'handle.crop.top' | 'handle.resize-bottom-left' | 'handle.resize-bottom-right' | 'handle.resize-bottom' | 'handle.resize-left' | 'handle.resize-right' | 'handle.resize-top-left' | 'handle.resize-top-right' | 'handle.resize-top' | 'handle.rotate.bottom_left_rotate' | 'handle.rotate.bottom_right_rotate' | 'handle.rotate.mobile_rotate' | 'handle.rotate.top_left_rotate' | 'handle.rotate.top_right_rotate' | 'help-menu.about' | 'help-menu.discord' | 'help-menu.github' | 'help-menu.import-tldr-file' | 'help-menu.keyboard-shortcuts' | 'help-menu.privacy' | 'help-menu.terms' | 'help-menu.title' | 'help-menu.twitter' | 'menu.accessibility' | 'menu.color-theme' | 'menu.copy-as' | 'menu.edit' | 'menu.export-as' | 'menu.file' | 'menu.input-device' | 'menu.language' | 'menu.preferences' | 'menu.theme' | 'menu.title' | 'menu.view' | 'navigation-zone.minimap' | 'navigation-zone.title' | 'navigation-zone.toggle-minimap' | 'navigation-zone.zoom' | 'opacity-style.0.1' | 'opacity-style.0.25' | 'opacity-style.0.5' | 'opacity-style.0.75' | 'opacity-style.1' | 'page-menu.create-new-page' | 'page-menu.edit-done' | 'page-menu.edit-start' | 'page-menu.go-to-page' | 'page-menu.max-pages-reached' | 'page-menu.new-page-initial-name' | 'page-menu.resize' | 'page-menu.submenu.delete' | 'page-menu.submenu.duplicate-page' | 'page-menu.submenu.move-down' | 'page-menu.submenu.move-up' | 'page-menu.submenu.rename' | 'page-menu.submenu.title' | 'page-menu.title' | 'people-menu.anonymous-user' | 'people-menu.avatar-color' | 'people-menu.change-color' | 'people-menu.change-name' | 'people-menu.follow' | 'people-menu.following' | 'people-menu.invite' | 'people-menu.leading' | 'people-menu.title' | 'people-menu.user' | 'share-menu.copied' | 'share-menu.copy-link-note' | 'share-menu.copy-link' | 'share-menu.copy-readonly-link-note' | 'share-menu.copy-readonly-link' | 'share-menu.create-snapshot-link' | 'share-menu.creating-project' | 'share-menu.fork-note' | 'share-menu.offline-note' | 'share-menu.project-too-large' | 'share-menu.save-note' | 'share-menu.share-project' | 'share-menu.snapshot-link-note' | 'share-menu.title' | 'share-menu.upload-failed' | 'sharing.confirm-leave.cancel' | 'sharing.confirm-leave.description' | 'sharing.confirm-leave.dont-show-again' | 'sharing.confirm-leave.leave' | 'sharing.confirm-leave.title' | 'shortcuts-dialog.a11y' | 'shortcuts-dialog.collaboration' | 'shortcuts-dialog.disabled-notice' | 'shortcuts-dialog.edit' | 'shortcuts-dialog.file' | 'shortcuts-dialog.preferences' | 'shortcuts-dialog.text-formatting' | 'shortcuts-dialog.title' | 'shortcuts-dialog.tools' | 'shortcuts-dialog.transform' | 'shortcuts-dialog.view' | 'size-style.l' | 'size-style.m' | 'size-style.s' | 'size-style.xl' | 'spline-style.cubic' | 'spline-style.line' | 'status.offline' | 'style-panel.align' | 'style-panel.arrow-kind' | 'style-panel.arrowhead-end' | 'style-panel.arrowhead-start' | 'style-panel.arrowheads' | 'style-panel.color' | 'style-panel.dash' | 'style-panel.fill' | 'style-panel.font' | 'style-panel.geo' | 'style-panel.label-align' | 'style-panel.mixed' | 'style-panel.opacity' | 'style-panel.position' | 'style-panel.selected' | 'style-panel.size' | 'style-panel.spline' | 'style-panel.title' | 'style-panel.vertical-align' | 'theme.dark' | 'theme.light' | 'theme.system' | 'toast.close' | 'toast.error.copy-fail.desc' | 'toast.error.copy-fail.title' | 'toast.error.export-fail.desc' | 'toast.error.export-fail.title' | 'toast.error' | 'toast.info' | 'toast.success' | 'toast.warning' | 'tool-panel.more' | 'tool-panel.title' | 'tool.arrow-down' | 'tool.arrow-left' | 'tool.arrow-right' | 'tool.arrow-up' | 'tool.arrow' | 'tool.aspect-ratio.circle' | 'tool.aspect-ratio.landscape' | 'tool.aspect-ratio.original' | 'tool.aspect-ratio.portrait' | 'tool.aspect-ratio.square' | 'tool.aspect-ratio.wide' | 'tool.aspect-ratio' | 'tool.bookmark' | 'tool.check-box' | 'tool.cloud' | 'tool.diamond' | 'tool.draw' | 'tool.ellipse' | 'tool.embed' | 'tool.eraser' | 'tool.flip-horz' | 'tool.flip-vert' | 'tool.frame' | 'tool.hand' | 'tool.heart' | 'tool.hexagon' | 'tool.highlight' | 'tool.image-crop-confirm' | 'tool.image-crop' | 'tool.image-toolbar-title' | 'tool.image-zoom' | 'tool.laser' | 'tool.line' | 'tool.media-alt-text-confirm' | 'tool.media-alt-text-desc' | 'tool.media-alt-text' | 'tool.media' | 'tool.note' | 'tool.octagon' | 'tool.oval' | 'tool.pentagon' | 'tool.pointer-down' | 'tool.rectangle' | 'tool.replace-media' | 'tool.rhombus' | 'tool.rich-text-bold' | 'tool.rich-text-bulletList' | 'tool.rich-text-code' | 'tool.rich-text-header' | 'tool.rich-text-highlight' | 'tool.rich-text-italic' | 'tool.rich-text-link-remove' | 'tool.rich-text-link-visit' | 'tool.rich-text-link' | 'tool.rich-text-orderedList' | 'tool.rich-text-strikethrough' | 'tool.rich-text-toolbar-title' | 'tool.rotate-cw' | 'tool.select' | 'tool.star' | 'tool.text' | 'tool.trapezoid' | 'tool.triangle' | 'tool.x-box' | 'ui.checked' | 'ui.close' | 'ui.unchecked' | 'verticalAlign-style.end' | 'verticalAlign-style.middle' | 'verticalAlign-style.start' | 'vscode.file-open.backup-failed' | 'vscode.file-open.backup-saved' | 'vscode.file-open.backup' | 'vscode.file-open.desc' | 'vscode.file-open.dont-show-again' | 'vscode.file-open.open';
5804
+ export declare type TLUiTranslationKey = 'a11y.adjust-shape-styles' | 'a11y.enlarge-shape' | 'a11y.enter-leave-container' | 'a11y.move-shape-faster' | 'a11y.move-shape' | 'a11y.multiple-shapes' | 'a11y.open-context-menu' | 'a11y.open-keyboard-shortcuts' | 'a11y.pan-camera' | 'a11y.repeat-shape' | 'a11y.rotate-shape-ccw-fine' | 'a11y.rotate-shape-ccw' | 'a11y.rotate-shape-cw-fine' | 'a11y.rotate-shape-cw' | 'a11y.select-shape-direction' | 'a11y.select-shape' | 'a11y.shape-image' | 'a11y.shape-index' | 'a11y.shape-video' | 'a11y.shrink-shape' | 'a11y.skip-to-main-content' | 'a11y.status' | 'action.align-bottom' | 'action.align-center-horizontal.short' | 'action.align-center-horizontal' | 'action.align-center-vertical.short' | 'action.align-center-vertical' | 'action.align-left' | 'action.align-right' | 'action.align-top' | 'action.back-to-content' | 'action.bring-forward' | 'action.bring-to-front' | 'action.convert-to-bookmark' | 'action.convert-to-embed' | 'action.copy-as-json.short' | 'action.copy-as-json' | 'action.copy-as-png.short' | 'action.copy-as-png' | 'action.copy-as-svg.short' | 'action.copy-as-svg' | 'action.copy-hovered-styles' | 'action.copy' | 'action.cut' | 'action.delete' | 'action.distribute-horizontal.short' | 'action.distribute-horizontal' | 'action.distribute-vertical.short' | 'action.distribute-vertical' | 'action.download-original' | 'action.duplicate' | 'action.edit-link' | 'action.enhanced-a11y-mode.menu' | 'action.enhanced-a11y-mode' | 'action.exit-pen-mode' | 'action.export-all-as-png.short' | 'action.export-all-as-png' | 'action.export-all-as-svg.short' | 'action.export-all-as-svg' | 'action.export-as-png.short' | 'action.export-as-png' | 'action.export-as-svg.short' | 'action.export-as-svg' | 'action.fit-frame-to-content' | 'action.flatten-to-image' | 'action.flip-horizontal.short' | 'action.flip-horizontal' | 'action.flip-vertical.short' | 'action.flip-vertical' | 'action.fork-project-on-tldraw' | 'action.fork-project' | 'action.frame-selection' | 'action.group' | 'action.insert-embed' | 'action.insert-media' | 'action.leave-shared-project' | 'action.new-project' | 'action.new-shared-project' | 'action.open-cursor-chat' | 'action.open-embed-link' | 'action.open-file' | 'action.open-kbd-shortcuts' | 'action.pack' | 'action.paste-error-description' | 'action.paste-error-title' | 'action.paste' | 'action.print' | 'action.redo' | 'action.remove-frame' | 'action.rename' | 'action.rotate-ccw' | 'action.rotate-cw' | 'action.save-copy' | 'action.select-all' | 'action.select-none' | 'action.select-zoom-tool' | 'action.send-backward' | 'action.send-to-back' | 'action.share-project' | 'action.stack-horizontal.short' | 'action.stack-horizontal' | 'action.stack-vertical.short' | 'action.stack-vertical' | 'action.stop-following' | 'action.stretch-horizontal.short' | 'action.stretch-horizontal' | 'action.stretch-vertical.short' | 'action.stretch-vertical' | 'action.toggle-auto-none' | 'action.toggle-auto-pan' | 'action.toggle-auto-size' | 'action.toggle-auto-zoom' | 'action.toggle-dark-mode.menu' | 'action.toggle-dark-mode' | 'action.toggle-debug-mode.menu' | 'action.toggle-debug-mode' | 'action.toggle-dynamic-size-mode.menu' | 'action.toggle-dynamic-size-mode' | 'action.toggle-edge-scrolling.menu' | 'action.toggle-edge-scrolling' | 'action.toggle-focus-mode.menu' | 'action.toggle-focus-mode' | 'action.toggle-grid.menu' | 'action.toggle-grid' | 'action.toggle-invert-zoom.menu' | 'action.toggle-invert-zoom' | 'action.toggle-keyboard-shortcuts.menu' | 'action.toggle-keyboard-shortcuts' | 'action.toggle-lock' | 'action.toggle-mouse' | 'action.toggle-paste-at-cursor.menu' | 'action.toggle-paste-at-cursor' | 'action.toggle-reduce-motion.menu' | 'action.toggle-reduce-motion' | 'action.toggle-snap-mode.menu' | 'action.toggle-snap-mode' | 'action.toggle-tool-lock.menu' | 'action.toggle-tool-lock' | 'action.toggle-trackpad' | 'action.toggle-transparent.context-menu' | 'action.toggle-transparent.menu' | 'action.toggle-transparent' | 'action.toggle-wrap-mode.menu' | 'action.toggle-wrap-mode' | 'action.undo' | 'action.ungroup' | 'action.unlock-all' | 'action.zoom-in' | 'action.zoom-out' | 'action.zoom-quick' | 'action.zoom-to-100' | 'action.zoom-to-fit' | 'action.zoom-to-selection' | 'actions-menu.title' | 'align-style.end' | 'align-style.justify' | 'align-style.middle' | 'align-style.start' | 'app.loading' | 'arrow-kind-style.arc' | 'arrow-kind-style.elbow' | 'arrowheadEnd-style.arrow' | 'arrowheadEnd-style.bar' | 'arrowheadEnd-style.diamond' | 'arrowheadEnd-style.dot' | 'arrowheadEnd-style.inverted' | 'arrowheadEnd-style.none' | 'arrowheadEnd-style.pipe' | 'arrowheadEnd-style.square' | 'arrowheadEnd-style.triangle' | 'arrowheadStart-style.arrow' | 'arrowheadStart-style.bar' | 'arrowheadStart-style.diamond' | 'arrowheadStart-style.dot' | 'arrowheadStart-style.inverted' | 'arrowheadStart-style.none' | 'arrowheadStart-style.pipe' | 'arrowheadStart-style.square' | 'arrowheadStart-style.triangle' | 'assets.files.amount-too-many' | 'assets.files.maximum-size' | 'assets.files.size-too-big' | 'assets.files.type-not-allowed' | 'assets.files.upload-failed' | 'assets.url.failed' | 'color-style.black' | 'color-style.blue' | 'color-style.green' | 'color-style.grey' | 'color-style.light-blue' | 'color-style.light-green' | 'color-style.light-red' | 'color-style.light-violet' | 'color-style.orange' | 'color-style.red' | 'color-style.violet' | 'color-style.white' | 'color-style.yellow' | 'comments.add-placeholder' | 'comments.add-reaction' | 'comments.cluster-label' | 'comments.copy-link' | 'comments.delete-comment' | 'comments.delete' | 'comments.dismiss' | 'comments.edit-comment' | 'comments.edit-placeholder' | 'comments.edit' | 'comments.empty-filtered' | 'comments.empty' | 'comments.filter' | 'comments.hide' | 'comments.link-copied' | 'comments.mention-no-matches' | 'comments.mention-you' | 'comments.more-options' | 'comments.only-my-comments' | 'comments.only-unread' | 'comments.pin-label-resolved' | 'comments.pin-label' | 'comments.preview-more' | 'comments.reacted-1' | 'comments.reacted-2' | 'comments.reacted-3' | 'comments.reacted-more-one' | 'comments.reacted-more' | 'comments.reopen' | 'comments.replies-one' | 'comments.replies' | 'comments.reply-placeholder' | 'comments.resolve' | 'comments.resolved-by' | 'comments.resolved' | 'comments.save' | 'comments.send' | 'comments.show-all-pages' | 'comments.show-resolved' | 'comments.show' | 'comments.stack-label' | 'comments.thread-title' | 'comments.title' | 'context-menu.arrange' | 'context-menu.copy-as' | 'context-menu.edit' | 'context-menu.export-all-as' | 'context-menu.export-as' | 'context-menu.move-to-page' | 'context-menu.reorder' | 'context-menu.title' | 'context.pages.new-page' | 'cursor-chat.type-to-chat' | 'dash-style.dashed' | 'dash-style.dotted' | 'dash-style.draw' | 'dash-style.solid' | 'document-name-menu.copy-link' | 'document.default-name' | 'edit-link-dialog.cancel' | 'edit-link-dialog.clear' | 'edit-link-dialog.detail' | 'edit-link-dialog.external-link' | 'edit-link-dialog.invalid-url' | 'edit-link-dialog.save' | 'edit-link-dialog.title' | 'edit-link-dialog.url' | 'embed-dialog.back' | 'embed-dialog.cancel' | 'embed-dialog.create' | 'embed-dialog.instruction' | 'embed-dialog.invalid-url' | 'embed-dialog.title' | 'embed-dialog.url' | 'file-system.confirm-clear.cancel' | 'file-system.confirm-clear.continue' | 'file-system.confirm-clear.description' | 'file-system.confirm-clear.dont-show-again' | 'file-system.confirm-clear.title' | 'file-system.confirm-open.cancel' | 'file-system.confirm-open.description' | 'file-system.confirm-open.dont-show-again' | 'file-system.confirm-open.open' | 'file-system.confirm-open.title' | 'file-system.file-open-error.file-format-version-too-new' | 'file-system.file-open-error.generic-corrupted-file' | 'file-system.file-open-error.not-a-tldraw-file' | 'file-system.file-open-error.title' | 'file-system.shared-document-file-open-error.description' | 'file-system.shared-document-file-open-error.title' | 'fill-style.fill' | 'fill-style.lined-fill' | 'fill-style.none' | 'fill-style.pattern' | 'fill-style.semi' | 'fill-style.solid' | 'focus-mode.toggle-focus-mode' | 'font-style.draw' | 'font-style.mono' | 'font-style.sans' | 'font-style.serif' | 'geo-style.arrow-down' | 'geo-style.arrow-left' | 'geo-style.arrow-right' | 'geo-style.arrow-up' | 'geo-style.check-box' | 'geo-style.cloud' | 'geo-style.diamond' | 'geo-style.ellipse' | 'geo-style.heart' | 'geo-style.hexagon' | 'geo-style.octagon' | 'geo-style.oval' | 'geo-style.pentagon' | 'geo-style.rectangle' | 'geo-style.rhombus-2' | 'geo-style.rhombus' | 'geo-style.star' | 'geo-style.trapezoid' | 'geo-style.triangle' | 'geo-style.x-box' | 'handle.crop.bottom-left' | 'handle.crop.bottom-right' | 'handle.crop.bottom' | 'handle.crop.left' | 'handle.crop.right' | 'handle.crop.top-left' | 'handle.crop.top-right' | 'handle.crop.top' | 'handle.resize-bottom-left' | 'handle.resize-bottom-right' | 'handle.resize-bottom' | 'handle.resize-left' | 'handle.resize-right' | 'handle.resize-top-left' | 'handle.resize-top-right' | 'handle.resize-top' | 'handle.rotate.bottom_left_rotate' | 'handle.rotate.bottom_right_rotate' | 'handle.rotate.mobile_rotate' | 'handle.rotate.top_left_rotate' | 'handle.rotate.top_right_rotate' | 'help-menu.about' | 'help-menu.discord' | 'help-menu.github' | 'help-menu.import-tldr-file' | 'help-menu.keyboard-shortcuts' | 'help-menu.privacy' | 'help-menu.terms' | 'help-menu.title' | 'help-menu.twitter' | 'menu.accessibility' | 'menu.color-theme' | 'menu.copy-as' | 'menu.edit' | 'menu.export-as' | 'menu.file' | 'menu.input-device' | 'menu.language' | 'menu.preferences' | 'menu.theme' | 'menu.title' | 'menu.view' | 'navigation-zone.minimap' | 'navigation-zone.title' | 'navigation-zone.toggle-minimap' | 'navigation-zone.zoom' | 'opacity-style.0.1' | 'opacity-style.0.25' | 'opacity-style.0.5' | 'opacity-style.0.75' | 'opacity-style.1' | 'page-menu.create-new-page' | 'page-menu.edit-done' | 'page-menu.edit-start' | 'page-menu.go-to-page' | 'page-menu.max-pages-reached' | 'page-menu.new-page-initial-name' | 'page-menu.resize' | 'page-menu.submenu.delete' | 'page-menu.submenu.duplicate-page' | 'page-menu.submenu.move-down' | 'page-menu.submenu.move-up' | 'page-menu.submenu.rename' | 'page-menu.submenu.title' | 'page-menu.title' | 'people-menu.anonymous-user' | 'people-menu.avatar-color' | 'people-menu.change-color' | 'people-menu.change-name' | 'people-menu.follow' | 'people-menu.following' | 'people-menu.invite' | 'people-menu.leading' | 'people-menu.title' | 'people-menu.user' | 'share-menu.copied' | 'share-menu.copy-link-note' | 'share-menu.copy-link' | 'share-menu.copy-readonly-link-note' | 'share-menu.copy-readonly-link' | 'share-menu.create-snapshot-link' | 'share-menu.creating-project' | 'share-menu.fork-note' | 'share-menu.offline-note' | 'share-menu.project-too-large' | 'share-menu.save-note' | 'share-menu.share-project' | 'share-menu.snapshot-link-note' | 'share-menu.title' | 'share-menu.upload-failed' | 'sharing.confirm-leave.cancel' | 'sharing.confirm-leave.description' | 'sharing.confirm-leave.dont-show-again' | 'sharing.confirm-leave.leave' | 'sharing.confirm-leave.title' | 'shortcuts-dialog.a11y' | 'shortcuts-dialog.collaboration' | 'shortcuts-dialog.disabled-notice' | 'shortcuts-dialog.edit' | 'shortcuts-dialog.file' | 'shortcuts-dialog.preferences' | 'shortcuts-dialog.text-formatting' | 'shortcuts-dialog.title' | 'shortcuts-dialog.tools' | 'shortcuts-dialog.transform' | 'shortcuts-dialog.view' | 'size-style.l' | 'size-style.m' | 'size-style.s' | 'size-style.xl' | 'spline-style.cubic' | 'spline-style.line' | 'status.offline' | 'style-panel.align' | 'style-panel.arrow-kind' | 'style-panel.arrowhead-end' | 'style-panel.arrowhead-start' | 'style-panel.arrowheads' | 'style-panel.color' | 'style-panel.dash' | 'style-panel.fill' | 'style-panel.font' | 'style-panel.geo' | 'style-panel.label-align' | 'style-panel.mixed' | 'style-panel.opacity' | 'style-panel.position' | 'style-panel.selected' | 'style-panel.size' | 'style-panel.spline' | 'style-panel.title' | 'style-panel.vertical-align' | 'theme.dark' | 'theme.light' | 'theme.system' | 'toast.close' | 'toast.error.copy-fail.desc' | 'toast.error.copy-fail.title' | 'toast.error.export-fail.desc' | 'toast.error.export-fail.title' | 'toast.error' | 'toast.info' | 'toast.success' | 'toast.warning' | 'tool-panel.more' | 'tool-panel.title' | 'tool.arrow-down' | 'tool.arrow-left' | 'tool.arrow-right' | 'tool.arrow-up' | 'tool.arrow' | 'tool.aspect-ratio.circle' | 'tool.aspect-ratio.landscape' | 'tool.aspect-ratio.original' | 'tool.aspect-ratio.portrait' | 'tool.aspect-ratio.square' | 'tool.aspect-ratio.wide' | 'tool.aspect-ratio' | 'tool.bookmark' | 'tool.check-box' | 'tool.cloud' | 'tool.diamond' | 'tool.draw' | 'tool.ellipse' | 'tool.embed' | 'tool.eraser' | 'tool.flip-horz' | 'tool.flip-vert' | 'tool.frame' | 'tool.hand' | 'tool.heart' | 'tool.hexagon' | 'tool.highlight' | 'tool.image-crop-confirm' | 'tool.image-crop' | 'tool.image-toolbar-title' | 'tool.image-zoom' | 'tool.laser' | 'tool.line' | 'tool.media-alt-text-confirm' | 'tool.media-alt-text-desc' | 'tool.media-alt-text' | 'tool.media' | 'tool.note' | 'tool.octagon' | 'tool.oval' | 'tool.pentagon' | 'tool.pointer-down' | 'tool.rectangle' | 'tool.replace-media' | 'tool.rhombus-2' | 'tool.rhombus' | 'tool.rich-text-bold' | 'tool.rich-text-bulletList' | 'tool.rich-text-code' | 'tool.rich-text-header' | 'tool.rich-text-highlight' | 'tool.rich-text-italic' | 'tool.rich-text-link-remove' | 'tool.rich-text-link-visit' | 'tool.rich-text-link' | 'tool.rich-text-orderedList' | 'tool.rich-text-strikethrough' | 'tool.rich-text-toolbar-title' | 'tool.rotate-cw' | 'tool.select' | 'tool.star' | 'tool.text' | 'tool.trapezoid' | 'tool.triangle' | 'tool.x-box' | 'ui.checked' | 'ui.close' | 'ui.unchecked' | 'verticalAlign-style.end' | 'verticalAlign-style.middle' | 'verticalAlign-style.start' | 'vscode.file-open.backup-failed' | 'vscode.file-open.backup-saved' | 'vscode.file-open.backup' | 'vscode.file-open.desc' | 'vscode.file-open.dont-show-again' | 'vscode.file-open.open';
5806
5805
 
5807
5806
  /** @public */
5808
5807
  export declare interface TLUiTranslationProviderProps {
package/dist-cjs/index.js CHANGED
@@ -666,7 +666,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
666
666
  var import_file = require("./lib/utils/tldr/file");
667
667
  (0, import_editor.registerTldrawLibraryVersion)(
668
668
  "tldraw",
669
- "5.5.2",
669
+ "5.6.0-canary.035b741dc331",
670
670
  "cjs"
671
671
  );
672
672
  //# sourceMappingURL=index.js.map
@@ -25,7 +25,7 @@ var import_editor = require("@tldraw/editor");
25
25
  var import_state = require("@tldraw/state");
26
26
  class ShapeIndicatorOverlayUtil extends import_editor.OverlayUtil {
27
27
  static type = "shape_indicator";
28
- options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 };
28
+ options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2 };
29
29
  // Narrow projection of instance state. Reading the full record would
30
30
  // re-fire getOverlays on every cursor move / brush update; gating on these
31
31
  // three booleans means we only re-fire when one of them actually flips.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/overlays/ShapeIndicatorOverlayUtil.ts"],
4
- "sourcesContent": ["import { OverlayUtil, strokeShapeIndicators, TLOverlay } from '@tldraw/editor'\nimport { computed } from '@tldraw/state'\nimport { TLShapeId } from '@tldraw/tlschema'\n\n/** @public */\nexport interface TLShapeIndicatorOverlay extends TLOverlay {\n\tprops: {\n\t\tidsToDisplay: TLShapeId[]\n\t\thintingShapeIds: TLShapeId[]\n\t}\n}\n\ninterface RelevantInstanceFlags {\n\tisChangingStyle: boolean\n\tisHoveringCanvas: boolean | null\n\tisCoarsePointer: boolean\n}\n\n/**\n * Overlay util for shape indicators \u2014 the selection / hover / hint outlines drawn\n * under the selection foreground. Paints local indicators in the theme's\n * selection color.\n *\n * Remote collaborator selection indicators are drawn by a separate overlay util\n * (e.g. `CollaboratorShapeIndicatorOverlayUtil` from `tldraw`) that runs at a\n * lower z-index so peer selections appear under the local indicators.\n *\n * Non-interactive: contributes no hit-test geometry.\n *\n * @public\n */\nexport class ShapeIndicatorOverlayUtil extends OverlayUtil<TLShapeIndicatorOverlay> {\n\tstatic override type = 'shape_indicator'\n\toverride options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 }\n\n\t// Narrow projection of instance state. Reading the full record would\n\t// re-fire getOverlays on every cursor move / brush update; gating on these\n\t// three booleans means we only re-fire when one of them actually flips.\n\tprivate _instanceFlags$ = computed<RelevantInstanceFlags>(\n\t\t'shape indicator instance flags',\n\t\t() => {\n\t\t\tconst i = this.editor.getInstanceState()\n\t\t\treturn {\n\t\t\t\tisChangingStyle: i.isChangingStyle,\n\t\t\t\tisHoveringCanvas: i.isHoveringCanvas,\n\t\t\t\tisCoarsePointer: i.isCoarsePointer,\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tisEqual: (a, b) =>\n\t\t\t\ta.isChangingStyle === b.isChangingStyle &&\n\t\t\t\ta.isHoveringCanvas === b.isHoveringCanvas &&\n\t\t\t\ta.isCoarsePointer === b.isCoarsePointer,\n\t\t}\n\t)\n\n\toverride isActive(): boolean {\n\t\treturn true\n\t}\n\n\toverride getOverlays(): TLShapeIndicatorOverlay[] {\n\t\tconst editor = this.editor\n\t\tconst renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id))\n\n\t\t// Local selected / hovered indicators.\n\t\tconst idsToDisplay: TLShapeId[] = []\n\t\tconst { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get()\n\t\tconst isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape')\n\t\tconst isInSelectState = editor.isInAny(\n\t\t\t'select.brushing',\n\t\t\t'select.scribble_brushing',\n\t\t\t'select.pointing_shape',\n\t\t\t'select.pointing_selection',\n\t\t\t'select.pointing_handle',\n\t\t\t'select.pointing_resize_handle',\n\t\t\t'select.pointing_rotate_handle'\n\t\t)\n\n\t\tif (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {\n\t\t\tfor (const id of editor.getSelectedShapeIds()) {\n\t\t\t\tif (renderingShapeIds.has(id)) idsToDisplay.push(id)\n\t\t\t}\n\t\t\tif (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {\n\t\t\t\tconst hovered = editor.getHoveredShapeId()\n\t\t\t\tif (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {\n\t\t\t\t\tidsToDisplay.push(hovered)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Hinted shapes (drawn thicker). Already deduped at write time in\n\t\t// `updateHintingShapeIds`, so no need to dedupe again here.\n\t\tconst hintingShapeIds: TLShapeId[] = []\n\t\tfor (const id of editor.getHintingShapeIds()) {\n\t\t\tif (renderingShapeIds.has(id)) hintingShapeIds.push(id)\n\t\t}\n\n\t\tif (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: 'shape_indicator',\n\t\t\t\ttype: 'shape_indicator',\n\t\t\t\tprops: { idsToDisplay, hintingShapeIds },\n\t\t\t},\n\t\t]\n\t}\n\n\toverride render(ctx: CanvasRenderingContext2D, overlays: TLShapeIndicatorOverlay[]): void {\n\t\tconst overlay = overlays[0]\n\t\tif (!overlay) return\n\n\t\tconst editor = this.editor\n\t\tconst zoom = editor.getZoomLevel()\n\t\tconst { idsToDisplay, hintingShapeIds } = overlay.props\n\n\t\tctx.lineCap = 'round'\n\t\tctx.lineJoin = 'round'\n\n\t\t// Local selected / hovered indicators \u2014 one stroke call for the whole batch.\n\t\tctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke\n\t\tctx.lineWidth = this.options.lineWidth / zoom\n\t\tstrokeShapeIndicators(editor, ctx, idsToDisplay)\n\n\t\t// Hinted shapes \u2014 thicker stroke, one call for the whole batch.\n\t\tif (hintingShapeIds.length > 0) {\n\t\t\tctx.lineWidth = this.options.hintedLineWidth / zoom\n\t\t\tstrokeShapeIndicators(editor, ctx, hintingShapeIds)\n\t\t}\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA8D;AAC9D,mBAAyB;AA8BlB,MAAM,kCAAkC,0BAAqC;AAAA,EACnF,OAAgB,OAAO;AAAA,EACd,UAAU,EAAE,QAAQ,IAAI,WAAW,KAAK,iBAAiB,IAAI;AAAA;AAAA;AAAA;AAAA,EAK9D,sBAAkB;AAAA,IACzB;AAAA,IACA,MAAM;AACL,YAAM,IAAI,KAAK,OAAO,iBAAiB;AACvC,aAAO;AAAA,QACN,iBAAiB,EAAE;AAAA,QACnB,kBAAkB,EAAE;AAAA,QACpB,iBAAiB,EAAE;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,SAAS,CAAC,GAAG,MACZ,EAAE,oBAAoB,EAAE,mBACxB,EAAE,qBAAqB,EAAE,oBACzB,EAAE,oBAAoB,EAAE;AAAA,IAC1B;AAAA,EACD;AAAA,EAES,WAAoB;AAC5B,WAAO;AAAA,EACR;AAAA,EAES,cAAyC;AACjD,UAAM,SAAS,KAAK;AACpB,UAAM,oBAAoB,IAAI,IAAI,OAAO,mBAAmB,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAG9E,UAAM,eAA4B,CAAC;AACnC,UAAM,EAAE,iBAAiB,kBAAkB,gBAAgB,IAAI,KAAK,gBAAgB,IAAI;AACxF,UAAM,kBAAkB,OAAO,QAAQ,eAAe,sBAAsB;AAC5E,UAAM,kBAAkB,OAAO;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,QAAI,CAAC,oBAAoB,mBAAmB,kBAAkB;AAC7D,iBAAW,MAAM,OAAO,oBAAoB,GAAG;AAC9C,YAAI,kBAAkB,IAAI,EAAE,EAAG,cAAa,KAAK,EAAE;AAAA,MACpD;AACA,UAAI,mBAAmB,oBAAoB,CAAC,iBAAiB;AAC5D,cAAM,UAAU,OAAO,kBAAkB;AACzC,YAAI,WAAW,kBAAkB,IAAI,OAAO,KAAK,CAAC,aAAa,SAAS,OAAO,GAAG;AACjF,uBAAa,KAAK,OAAO;AAAA,QAC1B;AAAA,MACD;AAAA,IACD;AAIA,UAAM,kBAA+B,CAAC;AACtC,eAAW,MAAM,OAAO,mBAAmB,GAAG;AAC7C,UAAI,kBAAkB,IAAI,EAAE,EAAG,iBAAgB,KAAK,EAAE;AAAA,IACvD;AAEA,QAAI,aAAa,WAAW,KAAK,gBAAgB,WAAW,GAAG;AAC9D,aAAO,CAAC;AAAA,IACT;AAEA,WAAO;AAAA,MACN;AAAA,QACC,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,cAAc,gBAAgB;AAAA,MACxC;AAAA,IACD;AAAA,EACD;AAAA,EAES,OAAO,KAA+B,UAA2C;AACzF,UAAM,UAAU,SAAS,CAAC;AAC1B,QAAI,CAAC,QAAS;AAEd,UAAM,SAAS,KAAK;AACpB,UAAM,OAAO,OAAO,aAAa;AACjC,UAAM,EAAE,cAAc,gBAAgB,IAAI,QAAQ;AAElD,QAAI,UAAU;AACd,QAAI,WAAW;AAGf,QAAI,cAAc,OAAO,gBAAgB,EAAE,OAAO,OAAO,aAAa,CAAC,EAAE;AACzE,QAAI,YAAY,KAAK,QAAQ,YAAY;AACzC,6CAAsB,QAAQ,KAAK,YAAY;AAG/C,QAAI,gBAAgB,SAAS,GAAG;AAC/B,UAAI,YAAY,KAAK,QAAQ,kBAAkB;AAC/C,+CAAsB,QAAQ,KAAK,eAAe;AAAA,IACnD;AAAA,EACD;AACD;",
4
+ "sourcesContent": ["import { OverlayUtil, strokeShapeIndicators, TLOverlay } from '@tldraw/editor'\nimport { computed } from '@tldraw/state'\nimport { TLShapeId } from '@tldraw/tlschema'\n\n/** @public */\nexport interface TLShapeIndicatorOverlay extends TLOverlay {\n\tprops: {\n\t\tidsToDisplay: TLShapeId[]\n\t\thintingShapeIds: TLShapeId[]\n\t}\n}\n\ninterface RelevantInstanceFlags {\n\tisChangingStyle: boolean\n\tisHoveringCanvas: boolean | null\n\tisCoarsePointer: boolean\n}\n\n/**\n * Overlay util for shape indicators \u2014 the selection / hover / hint outlines drawn\n * under the selection foreground. Paints local indicators in the theme's\n * selection color.\n *\n * Remote collaborator selection indicators are drawn by a separate overlay util\n * (e.g. `CollaboratorShapeIndicatorOverlayUtil` from `tldraw`) that runs at a\n * lower z-index so peer selections appear under the local indicators.\n *\n * Non-interactive: contributes no hit-test geometry.\n *\n * @public\n */\nexport class ShapeIndicatorOverlayUtil extends OverlayUtil<TLShapeIndicatorOverlay> {\n\tstatic override type = 'shape_indicator'\n\toverride options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2 }\n\n\t// Narrow projection of instance state. Reading the full record would\n\t// re-fire getOverlays on every cursor move / brush update; gating on these\n\t// three booleans means we only re-fire when one of them actually flips.\n\tprivate _instanceFlags$ = computed<RelevantInstanceFlags>(\n\t\t'shape indicator instance flags',\n\t\t() => {\n\t\t\tconst i = this.editor.getInstanceState()\n\t\t\treturn {\n\t\t\t\tisChangingStyle: i.isChangingStyle,\n\t\t\t\tisHoveringCanvas: i.isHoveringCanvas,\n\t\t\t\tisCoarsePointer: i.isCoarsePointer,\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tisEqual: (a, b) =>\n\t\t\t\ta.isChangingStyle === b.isChangingStyle &&\n\t\t\t\ta.isHoveringCanvas === b.isHoveringCanvas &&\n\t\t\t\ta.isCoarsePointer === b.isCoarsePointer,\n\t\t}\n\t)\n\n\toverride isActive(): boolean {\n\t\treturn true\n\t}\n\n\toverride getOverlays(): TLShapeIndicatorOverlay[] {\n\t\tconst editor = this.editor\n\t\tconst renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id))\n\n\t\t// Local selected / hovered indicators.\n\t\tconst idsToDisplay: TLShapeId[] = []\n\t\tconst { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get()\n\t\tconst isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape')\n\t\tconst isInSelectState = editor.isInAny(\n\t\t\t'select.brushing',\n\t\t\t'select.scribble_brushing',\n\t\t\t'select.pointing_shape',\n\t\t\t'select.pointing_selection',\n\t\t\t'select.pointing_handle',\n\t\t\t'select.pointing_resize_handle',\n\t\t\t'select.pointing_rotate_handle'\n\t\t)\n\n\t\tif (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {\n\t\t\tfor (const id of editor.getSelectedShapeIds()) {\n\t\t\t\tif (renderingShapeIds.has(id)) idsToDisplay.push(id)\n\t\t\t}\n\t\t\tif (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {\n\t\t\t\tconst hovered = editor.getHoveredShapeId()\n\t\t\t\tif (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {\n\t\t\t\t\tidsToDisplay.push(hovered)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Hinted shapes (drawn thicker). Already deduped at write time in\n\t\t// `updateHintingShapeIds`, so no need to dedupe again here.\n\t\tconst hintingShapeIds: TLShapeId[] = []\n\t\tfor (const id of editor.getHintingShapeIds()) {\n\t\t\tif (renderingShapeIds.has(id)) hintingShapeIds.push(id)\n\t\t}\n\n\t\tif (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: 'shape_indicator',\n\t\t\t\ttype: 'shape_indicator',\n\t\t\t\tprops: { idsToDisplay, hintingShapeIds },\n\t\t\t},\n\t\t]\n\t}\n\n\toverride render(ctx: CanvasRenderingContext2D, overlays: TLShapeIndicatorOverlay[]): void {\n\t\tconst overlay = overlays[0]\n\t\tif (!overlay) return\n\n\t\tconst editor = this.editor\n\t\tconst zoom = editor.getZoomLevel()\n\t\tconst { idsToDisplay, hintingShapeIds } = overlay.props\n\n\t\tctx.lineCap = 'round'\n\t\tctx.lineJoin = 'round'\n\n\t\t// Local selected / hovered indicators \u2014 one stroke call for the whole batch.\n\t\tctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke\n\t\tctx.lineWidth = this.options.lineWidth / zoom\n\t\tstrokeShapeIndicators(editor, ctx, idsToDisplay)\n\n\t\t// Hinted shapes \u2014 thicker stroke, one call for the whole batch.\n\t\tif (hintingShapeIds.length > 0) {\n\t\t\tctx.lineWidth = this.options.hintedLineWidth / zoom\n\t\t\tstrokeShapeIndicators(editor, ctx, hintingShapeIds)\n\t\t}\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA8D;AAC9D,mBAAyB;AA8BlB,MAAM,kCAAkC,0BAAqC;AAAA,EACnF,OAAgB,OAAO;AAAA,EACd,UAAU,EAAE,QAAQ,IAAI,WAAW,KAAK,iBAAiB,EAAE;AAAA;AAAA;AAAA;AAAA,EAK5D,sBAAkB;AAAA,IACzB;AAAA,IACA,MAAM;AACL,YAAM,IAAI,KAAK,OAAO,iBAAiB;AACvC,aAAO;AAAA,QACN,iBAAiB,EAAE;AAAA,QACnB,kBAAkB,EAAE;AAAA,QACpB,iBAAiB,EAAE;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,SAAS,CAAC,GAAG,MACZ,EAAE,oBAAoB,EAAE,mBACxB,EAAE,qBAAqB,EAAE,oBACzB,EAAE,oBAAoB,EAAE;AAAA,IAC1B;AAAA,EACD;AAAA,EAES,WAAoB;AAC5B,WAAO;AAAA,EACR;AAAA,EAES,cAAyC;AACjD,UAAM,SAAS,KAAK;AACpB,UAAM,oBAAoB,IAAI,IAAI,OAAO,mBAAmB,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAG9E,UAAM,eAA4B,CAAC;AACnC,UAAM,EAAE,iBAAiB,kBAAkB,gBAAgB,IAAI,KAAK,gBAAgB,IAAI;AACxF,UAAM,kBAAkB,OAAO,QAAQ,eAAe,sBAAsB;AAC5E,UAAM,kBAAkB,OAAO;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,QAAI,CAAC,oBAAoB,mBAAmB,kBAAkB;AAC7D,iBAAW,MAAM,OAAO,oBAAoB,GAAG;AAC9C,YAAI,kBAAkB,IAAI,EAAE,EAAG,cAAa,KAAK,EAAE;AAAA,MACpD;AACA,UAAI,mBAAmB,oBAAoB,CAAC,iBAAiB;AAC5D,cAAM,UAAU,OAAO,kBAAkB;AACzC,YAAI,WAAW,kBAAkB,IAAI,OAAO,KAAK,CAAC,aAAa,SAAS,OAAO,GAAG;AACjF,uBAAa,KAAK,OAAO;AAAA,QAC1B;AAAA,MACD;AAAA,IACD;AAIA,UAAM,kBAA+B,CAAC;AACtC,eAAW,MAAM,OAAO,mBAAmB,GAAG;AAC7C,UAAI,kBAAkB,IAAI,EAAE,EAAG,iBAAgB,KAAK,EAAE;AAAA,IACvD;AAEA,QAAI,aAAa,WAAW,KAAK,gBAAgB,WAAW,GAAG;AAC9D,aAAO,CAAC;AAAA,IACT;AAEA,WAAO;AAAA,MACN;AAAA,QACC,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,cAAc,gBAAgB;AAAA,MACxC;AAAA,IACD;AAAA,EACD;AAAA,EAES,OAAO,KAA+B,UAA2C;AACzF,UAAM,UAAU,SAAS,CAAC;AAC1B,QAAI,CAAC,QAAS;AAEd,UAAM,SAAS,KAAK;AACpB,UAAM,OAAO,OAAO,aAAa;AACjC,UAAM,EAAE,cAAc,gBAAgB,IAAI,QAAQ;AAElD,QAAI,UAAU;AACd,QAAI,WAAW;AAGf,QAAI,cAAc,OAAO,gBAAgB,EAAE,OAAO,OAAO,aAAa,CAAC,EAAE;AACzE,QAAI,YAAY,KAAK,QAAQ,YAAY;AACzC,6CAAsB,QAAQ,KAAK,YAAY;AAG/C,QAAI,gBAAgB,SAAS,GAAG;AAC/B,UAAI,YAAY,KAAK,QAAQ,kBAAkB;AAC/C,+CAAsB,QAAQ,KAAK,eAAe;AAAA,IACnD;AAAA,EACD;AACD;",
6
6
  "names": []
7
7
  }
@@ -33,6 +33,7 @@ class Idle extends import_editor.StateNode {
33
33
  this.update();
34
34
  }
35
35
  onPointerDown(info) {
36
+ this.update();
36
37
  this.parent.transition("pointing", { ...info, isPrecise: this.isPrecise });
37
38
  }
38
39
  onEnter() {
@@ -44,8 +45,16 @@ class Idle extends import_editor.StateNode {
44
45
  }
45
46
  onExit() {
46
47
  (0, import_arrowTargetState.clearArrowTargetState)(this.editor);
48
+ this.resetPrecise();
49
+ }
50
+ // Precise mode is earned by hovering one target; it must not carry over to the next
51
+ // target or to the next arrow.
52
+ resetPrecise() {
53
+ this.isPrecise = false;
54
+ this.preciseTargetId = null;
47
55
  if (this.isPreciseTimerId !== null) {
48
56
  this.editor.timers.clearTimeout(this.isPreciseTimerId);
57
+ this.isPreciseTimerId = null;
49
58
  }
50
59
  }
51
60
  onKeyDown() {
@@ -61,30 +70,29 @@ class Idle extends import_editor.StateNode {
61
70
  }
62
71
  update() {
63
72
  const arrowUtil = this.editor.getShapeUtil("arrow");
64
- const targetState = (0, import_arrowTargetState.updateArrowTargetState)({
65
- editor: this.editor,
66
- pointInPageSpace: this.editor.inputs.getCurrentPagePoint(),
67
- arrow: void 0,
68
- isPrecise: this.isPrecise,
69
- currentBinding: void 0,
70
- oppositeBinding: void 0
71
- });
73
+ const targetState = this.updateTargetState();
72
74
  if (targetState && targetState.target.id !== this.preciseTargetId) {
73
- if (this.isPreciseTimerId !== null) {
74
- this.editor.timers.clearTimeout(this.isPreciseTimerId);
75
- }
75
+ const wasPrecise = this.isPrecise;
76
+ this.resetPrecise();
77
+ if (wasPrecise) this.updateTargetState();
76
78
  this.preciseTargetId = targetState.target.id;
77
79
  this.isPreciseTimerId = this.editor.timers.setTimeout(() => {
78
80
  this.isPrecise = true;
79
81
  this.update();
80
82
  }, arrowUtil.options.hoverPreciseTimeout);
81
83
  } else if (!targetState && this.preciseTargetId) {
82
- this.isPrecise = false;
83
- this.preciseTargetId = null;
84
- if (this.isPreciseTimerId !== null) {
85
- this.editor.timers.clearTimeout(this.isPreciseTimerId);
86
- }
84
+ this.resetPrecise();
87
85
  }
88
86
  }
87
+ updateTargetState() {
88
+ return (0, import_arrowTargetState.updateArrowTargetState)({
89
+ editor: this.editor,
90
+ pointInPageSpace: this.editor.inputs.getCurrentPagePoint(),
91
+ arrow: void 0,
92
+ isPrecise: this.isPrecise,
93
+ currentBinding: void 0,
94
+ oppositeBinding: void 0
95
+ });
96
+ }
89
97
  }
90
98
  //# sourceMappingURL=Idle.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/shapes/arrow/toolStates/Idle.tsx"],
4
- "sourcesContent": ["import { StateNode, TLKeyboardEventInfo, TLPointerEventInfo, TLShapeId } from '@tldraw/editor'\nimport { startEditingShape } from '../../../tools/SelectTool/selectHelpers'\nimport { ArrowShapeUtil } from '../ArrowShapeUtil'\nimport { clearArrowTargetState, updateArrowTargetState } from '../arrowTargetState'\n\nexport class Idle extends StateNode {\n\tstatic override id = 'idle'\n\n\tisPrecise = false\n\tisPreciseTimerId: number | null = null\n\tpreciseTargetId: TLShapeId | null = null\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerDown(info: TLPointerEventInfo) {\n\t\tthis.parent.transition('pointing', { ...info, isPrecise: this.isPrecise })\n\t}\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.update()\n\t}\n\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n\n\toverride onExit() {\n\t\tclearArrowTargetState(this.editor)\n\t\tif (this.isPreciseTimerId !== null) {\n\t\t\tthis.editor.timers.clearTimeout(this.isPreciseTimerId)\n\t\t}\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyUp(info: TLKeyboardEventInfo) {\n\t\tthis.update()\n\t\tif (info.key === 'Enter') {\n\t\t\tconst onlySelectedShape = this.editor.getOnlySelectedShape()\n\t\t\tif (!this.editor.canEditShape(onlySelectedShape)) return\n\t\t\tstartEditingShape(this.editor, onlySelectedShape, { selectAll: true })\n\t\t}\n\t}\n\n\tupdate() {\n\t\tconst arrowUtil = this.editor.getShapeUtil<ArrowShapeUtil>('arrow')\n\n\t\tconst targetState = updateArrowTargetState({\n\t\t\teditor: this.editor,\n\t\t\tpointInPageSpace: this.editor.inputs.getCurrentPagePoint(),\n\t\t\tarrow: undefined,\n\t\t\tisPrecise: this.isPrecise,\n\t\t\tcurrentBinding: undefined,\n\t\t\toppositeBinding: undefined,\n\t\t})\n\n\t\tif (targetState && targetState.target.id !== this.preciseTargetId) {\n\t\t\tif (this.isPreciseTimerId !== null) {\n\t\t\t\tthis.editor.timers.clearTimeout(this.isPreciseTimerId)\n\t\t\t}\n\n\t\t\tthis.preciseTargetId = targetState.target.id\n\t\t\tthis.isPreciseTimerId = this.editor.timers.setTimeout(() => {\n\t\t\t\tthis.isPrecise = true\n\t\t\t\tthis.update()\n\t\t\t}, arrowUtil.options.hoverPreciseTimeout)\n\t\t} else if (!targetState && this.preciseTargetId) {\n\t\t\tthis.isPrecise = false\n\t\t\tthis.preciseTargetId = null\n\t\t\tif (this.isPreciseTimerId !== null) {\n\t\t\t\tthis.editor.timers.clearTimeout(this.isPreciseTimerId)\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA8E;AAC9E,2BAAkC;AAElC,8BAA8D;AAEvD,MAAM,aAAa,wBAAU;AAAA,EACnC,OAAgB,KAAK;AAAA,EAErB,YAAY;AAAA,EACZ,mBAAkC;AAAA,EAClC,kBAAoC;AAAA,EAE3B,gBAAgB;AACxB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,cAAc,MAA0B;AAChD,SAAK,OAAO,WAAW,YAAY,EAAE,GAAG,MAAM,WAAW,KAAK,UAAU,CAAC;AAAA,EAC1E;AAAA,EAES,UAAU;AAClB,SAAK,OAAO,UAAU,EAAE,MAAM,SAAS,UAAU,EAAE,CAAC;AACpD,SAAK,OAAO;AAAA,EACb;AAAA,EAES,WAAW;AACnB,SAAK,OAAO,eAAe,QAAQ;AAAA,EACpC;AAAA,EAES,SAAS;AACjB,uDAAsB,KAAK,MAAM;AACjC,QAAI,KAAK,qBAAqB,MAAM;AACnC,WAAK,OAAO,OAAO,aAAa,KAAK,gBAAgB;AAAA,IACtD;AAAA,EACD;AAAA,EAES,YAAY;AACpB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,QAAQ,MAA2B;AAC3C,SAAK,OAAO;AACZ,QAAI,KAAK,QAAQ,SAAS;AACzB,YAAM,oBAAoB,KAAK,OAAO,qBAAqB;AAC3D,UAAI,CAAC,KAAK,OAAO,aAAa,iBAAiB,EAAG;AAClD,kDAAkB,KAAK,QAAQ,mBAAmB,EAAE,WAAW,KAAK,CAAC;AAAA,IACtE;AAAA,EACD;AAAA,EAEA,SAAS;AACR,UAAM,YAAY,KAAK,OAAO,aAA6B,OAAO;AAElE,UAAM,kBAAc,gDAAuB;AAAA,MAC1C,QAAQ,KAAK;AAAA,MACb,kBAAkB,KAAK,OAAO,OAAO,oBAAoB;AAAA,MACzD,OAAO;AAAA,MACP,WAAW,KAAK;AAAA,MAChB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IAClB,CAAC;AAED,QAAI,eAAe,YAAY,OAAO,OAAO,KAAK,iBAAiB;AAClE,UAAI,KAAK,qBAAqB,MAAM;AACnC,aAAK,OAAO,OAAO,aAAa,KAAK,gBAAgB;AAAA,MACtD;AAEA,WAAK,kBAAkB,YAAY,OAAO;AAC1C,WAAK,mBAAmB,KAAK,OAAO,OAAO,WAAW,MAAM;AAC3D,aAAK,YAAY;AACjB,aAAK,OAAO;AAAA,MACb,GAAG,UAAU,QAAQ,mBAAmB;AAAA,IACzC,WAAW,CAAC,eAAe,KAAK,iBAAiB;AAChD,WAAK,YAAY;AACjB,WAAK,kBAAkB;AACvB,UAAI,KAAK,qBAAqB,MAAM;AACnC,aAAK,OAAO,OAAO,aAAa,KAAK,gBAAgB;AAAA,MACtD;AAAA,IACD;AAAA,EACD;AACD;",
4
+ "sourcesContent": ["import { StateNode, TLKeyboardEventInfo, TLPointerEventInfo, TLShapeId } from '@tldraw/editor'\nimport { startEditingShape } from '../../../tools/SelectTool/selectHelpers'\nimport { ArrowShapeUtil } from '../ArrowShapeUtil'\nimport { clearArrowTargetState, updateArrowTargetState } from '../arrowTargetState'\n\nexport class Idle extends StateNode {\n\tstatic override id = 'idle'\n\n\tisPrecise = false\n\tisPreciseTimerId: number | null = null\n\tpreciseTargetId: TLShapeId | null = null\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerDown(info: TLPointerEventInfo) {\n\t\t// A touch press arrives without a hover move, so the stored flag may belong to another target\n\t\tthis.update()\n\t\tthis.parent.transition('pointing', { ...info, isPrecise: this.isPrecise })\n\t}\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.update()\n\t}\n\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n\n\toverride onExit() {\n\t\tclearArrowTargetState(this.editor)\n\t\tthis.resetPrecise()\n\t}\n\n\t// Precise mode is earned by hovering one target; it must not carry over to the next\n\t// target or to the next arrow.\n\tprivate resetPrecise() {\n\t\tthis.isPrecise = false\n\t\tthis.preciseTargetId = null\n\t\tif (this.isPreciseTimerId !== null) {\n\t\t\tthis.editor.timers.clearTimeout(this.isPreciseTimerId)\n\t\t\tthis.isPreciseTimerId = null\n\t\t}\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyUp(info: TLKeyboardEventInfo) {\n\t\tthis.update()\n\t\tif (info.key === 'Enter') {\n\t\t\tconst onlySelectedShape = this.editor.getOnlySelectedShape()\n\t\t\tif (!this.editor.canEditShape(onlySelectedShape)) return\n\t\t\tstartEditingShape(this.editor, onlySelectedShape, { selectAll: true })\n\t\t}\n\t}\n\n\tupdate() {\n\t\tconst arrowUtil = this.editor.getShapeUtil<ArrowShapeUtil>('arrow')\n\n\t\tconst targetState = this.updateTargetState()\n\n\t\tif (targetState && targetState.target.id !== this.preciseTargetId) {\n\t\t\tconst wasPrecise = this.isPrecise\n\t\t\tthis.resetPrecise()\n\t\t\t// The hint above was published with the previous target's precise flag. Republish it\n\t\t\t// so the overlay matches the imprecise binding a click would now create.\n\t\t\tif (wasPrecise) this.updateTargetState()\n\t\t\tthis.preciseTargetId = targetState.target.id\n\t\t\tthis.isPreciseTimerId = this.editor.timers.setTimeout(() => {\n\t\t\t\tthis.isPrecise = true\n\t\t\t\tthis.update()\n\t\t\t}, arrowUtil.options.hoverPreciseTimeout)\n\t\t} else if (!targetState && this.preciseTargetId) {\n\t\t\tthis.resetPrecise()\n\t\t}\n\t}\n\n\tprivate updateTargetState() {\n\t\treturn updateArrowTargetState({\n\t\t\teditor: this.editor,\n\t\t\tpointInPageSpace: this.editor.inputs.getCurrentPagePoint(),\n\t\t\tarrow: undefined,\n\t\t\tisPrecise: this.isPrecise,\n\t\t\tcurrentBinding: undefined,\n\t\t\toppositeBinding: undefined,\n\t\t})\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA8E;AAC9E,2BAAkC;AAElC,8BAA8D;AAEvD,MAAM,aAAa,wBAAU;AAAA,EACnC,OAAgB,KAAK;AAAA,EAErB,YAAY;AAAA,EACZ,mBAAkC;AAAA,EAClC,kBAAoC;AAAA,EAE3B,gBAAgB;AACxB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,cAAc,MAA0B;AAEhD,SAAK,OAAO;AACZ,SAAK,OAAO,WAAW,YAAY,EAAE,GAAG,MAAM,WAAW,KAAK,UAAU,CAAC;AAAA,EAC1E;AAAA,EAES,UAAU;AAClB,SAAK,OAAO,UAAU,EAAE,MAAM,SAAS,UAAU,EAAE,CAAC;AACpD,SAAK,OAAO;AAAA,EACb;AAAA,EAES,WAAW;AACnB,SAAK,OAAO,eAAe,QAAQ;AAAA,EACpC;AAAA,EAES,SAAS;AACjB,uDAAsB,KAAK,MAAM;AACjC,SAAK,aAAa;AAAA,EACnB;AAAA;AAAA;AAAA,EAIQ,eAAe;AACtB,SAAK,YAAY;AACjB,SAAK,kBAAkB;AACvB,QAAI,KAAK,qBAAqB,MAAM;AACnC,WAAK,OAAO,OAAO,aAAa,KAAK,gBAAgB;AACrD,WAAK,mBAAmB;AAAA,IACzB;AAAA,EACD;AAAA,EAES,YAAY;AACpB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,QAAQ,MAA2B;AAC3C,SAAK,OAAO;AACZ,QAAI,KAAK,QAAQ,SAAS;AACzB,YAAM,oBAAoB,KAAK,OAAO,qBAAqB;AAC3D,UAAI,CAAC,KAAK,OAAO,aAAa,iBAAiB,EAAG;AAClD,kDAAkB,KAAK,QAAQ,mBAAmB,EAAE,WAAW,KAAK,CAAC;AAAA,IACtE;AAAA,EACD;AAAA,EAEA,SAAS;AACR,UAAM,YAAY,KAAK,OAAO,aAA6B,OAAO;AAElE,UAAM,cAAc,KAAK,kBAAkB;AAE3C,QAAI,eAAe,YAAY,OAAO,OAAO,KAAK,iBAAiB;AAClE,YAAM,aAAa,KAAK;AACxB,WAAK,aAAa;AAGlB,UAAI,WAAY,MAAK,kBAAkB;AACvC,WAAK,kBAAkB,YAAY,OAAO;AAC1C,WAAK,mBAAmB,KAAK,OAAO,OAAO,WAAW,MAAM;AAC3D,aAAK,YAAY;AACjB,aAAK,OAAO;AAAA,MACb,GAAG,UAAU,QAAQ,mBAAmB;AAAA,IACzC,WAAW,CAAC,eAAe,KAAK,iBAAiB;AAChD,WAAK,aAAa;AAAA,IACnB;AAAA,EACD;AAAA,EAEQ,oBAAoB;AAC3B,eAAO,gDAAuB;AAAA,MAC7B,QAAQ,KAAK;AAAA,MACb,kBAAkB,KAAK,OAAO,OAAO,oBAAoB;AAAA,MACzD,OAAO;AAAA,MACP,WAAW,KAAK;AAAA,MAChB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IAClB,CAAC;AAAA,EACF;AACD;",
6
6
  "names": []
7
7
  }
@@ -29,7 +29,6 @@ class Erasing extends import_editor.StateNode {
29
29
  scribbleId = "id";
30
30
  markId = "";
31
31
  excludedShapeIds = /* @__PURE__ */ new Set();
32
- _erasingShapeIds = [];
33
32
  onEnter(info) {
34
33
  this.markId = this.editor.markHistoryStoppingPoint("erase scribble begin");
35
34
  this.info = info;
@@ -47,9 +46,9 @@ class Erasing extends import_editor.StateNode {
47
46
  return false;
48
47
  }).map((shape) => shape.id)
49
48
  );
50
- this._erasingShapeIds = this.editor.getShapesAtPoint(originPagePoint).filter((s) => !this.excludedShapeIds.has(s.id)).map((s) => s.id);
49
+ const hitShapeIds = this.editor.getShapesAtPoint(originPagePoint).filter((s) => !this.excludedShapeIds.has(s.id)).map((s) => s.id);
51
50
  this.editor.setErasingShapes([
52
- .../* @__PURE__ */ new Set([...this.editor.getErasingShapeIds(), ...this._erasingShapeIds])
51
+ .../* @__PURE__ */ new Set([...this.editor.getErasingShapeIds(), ...hitShapeIds])
53
52
  ]);
54
53
  const scribble = this.editor.scribbles.addScribble({
55
54
  color: "muted-1",
@@ -120,15 +119,13 @@ class Erasing extends import_editor.StateNode {
120
119
  erasing.add(outermost.id);
121
120
  }
122
121
  }
123
- this._erasingShapeIds = [...erasing];
124
122
  }
125
- this.editor.setErasingShapes(this._erasingShapeIds.filter((id) => !excludedShapeIds.has(id)));
123
+ this.editor.setErasingShapes([...erasing].filter((id) => !excludedShapeIds.has(id)));
126
124
  }
127
125
  complete(info) {
128
126
  const { editor } = this;
129
127
  editor.deleteShapes(editor.getCurrentPageState().erasingShapeIds);
130
128
  this.parent.transition("idle", info);
131
- this._erasingShapeIds = [];
132
129
  }
133
130
  cancel() {
134
131
  const { editor } = this;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/tools/EraserTool/childStates/Erasing.ts"],
4
- "sourcesContent": ["import { Box, StateNode, TLPointerEventInfo, TLShapeId, pointInPolygon } from '@tldraw/editor'\n\nexport class Erasing extends StateNode {\n\tstatic override id = 'erasing'\n\tstatic override trackPerformance = true\n\n\tprivate info = {} as TLPointerEventInfo\n\tprivate scribbleId = 'id'\n\tprivate markId = ''\n\tprivate excludedShapeIds = new Set<TLShapeId>()\n\n\t_erasingShapeIds: TLShapeId[] = []\n\n\toverride onEnter(info: TLPointerEventInfo) {\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('erase scribble begin')\n\t\tthis.info = info\n\n\t\tconst originPagePoint = this.editor.inputs.getOriginPagePoint()\n\t\tconst pressedShapeIds = new Set(this.editor.getErasingShapeIds())\n\t\tthis.excludedShapeIds = new Set(\n\t\t\tthis.editor\n\t\t\t\t.getCurrentPageShapes()\n\t\t\t\t.filter((shape) => {\n\t\t\t\t\t//If the shape is locked, we shouldn't erase it\n\t\t\t\t\tif (this.editor.isShapeOrAncestorLocked(shape)) return true\n\t\t\t\t\t// A frame the press hit on its outline stays marked: a press just inside the outline\n\t\t\t\t\t// is within the frame's bounds, so the containment check below would otherwise\n\t\t\t\t\t// unmark it on the first move while a press just outside would erase it\n\t\t\t\t\tif (this.editor.isShapeFrameLike(shape) && pressedShapeIds.has(shape.id)) return false\n\t\t\t\t\t//If the shape is a group or frame-like, check we're inside it when we start erasing\n\t\t\t\t\tif (this.editor.isShapeOfType(shape, 'group') || this.editor.isShapeFrameLike(shape)) {\n\t\t\t\t\t\tconst pointInShapeShape = this.editor.getPointInShapeSpace(shape, originPagePoint)\n\t\t\t\t\t\tconst geometry = this.editor.getShapeGeometry(shape)\n\t\t\t\t\t\treturn geometry.bounds.containsPoint(pointInShapeShape)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false\n\t\t\t\t})\n\t\t\t\t.map((shape) => shape.id)\n\t\t)\n\n\t\tthis._erasingShapeIds = this.editor\n\t\t\t.getShapesAtPoint(originPagePoint)\n\t\t\t.filter((s) => !this.excludedShapeIds.has(s.id))\n\t\t\t.map((s) => s.id)\n\n\t\tthis.editor.setErasingShapes([\n\t\t\t...new Set([...this.editor.getErasingShapeIds(), ...this._erasingShapeIds]),\n\t\t])\n\n\t\tconst scribble = this.editor.scribbles.addScribble({\n\t\t\tcolor: 'muted-1',\n\t\t\tsize: 12,\n\t\t})\n\t\tthis.scribbleId = scribble.id\n\n\t\tthis.update()\n\t}\n\n\tprivate pushPointToScribble() {\n\t\tconst { x, y } = this.editor.inputs.getCurrentPagePoint()\n\t\tthis.editor.scribbles.addPoint(this.scribbleId, x, y)\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setErasingShapes([])\n\t\tthis.editor.scribbles.stop(this.scribbleId)\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerUp(info: TLPointerEventInfo) {\n\t\tthis.complete(info)\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\tupdate() {\n\t\tconst { editor, excludedShapeIds } = this\n\t\tconst erasingShapeIds = editor.getErasingShapeIds()\n\t\tconst currentPagePoint = editor.inputs.getCurrentPagePoint()\n\t\tconst previousPagePoint = editor.inputs.getPreviousPagePoint()\n\n\t\tthis.pushPointToScribble()\n\n\t\t// Otherwise, erasing shapes are all the shapes that were hit before plus any new shapes that are hit\n\t\tconst erasing = new Set<TLShapeId>(erasingShapeIds)\n\t\tconst minDist = this.editor.getHitTestMargin()\n\n\t\t// Create bounds around line segment with margin\n\t\tconst lineBounds = Box.FromPoints([previousPagePoint, currentPagePoint]).expandBy(minDist)\n\t\tconst candidateIds = editor.getShapeIdsInsideBounds(lineBounds)\n\n\t\t// Early return if no candidates - avoid expensive getCurrentPageRenderingShapesSorted()\n\t\tif (candidateIds.size === 0) {\n\t\t\teditor.setErasingShapes(Array.from(erasing))\n\t\t\treturn\n\t\t}\n\n\t\tconst allShapes = editor.getCurrentPageRenderingShapesSorted()\n\t\tconst currentPageShapes = allShapes.filter((shape) => candidateIds.has(shape.id))\n\n\t\tfor (const shape of currentPageShapes) {\n\t\t\tif (editor.isShapeOfType(shape, 'group')) continue\n\n\t\t\t// Avoid testing masked shapes, unless the pointer is inside the mask\n\t\t\tconst pageMask = editor.getShapeMask(shape.id)\n\t\t\tif (pageMask && !pointInPolygon(currentPagePoint, pageMask)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Hit test the shape using a line segment\n\t\t\tconst geometry = editor.getShapeGeometry(shape)\n\t\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\t\tif (!geometry || !pageTransform) continue\n\t\t\tconst pt = pageTransform.clone().invert()\n\t\t\tconst A = pt.applyToPoint(previousPagePoint)\n\t\t\tconst B = pt.applyToPoint(currentPagePoint)\n\n\t\t\t// If the line segment is entirely above / below / left / right of the shape's bounding box, skip the hit test\n\t\t\tconst { bounds } = geometry\n\t\t\tif (\n\t\t\t\tbounds.minX - minDist > Math.max(A.x, B.x) ||\n\t\t\t\tbounds.minY - minDist > Math.max(A.y, B.y) ||\n\t\t\t\tbounds.maxX + minDist < Math.min(A.x, B.x) ||\n\t\t\t\tbounds.maxY + minDist < Math.min(A.y, B.y)\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If a shape is hit and is part of a group, erase the group\n\t\t\t// If we started erasing inside a group's bounds, erase child shapes (or nested groups) inside it\n\t\t\tif (geometry.hitTestLineSegment(A, B, minDist)) {\n\t\t\t\tconst outermost = editor.getOutermostSelectableShape(shape)\n\t\t\t\tif (excludedShapeIds.has(outermost.id)) {\n\t\t\t\t\terasing.add(\n\t\t\t\t\t\teditor.getOutermostSelectableShape(shape, (s) => !excludedShapeIds.has(s.id)).id\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\terasing.add(outermost.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._erasingShapeIds = [...erasing]\n\t\t}\n\n\t\t// Remove the hit shapes, except if they're in the list of excluded shapes\n\t\t// (these excluded shapes will be any frames or groups the pointer was inside of\n\t\t// when the user started erasing)\n\t\tthis.editor.setErasingShapes(this._erasingShapeIds.filter((id) => !excludedShapeIds.has(id)))\n\t}\n\n\tcomplete(info?: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\teditor.deleteShapes(editor.getCurrentPageState().erasingShapeIds)\n\t\tthis.parent.transition('idle', info)\n\t\tthis._erasingShapeIds = []\n\t}\n\n\tcancel() {\n\t\tconst { editor } = this\n\t\teditor.bailToMark(this.markId)\n\t\tthis.parent.transition('idle', this.info)\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA8E;AAEvE,MAAM,gBAAgB,wBAAU;AAAA,EACtC,OAAgB,KAAK;AAAA,EACrB,OAAgB,mBAAmB;AAAA,EAE3B,OAAO,CAAC;AAAA,EACR,aAAa;AAAA,EACb,SAAS;AAAA,EACT,mBAAmB,oBAAI,IAAe;AAAA,EAE9C,mBAAgC,CAAC;AAAA,EAExB,QAAQ,MAA0B;AAC1C,SAAK,SAAS,KAAK,OAAO,yBAAyB,sBAAsB;AACzE,SAAK,OAAO;AAEZ,UAAM,kBAAkB,KAAK,OAAO,OAAO,mBAAmB;AAC9D,UAAM,kBAAkB,IAAI,IAAI,KAAK,OAAO,mBAAmB,CAAC;AAChE,SAAK,mBAAmB,IAAI;AAAA,MAC3B,KAAK,OACH,qBAAqB,EACrB,OAAO,CAAC,UAAU;AAElB,YAAI,KAAK,OAAO,wBAAwB,KAAK,EAAG,QAAO;AAIvD,YAAI,KAAK,OAAO,iBAAiB,KAAK,KAAK,gBAAgB,IAAI,MAAM,EAAE,EAAG,QAAO;AAEjF,YAAI,KAAK,OAAO,cAAc,OAAO,OAAO,KAAK,KAAK,OAAO,iBAAiB,KAAK,GAAG;AACrF,gBAAM,oBAAoB,KAAK,OAAO,qBAAqB,OAAO,eAAe;AACjF,gBAAM,WAAW,KAAK,OAAO,iBAAiB,KAAK;AACnD,iBAAO,SAAS,OAAO,cAAc,iBAAiB;AAAA,QACvD;AAEA,eAAO;AAAA,MACR,CAAC,EACA,IAAI,CAAC,UAAU,MAAM,EAAE;AAAA,IAC1B;AAEA,SAAK,mBAAmB,KAAK,OAC3B,iBAAiB,eAAe,EAChC,OAAO,CAAC,MAAM,CAAC,KAAK,iBAAiB,IAAI,EAAE,EAAE,CAAC,EAC9C,IAAI,CAAC,MAAM,EAAE,EAAE;AAEjB,SAAK,OAAO,iBAAiB;AAAA,MAC5B,GAAG,oBAAI,IAAI,CAAC,GAAG,KAAK,OAAO,mBAAmB,GAAG,GAAG,KAAK,gBAAgB,CAAC;AAAA,IAC3E,CAAC;AAED,UAAM,WAAW,KAAK,OAAO,UAAU,YAAY;AAAA,MAClD,OAAO;AAAA,MACP,MAAM;AAAA,IACP,CAAC;AACD,SAAK,aAAa,SAAS;AAE3B,SAAK,OAAO;AAAA,EACb;AAAA,EAEQ,sBAAsB;AAC7B,UAAM,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,OAAO,oBAAoB;AACxD,SAAK,OAAO,UAAU,SAAS,KAAK,YAAY,GAAG,CAAC;AAAA,EACrD;AAAA,EAES,SAAS;AACjB,SAAK,OAAO,iBAAiB,CAAC,CAAC;AAC/B,SAAK,OAAO,UAAU,KAAK,KAAK,UAAU;AAAA,EAC3C;AAAA,EAES,gBAAgB;AACxB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,YAAY,MAA0B;AAC9C,SAAK,SAAS,IAAI;AAAA,EACnB;AAAA,EAES,WAAW;AACnB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,aAAa;AACrB,SAAK,SAAS;AAAA,EACf;AAAA,EAEA,SAAS;AACR,UAAM,EAAE,QAAQ,iBAAiB,IAAI;AACrC,UAAM,kBAAkB,OAAO,mBAAmB;AAClD,UAAM,mBAAmB,OAAO,OAAO,oBAAoB;AAC3D,UAAM,oBAAoB,OAAO,OAAO,qBAAqB;AAE7D,SAAK,oBAAoB;AAGzB,UAAM,UAAU,IAAI,IAAe,eAAe;AAClD,UAAM,UAAU,KAAK,OAAO,iBAAiB;AAG7C,UAAM,aAAa,kBAAI,WAAW,CAAC,mBAAmB,gBAAgB,CAAC,EAAE,SAAS,OAAO;AACzF,UAAM,eAAe,OAAO,wBAAwB,UAAU;AAG9D,QAAI,aAAa,SAAS,GAAG;AAC5B,aAAO,iBAAiB,MAAM,KAAK,OAAO,CAAC;AAC3C;AAAA,IACD;AAEA,UAAM,YAAY,OAAO,oCAAoC;AAC7D,UAAM,oBAAoB,UAAU,OAAO,CAAC,UAAU,aAAa,IAAI,MAAM,EAAE,CAAC;AAEhF,eAAW,SAAS,mBAAmB;AACtC,UAAI,OAAO,cAAc,OAAO,OAAO,EAAG;AAG1C,YAAM,WAAW,OAAO,aAAa,MAAM,EAAE;AAC7C,UAAI,YAAY,KAAC,8BAAe,kBAAkB,QAAQ,GAAG;AAC5D;AAAA,MACD;AAGA,YAAM,WAAW,OAAO,iBAAiB,KAAK;AAC9C,YAAM,gBAAgB,OAAO,sBAAsB,KAAK;AACxD,UAAI,CAAC,YAAY,CAAC,cAAe;AACjC,YAAM,KAAK,cAAc,MAAM,EAAE,OAAO;AACxC,YAAM,IAAI,GAAG,aAAa,iBAAiB;AAC3C,YAAM,IAAI,GAAG,aAAa,gBAAgB;AAG1C,YAAM,EAAE,OAAO,IAAI;AACnB,UACC,OAAO,OAAO,UAAU,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KACzC,OAAO,OAAO,UAAU,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KACzC,OAAO,OAAO,UAAU,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KACzC,OAAO,OAAO,UAAU,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,GACxC;AACD;AAAA,MACD;AAIA,UAAI,SAAS,mBAAmB,GAAG,GAAG,OAAO,GAAG;AAC/C,cAAM,YAAY,OAAO,4BAA4B,KAAK;AAC1D,YAAI,iBAAiB,IAAI,UAAU,EAAE,GAAG;AACvC,kBAAQ;AAAA,YACP,OAAO,4BAA4B,OAAO,CAAC,MAAM,CAAC,iBAAiB,IAAI,EAAE,EAAE,CAAC,EAAE;AAAA,UAC/E;AAAA,QACD,OAAO;AACN,kBAAQ,IAAI,UAAU,EAAE;AAAA,QACzB;AAAA,MACD;AAEA,WAAK,mBAAmB,CAAC,GAAG,OAAO;AAAA,IACpC;AAKA,SAAK,OAAO,iBAAiB,KAAK,iBAAiB,OAAO,CAAC,OAAO,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;AAAA,EAC7F;AAAA,EAEA,SAAS,MAA2B;AACnC,UAAM,EAAE,OAAO,IAAI;AACnB,WAAO,aAAa,OAAO,oBAAoB,EAAE,eAAe;AAChE,SAAK,OAAO,WAAW,QAAQ,IAAI;AACnC,SAAK,mBAAmB,CAAC;AAAA,EAC1B;AAAA,EAEA,SAAS;AACR,UAAM,EAAE,OAAO,IAAI;AACnB,WAAO,WAAW,KAAK,MAAM;AAC7B,SAAK,OAAO,WAAW,QAAQ,KAAK,IAAI;AAAA,EACzC;AACD;",
4
+ "sourcesContent": ["import { Box, StateNode, TLPointerEventInfo, TLShapeId, pointInPolygon } from '@tldraw/editor'\n\nexport class Erasing extends StateNode {\n\tstatic override id = 'erasing'\n\tstatic override trackPerformance = true\n\n\tprivate info = {} as TLPointerEventInfo\n\tprivate scribbleId = 'id'\n\tprivate markId = ''\n\tprivate excludedShapeIds = new Set<TLShapeId>()\n\n\toverride onEnter(info: TLPointerEventInfo) {\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('erase scribble begin')\n\t\tthis.info = info\n\n\t\tconst originPagePoint = this.editor.inputs.getOriginPagePoint()\n\t\tconst pressedShapeIds = new Set(this.editor.getErasingShapeIds())\n\t\tthis.excludedShapeIds = new Set(\n\t\t\tthis.editor\n\t\t\t\t.getCurrentPageShapes()\n\t\t\t\t.filter((shape) => {\n\t\t\t\t\t//If the shape is locked, we shouldn't erase it\n\t\t\t\t\tif (this.editor.isShapeOrAncestorLocked(shape)) return true\n\t\t\t\t\t// A frame the press hit on its outline stays marked: a press just inside the outline\n\t\t\t\t\t// is within the frame's bounds, so the containment check below would otherwise\n\t\t\t\t\t// unmark it on the first move while a press just outside would erase it\n\t\t\t\t\tif (this.editor.isShapeFrameLike(shape) && pressedShapeIds.has(shape.id)) return false\n\t\t\t\t\t//If the shape is a group or frame-like, check we're inside it when we start erasing\n\t\t\t\t\tif (this.editor.isShapeOfType(shape, 'group') || this.editor.isShapeFrameLike(shape)) {\n\t\t\t\t\t\tconst pointInShapeShape = this.editor.getPointInShapeSpace(shape, originPagePoint)\n\t\t\t\t\t\tconst geometry = this.editor.getShapeGeometry(shape)\n\t\t\t\t\t\treturn geometry.bounds.containsPoint(pointInShapeShape)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false\n\t\t\t\t})\n\t\t\t\t.map((shape) => shape.id)\n\t\t)\n\n\t\tconst hitShapeIds = this.editor\n\t\t\t.getShapesAtPoint(originPagePoint)\n\t\t\t.filter((s) => !this.excludedShapeIds.has(s.id))\n\t\t\t.map((s) => s.id)\n\n\t\tthis.editor.setErasingShapes([\n\t\t\t...new Set([...this.editor.getErasingShapeIds(), ...hitShapeIds]),\n\t\t])\n\n\t\tconst scribble = this.editor.scribbles.addScribble({\n\t\t\tcolor: 'muted-1',\n\t\t\tsize: 12,\n\t\t})\n\t\tthis.scribbleId = scribble.id\n\n\t\tthis.update()\n\t}\n\n\tprivate pushPointToScribble() {\n\t\tconst { x, y } = this.editor.inputs.getCurrentPagePoint()\n\t\tthis.editor.scribbles.addPoint(this.scribbleId, x, y)\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setErasingShapes([])\n\t\tthis.editor.scribbles.stop(this.scribbleId)\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerUp(info: TLPointerEventInfo) {\n\t\tthis.complete(info)\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\tupdate() {\n\t\tconst { editor, excludedShapeIds } = this\n\t\tconst erasingShapeIds = editor.getErasingShapeIds()\n\t\tconst currentPagePoint = editor.inputs.getCurrentPagePoint()\n\t\tconst previousPagePoint = editor.inputs.getPreviousPagePoint()\n\n\t\tthis.pushPointToScribble()\n\n\t\t// Otherwise, erasing shapes are all the shapes that were hit before plus any new shapes that are hit\n\t\tconst erasing = new Set<TLShapeId>(erasingShapeIds)\n\t\tconst minDist = this.editor.getHitTestMargin()\n\n\t\t// Create bounds around line segment with margin\n\t\tconst lineBounds = Box.FromPoints([previousPagePoint, currentPagePoint]).expandBy(minDist)\n\t\tconst candidateIds = editor.getShapeIdsInsideBounds(lineBounds)\n\n\t\t// Early return if no candidates - avoid expensive getCurrentPageRenderingShapesSorted()\n\t\tif (candidateIds.size === 0) {\n\t\t\teditor.setErasingShapes(Array.from(erasing))\n\t\t\treturn\n\t\t}\n\n\t\tconst allShapes = editor.getCurrentPageRenderingShapesSorted()\n\t\tconst currentPageShapes = allShapes.filter((shape) => candidateIds.has(shape.id))\n\n\t\tfor (const shape of currentPageShapes) {\n\t\t\tif (editor.isShapeOfType(shape, 'group')) continue\n\n\t\t\t// Avoid testing masked shapes, unless the pointer is inside the mask\n\t\t\tconst pageMask = editor.getShapeMask(shape.id)\n\t\t\tif (pageMask && !pointInPolygon(currentPagePoint, pageMask)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Hit test the shape using a line segment\n\t\t\tconst geometry = editor.getShapeGeometry(shape)\n\t\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\t\tif (!geometry || !pageTransform) continue\n\t\t\tconst pt = pageTransform.clone().invert()\n\t\t\tconst A = pt.applyToPoint(previousPagePoint)\n\t\t\tconst B = pt.applyToPoint(currentPagePoint)\n\n\t\t\t// If the line segment is entirely above / below / left / right of the shape's bounding box, skip the hit test\n\t\t\tconst { bounds } = geometry\n\t\t\tif (\n\t\t\t\tbounds.minX - minDist > Math.max(A.x, B.x) ||\n\t\t\t\tbounds.minY - minDist > Math.max(A.y, B.y) ||\n\t\t\t\tbounds.maxX + minDist < Math.min(A.x, B.x) ||\n\t\t\t\tbounds.maxY + minDist < Math.min(A.y, B.y)\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If a shape is hit and is part of a group, erase the group\n\t\t\t// If we started erasing inside a group's bounds, erase child shapes (or nested groups) inside it\n\t\t\tif (geometry.hitTestLineSegment(A, B, minDist)) {\n\t\t\t\tconst outermost = editor.getOutermostSelectableShape(shape)\n\t\t\t\tif (excludedShapeIds.has(outermost.id)) {\n\t\t\t\t\terasing.add(\n\t\t\t\t\t\teditor.getOutermostSelectableShape(shape, (s) => !excludedShapeIds.has(s.id)).id\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\terasing.add(outermost.id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Remove the hit shapes, except if they're in the list of excluded shapes\n\t\t// (these excluded shapes will be any frames or groups the pointer was inside of\n\t\t// when the user started erasing)\n\t\tthis.editor.setErasingShapes([...erasing].filter((id) => !excludedShapeIds.has(id)))\n\t}\n\n\tcomplete(info?: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\teditor.deleteShapes(editor.getCurrentPageState().erasingShapeIds)\n\t\tthis.parent.transition('idle', info)\n\t}\n\n\tcancel() {\n\t\tconst { editor } = this\n\t\teditor.bailToMark(this.markId)\n\t\tthis.parent.transition('idle', this.info)\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }