tldraw 5.2.0-next.e2b8d10bf10e → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +20294 -0
- package/README.md +13 -1
- package/RELEASE_NOTES.md +2908 -0
- package/dist-cjs/index.d.ts +110 -23
- package/dist-cjs/index.js +3 -3
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawImage.js +0 -1
- package/dist-cjs/lib/TldrawImage.js.map +2 -2
- package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js +2 -1
- package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
- package/dist-cjs/lib/defaultEmbedDefinitions.js +3 -2
- package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
- package/dist-cjs/lib/defaultOverlayUtils.js +11 -11
- package/dist-cjs/lib/defaultOverlayUtils.js.map +2 -2
- package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js +76 -64
- package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js.map +2 -2
- package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js +1 -1
- package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js.map +2 -2
- package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js +107 -0
- package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js.map +7 -0
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +3 -0
- package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +12 -3
- package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js +54 -6
- package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +2 -1
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/getPath.js +1 -1
- package/dist-cjs/lib/shapes/draw/getPath.js.map +2 -2
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +45 -47
- package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +2 -1
- package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeTool.js +23 -18
- package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +3 -0
- package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +11 -7
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -11
- package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +5 -1
- package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +31 -41
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/noteHelpers.js +1 -1
- package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +28 -6
- package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +3 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +3 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js +17 -3
- package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/core.js +424 -0
- package/dist-cjs/lib/shapes/shared/freehand/core.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/freehand/fmt.js +106 -0
- package/dist-cjs/lib/shapes/shared/freehand/fmt.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/freehand/getStroke.js +6 -6
- package/dist-cjs/lib/shapes/shared/freehand/getStroke.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js +320 -86
- package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js +20 -96
- package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/freehand/svg.js +55 -19
- package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/freehand/svgInk.js +189 -97
- package/dist-cjs/lib/shapes/shared/freehand/svgInk.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/freehand/types.js.map +1 -1
- package/dist-cjs/lib/shapes/shared/interpolate-props.js +6 -2
- package/dist-cjs/lib/shapes/shared/interpolate-props.js.map +2 -2
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +3 -0
- package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/HandTool.js +16 -6
- package/dist-cjs/lib/tools/HandTool/HandTool.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js +10 -1
- package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js.map +2 -2
- package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js +84 -0
- package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js.map +7 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +12 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +4 -0
- package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +107 -46
- package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
- package/dist-cjs/lib/ui/TldrawUi.js +4 -3
- package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
- package/dist-cjs/lib/ui/components/A11y.js +2 -0
- package/dist-cjs/lib/ui/components/A11y.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +14 -1
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js +0 -6
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +2 -2
- package/dist-cjs/lib/ui/components/Dialogs.js +17 -37
- package/dist-cjs/lib/ui/components/Dialogs.js.map +2 -2
- package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js +19 -4
- package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +12 -2
- package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +47 -70
- package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +25 -46
- package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.js.map +2 -2
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js +1 -1
- package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +1 -0
- package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js +7 -0
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js +2 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +14 -3
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +2 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +2 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js +0 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js +1 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js +70 -1
- package/dist-cjs/lib/ui/context/actions.js.map +2 -2
- package/dist-cjs/lib/ui/context/asset-urls.js +5 -8
- package/dist-cjs/lib/ui/context/asset-urls.js.map +2 -2
- package/dist-cjs/lib/ui/context/events.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +13 -0
- package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
- package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
- package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/embeds/embeds.js +4 -4
- package/dist-cjs/lib/utils/embeds/embeds.js.map +2 -2
- package/dist-cjs/lib/utils/test-helpers.js +2 -2
- package/dist-cjs/lib/utils/test-helpers.js.map +2 -2
- package/dist-cjs/lib/utils/text/richText.js +3 -1
- package/dist-cjs/lib/utils/text/richText.js.map +2 -2
- package/dist-esm/index.d.mts +110 -23
- package/dist-esm/index.mjs +5 -3
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawImage.mjs +0 -1
- package/dist-esm/lib/TldrawImage.mjs.map +2 -2
- package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs +2 -1
- package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
- package/dist-esm/lib/defaultEmbedDefinitions.mjs +3 -2
- package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
- package/dist-esm/lib/defaultOverlayUtils.mjs +11 -11
- package/dist-esm/lib/defaultOverlayUtils.mjs.map +2 -2
- package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs +77 -65
- package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs.map +2 -2
- package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs +1 -1
- package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs.map +2 -2
- package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs +87 -0
- package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs.map +7 -0
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +9 -1
- package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +15 -3
- package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +54 -6
- package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -1
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/getPath.mjs +1 -1
- package/dist-esm/lib/shapes/draw/getPath.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +46 -47
- package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +2 -1
- package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs +23 -18
- package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +2 -1
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +4 -0
- package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +12 -7
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -11
- package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +6 -1
- package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +32 -42
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/noteHelpers.mjs +1 -1
- package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +29 -6
- package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +3 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +4 -3
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs +17 -3
- package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/core.mjs +404 -0
- package/dist-esm/lib/shapes/shared/freehand/core.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/freehand/fmt.mjs +86 -0
- package/dist-esm/lib/shapes/shared/freehand/fmt.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs +7 -7
- package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs +332 -86
- package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs.map +3 -3
- package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs +31 -96
- package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs.map +3 -3
- package/dist-esm/lib/shapes/shared/freehand/svg.mjs +55 -19
- package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs +214 -99
- package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs.map +3 -3
- package/dist-esm/lib/shapes/shared/interpolate-props.mjs +6 -2
- package/dist-esm/lib/shapes/shared/interpolate-props.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +4 -0
- package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/HandTool.mjs +16 -6
- package/dist-esm/lib/tools/HandTool/HandTool.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs +10 -1
- package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs.map +2 -2
- package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs +64 -0
- package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs.map +7 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +12 -0
- package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +4 -0
- package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +108 -46
- package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
- package/dist-esm/lib/ui/TldrawUi.mjs +4 -3
- package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
- package/dist-esm/lib/ui/components/A11y.mjs +2 -0
- package/dist-esm/lib/ui/components/A11y.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +21 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs +0 -6
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Dialogs.mjs +19 -39
- package/dist-esm/lib/ui/components/Dialogs.mjs.map +2 -2
- package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs +20 -5
- package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +12 -2
- package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +47 -70
- package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +25 -46
- package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.mjs.map +2 -2
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs +1 -1
- package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +1 -0
- package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +7 -0
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs +2 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +14 -3
- package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +2 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +2 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs +0 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs +1 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs +71 -1
- package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
- package/dist-esm/lib/ui/context/asset-urls.mjs +5 -8
- package/dist-esm/lib/ui/context/asset-urls.mjs.map +2 -2
- package/dist-esm/lib/ui/context/events.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +13 -0
- package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
- package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
- package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/embeds/embeds.mjs +4 -4
- package/dist-esm/lib/utils/embeds/embeds.mjs.map +2 -2
- package/dist-esm/lib/utils/test-helpers.mjs +2 -2
- package/dist-esm/lib/utils/test-helpers.mjs.map +2 -2
- package/dist-esm/lib/utils/text/richText.mjs +3 -1
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/package.json +11 -6
- package/src/index.ts +6 -1
- package/src/lib/TldrawImage.tsx +2 -2
- package/src/lib/bindings/arrow/ArrowBindingUtil.ts +5 -1
- package/src/lib/defaultEmbedDefinitions.ts +28 -6
- package/src/lib/defaultOverlayUtils.ts +11 -11
- package/src/lib/overlays/SelectionForegroundOverlayUtil.ts +91 -72
- package/src/lib/overlays/ShapeHandleOverlayUtil.ts +3 -1
- package/src/lib/overlays/ShapeIndicatorOverlayUtil.ts +133 -0
- package/src/lib/shapes/arrow/toolStates/Pointing.tsx +11 -1
- package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +16 -3
- package/src/lib/shapes/bookmark/bookmarks.ts +105 -15
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +7 -5
- package/src/lib/shapes/draw/getPath.ts +1 -1
- package/src/lib/shapes/draw/toolStates/Drawing.ts +55 -48
- package/src/lib/shapes/embed/EmbedShapeUtil.tsx +14 -4
- package/src/lib/shapes/frame/FrameShapeTool.ts +35 -24
- package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +46 -0
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +2 -1
- package/src/lib/shapes/geo/toolStates/Pointing.ts +5 -0
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +14 -11
- package/src/lib/shapes/image/ImageShapeUtil.tsx +7 -11
- package/src/lib/shapes/line/LineShapeTool.test.ts +41 -0
- package/src/lib/shapes/line/toolStates/Pointing.ts +12 -2
- package/src/lib/shapes/note/NoteShapeUtil.tsx +36 -51
- package/src/lib/shapes/note/noteCloning.test.ts +5 -5
- package/src/lib/shapes/note/noteHelpers.ts +1 -1
- package/src/lib/shapes/note/toolStates/Pointing.ts +40 -7
- package/src/lib/shapes/shared/PlainTextLabel.tsx +3 -3
- package/src/lib/shapes/shared/RichTextLabel.tsx +4 -3
- package/src/lib/shapes/shared/defaultStyleDefs.tsx +25 -3
- package/src/lib/shapes/shared/freehand/core.ts +547 -0
- package/src/lib/shapes/shared/freehand/fmt.ts +106 -0
- package/src/lib/shapes/shared/freehand/getStroke.ts +7 -7
- package/src/lib/shapes/shared/freehand/getStrokeOutlinePoints.ts +451 -139
- package/src/lib/shapes/shared/freehand/getStrokePoints.ts +33 -153
- package/src/lib/shapes/shared/freehand/svg.ts +72 -23
- package/src/lib/shapes/shared/freehand/svgInk.ts +281 -136
- package/src/lib/shapes/shared/freehand/types.ts +0 -1
- package/src/lib/shapes/shared/interpolate-props.ts +6 -2
- package/src/lib/shapes/text/toolStates/Pointing.ts +5 -0
- package/src/lib/tools/HandTool/HandTool.ts +19 -6
- package/src/lib/tools/HandTool/childStates/Dragging.ts +17 -1
- package/src/lib/tools/HandTool/childStates/OneFingerZooming.ts +79 -0
- package/src/lib/tools/SelectTool/childStates/Idle.ts +16 -2
- package/src/lib/tools/SelectTool/childStates/PointingShape.ts +8 -0
- package/src/lib/tools/SelectTool/childStates/Resizing.ts +137 -49
- package/src/lib/ui/TldrawUi.tsx +3 -3
- package/src/lib/ui/components/A11y.tsx +2 -0
- package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +32 -2
- package/src/lib/ui/components/ContextMenu/DefaultContextMenuContent.tsx +5 -6
- package/src/lib/ui/components/DefaultFollowingIndicator.tsx +2 -2
- package/src/lib/ui/components/Dialogs.tsx +20 -23
- package/src/lib/ui/components/HelperButtons/BackToContent.tsx +32 -4
- package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +10 -0
- package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +60 -74
- package/src/lib/ui/components/Minimap/MinimapManager.ts +35 -57
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.tsx +2 -2
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuContent.tsx +2 -1
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.tsx +2 -1
- package/src/lib/ui/components/SharePanel/DefaultPeopleMenuItem.tsx +3 -3
- package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +5 -0
- package/src/lib/ui/components/menu-items.tsx +9 -0
- package/src/lib/ui/components/primitives/TldrawUiDropdownMenu.tsx +3 -1
- package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +15 -3
- package/src/lib/ui/components/primitives/TldrawUiInput.tsx +4 -0
- package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +7 -0
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +0 -2
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.tsx +0 -1
- package/src/lib/ui/context/actions.tsx +88 -1
- package/src/lib/ui/context/asset-urls.tsx +9 -8
- package/src/lib/ui/context/events.tsx +2 -0
- package/src/lib/ui/hooks/useKeyboardShortcuts.ts +45 -7
- package/src/lib/ui/hooks/useTools.tsx +7 -1
- package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
- package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +20 -9
- package/src/lib/utils/embeds/embeds.test.ts +14 -12
- package/src/lib/utils/embeds/embeds.ts +10 -4
- package/src/lib/utils/test-helpers.ts +3 -3
- package/src/lib/utils/text/richText.test.ts +36 -0
- package/src/lib/utils/text/richText.ts +9 -1
- package/src/test/HandTool.test.ts +93 -0
- package/src/test/ImageShapeUtil.test.ts +88 -0
- package/src/test/SelectTool.test.ts +102 -1
- package/src/test/__snapshots__/drawing.test.ts.snap +2 -1
- package/src/test/attribution.test.ts +40 -29
- package/src/test/bookmark-shapes.test.ts +256 -28
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
- package/src/test/commands/clipboardCallbacks.test.ts +3 -1
- package/src/test/commands/getSvgString.test.ts +30 -0
- package/src/test/commands/penmode.test.ts +97 -1
- package/src/test/commands/setCamera.test.ts +12 -0
- package/src/test/dragFromToolbar.test.ts +67 -0
- package/src/test/drawing.test.ts +48 -7
- package/src/test/frames.test.ts +72 -0
- package/src/test/freehand/corpus/presets.ts +71 -0
- package/src/test/freehand/corpus/real.ts +370 -0
- package/src/test/freehand/corpus/strokes.ts +50 -0
- package/src/test/freehand/svg.test.ts +20 -0
- package/src/test/freehand/wellformed.test.ts +30 -0
- package/src/test/long-press-create.test.ts +102 -0
- package/src/test/overlays/CollaboratorBrushOverlayUtil.test.ts +4 -4
- package/src/test/overlays/CollaboratorCursorOverlayUtil.test.ts +18 -18
- package/src/test/overlays/CollaboratorHintOverlayUtil.test.ts +7 -7
- package/src/test/overlays/CollaboratorScribbleOverlayUtil.test.ts +3 -3
- package/src/test/overlays/OverlayManager.test.ts +1 -1
- package/src/test/overlays/SelectionForegroundOverlayUtil.test.ts +58 -0
- package/src/test/overlays/ShapeHandleOverlayUtil.test.ts +2 -2
- package/src/test/overlays/ShapeIndicatorOverlayUtil.test.ts +36 -0
- package/src/test/resizing.test.ts +250 -0
- package/src/test/right-click-context-menu.test.ts +90 -0
- package/src/test/rightClickPanning.test.ts +11 -0
- package/src/test/selection-omnibus.test.ts +15 -2
- package/src/test/spatialIndex.test.ts +132 -0
- package/src/test/translating.test.ts +0 -6
- package/src/test/ui/ContextMenu.test.tsx +146 -3
- package/src/test/ui/Dialogs.test.tsx +256 -0
- package/src/test/ui/copyHoveredStyles.test.tsx +154 -0
- package/src/test/ui/keyboardShortcuts.test.tsx +266 -0
- package/tldraw.css +35 -12
- package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js +0 -100
- package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js.map +0 -7
- package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs +0 -80
- package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs.map +0 -7
- package/src/lib/shapes/shared/freehand/setStrokePointRadii.ts +0 -127
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Vec } from '@tldraw/editor'
|
|
1
2
|
import { vi } from 'vitest'
|
|
2
3
|
import { HandTool } from '../lib/tools/HandTool/HandTool'
|
|
3
4
|
import { TestEditor } from './TestEditor'
|
|
@@ -18,6 +19,43 @@ afterEach(() => {
|
|
|
18
19
|
vi.useFakeTimers()
|
|
19
20
|
|
|
20
21
|
describe(HandTool, () => {
|
|
22
|
+
it('Uses one-finger zoom when coarse pointer double-taps, holds, and drags', () => {
|
|
23
|
+
editor.setCurrentTool('hand')
|
|
24
|
+
editor.updateInstanceState({ isCoarsePointer: true })
|
|
25
|
+
|
|
26
|
+
// Double-tap, holding the second press down.
|
|
27
|
+
editor.pointerDown(0, 0).pointerUp(0, 0)
|
|
28
|
+
editor.pointerDown(0, 0)
|
|
29
|
+
// On the second press we're still just pointing — the zoom hasn't started yet.
|
|
30
|
+
editor.expectToBeIn('hand.pointing')
|
|
31
|
+
|
|
32
|
+
// One-finger zoom begins when the multi-click timer settles with the pointer
|
|
33
|
+
// still held down (the `settle-down` phase).
|
|
34
|
+
vi.advanceTimersByTime(editor.options.multiClickDurationMs)
|
|
35
|
+
editor.expectToBeIn('hand.one_finger_zooming')
|
|
36
|
+
|
|
37
|
+
const before = editor.getCamera().z
|
|
38
|
+
editor.pointerMove(0, 100).pointerUp(0, 100).forceTick()
|
|
39
|
+
expect(editor.getCamera().z).toBeGreaterThan(before)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('Zooms in (not one-finger zoom) when a coarse pointer double-taps and releases', () => {
|
|
43
|
+
editor.setCurrentTool('hand')
|
|
44
|
+
editor.updateInstanceState({ isCoarsePointer: true })
|
|
45
|
+
|
|
46
|
+
// Double-tap and release the second press before the timer settles.
|
|
47
|
+
editor.pointerDown(0, 0).pointerUp(0, 0)
|
|
48
|
+
editor.pointerDown(0, 0).pointerUp(0, 0)
|
|
49
|
+
// Releasing means we never enter the one-finger zoom drag state.
|
|
50
|
+
editor.expectToBeIn('hand.idle')
|
|
51
|
+
|
|
52
|
+
// The `settle-up` phase zooms in by one step.
|
|
53
|
+
const before = editor.getZoomLevel()
|
|
54
|
+
vi.advanceTimersByTime(editor.options.multiClickDurationMs)
|
|
55
|
+
vi.advanceTimersByTime(600) // let the zoom animation finish
|
|
56
|
+
expect(editor.getZoomLevel()).toBeGreaterThan(before)
|
|
57
|
+
})
|
|
58
|
+
|
|
21
59
|
it('Double taps to zoom in', () => {
|
|
22
60
|
editor.setCurrentTool('hand')
|
|
23
61
|
expect(editor.getZoomLevel()).toBe(1)
|
|
@@ -129,6 +167,16 @@ describe('When in the dragging state', () => {
|
|
|
129
167
|
editor.pointerUp()
|
|
130
168
|
})
|
|
131
169
|
|
|
170
|
+
it('Does not zoom when momentum panning on release', () => {
|
|
171
|
+
editor.setCurrentTool('hand')
|
|
172
|
+
editor.pointerDown(0, 0)
|
|
173
|
+
editor.pointerMove(50, 50)
|
|
174
|
+
editor.inputs.setPointerVelocity(new Vec(1, 1))
|
|
175
|
+
editor.pointerUp().forceTick()
|
|
176
|
+
|
|
177
|
+
expect(editor.getCamera().z).toBe(1)
|
|
178
|
+
})
|
|
179
|
+
|
|
132
180
|
// it('Moves the camera with inertia on pointer up', () => {
|
|
133
181
|
// Can't test this—x is set to Inifnity in tests
|
|
134
182
|
// editor.setCurrentTool('hand')
|
|
@@ -160,4 +208,49 @@ describe('When in the dragging state', () => {
|
|
|
160
208
|
editor.cancel()
|
|
161
209
|
editor.expectToBeIn('hand.idle')
|
|
162
210
|
})
|
|
211
|
+
|
|
212
|
+
it('Returns to the idle state on interrupt', () => {
|
|
213
|
+
editor.setCurrentTool('hand')
|
|
214
|
+
editor.pointerDown(50, 50)
|
|
215
|
+
editor.pointerMove(100, 100)
|
|
216
|
+
editor.expectToBeIn('hand.dragging')
|
|
217
|
+
editor.interrupt()
|
|
218
|
+
editor.expectToBeIn('hand.idle')
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('Ends the pan when a second touch starts, without jumping the camera', () => {
|
|
222
|
+
editor.setCurrentTool('hand')
|
|
223
|
+
editor.pointerDown(50, 50)
|
|
224
|
+
editor.pointerMove(100, 100)
|
|
225
|
+
editor.expectToBeIn('hand.dragging')
|
|
226
|
+
expect(editor.getCamera()).toMatchObject({ x: 50, y: 50 })
|
|
227
|
+
|
|
228
|
+
// A second finger lands far from the first, resetting the input origin.
|
|
229
|
+
editor.pointerDown(200, 0)
|
|
230
|
+
editor.expectToBeIn('hand.idle')
|
|
231
|
+
|
|
232
|
+
// Movement before pinch_start arrives no longer pans the camera.
|
|
233
|
+
editor.pointerMove(210, 10)
|
|
234
|
+
expect(editor.getCamera()).toMatchObject({ x: 50, y: 50 })
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('Does not resume the pan after a pinch ends', () => {
|
|
238
|
+
editor.setCurrentTool('hand')
|
|
239
|
+
editor.pointerDown(50, 50)
|
|
240
|
+
editor.pointerMove(100, 100)
|
|
241
|
+
editor.expectToBeIn('hand.dragging')
|
|
242
|
+
|
|
243
|
+
// A second finger lands and starts a pinch.
|
|
244
|
+
editor.pointerDown(200, 0)
|
|
245
|
+
editor.expectToBeIn('hand.idle')
|
|
246
|
+
editor.pinchStart(150, 50, 1, 0, 0, 0)
|
|
247
|
+
editor.pinchTo(150, 50, 2, 0, 0, 0)
|
|
248
|
+
editor.pinchEnd(150, 50, 2, 0, 0, 0)
|
|
249
|
+
editor.expectToBeIn('hand.idle')
|
|
250
|
+
|
|
251
|
+
// Moving the remaining finger does not pan the camera.
|
|
252
|
+
const { x, y } = editor.getCamera()
|
|
253
|
+
editor.pointerMove(120, 120)
|
|
254
|
+
expect(editor.getCamera()).toMatchObject({ x, y })
|
|
255
|
+
})
|
|
163
256
|
})
|
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
import { createShapeId, Ellipse2d, Rectangle2d, TLImageShape } from '@tldraw/editor'
|
|
2
2
|
import { TestEditor } from './TestEditor'
|
|
3
3
|
|
|
4
|
+
const ids = {
|
|
5
|
+
image: createShapeId('image'),
|
|
6
|
+
box: createShapeId('box'),
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function createCroppedImageWithSibling() {
|
|
10
|
+
editor.createShapes([
|
|
11
|
+
{
|
|
12
|
+
id: ids.image,
|
|
13
|
+
type: 'image',
|
|
14
|
+
x: 0,
|
|
15
|
+
y: 0,
|
|
16
|
+
props: {
|
|
17
|
+
w: 100,
|
|
18
|
+
h: 100,
|
|
19
|
+
assetId: null,
|
|
20
|
+
playing: true,
|
|
21
|
+
url: '',
|
|
22
|
+
// crop to the top-left quadrant of the source image
|
|
23
|
+
crop: {
|
|
24
|
+
topLeft: { x: 0, y: 0 },
|
|
25
|
+
bottomRight: { x: 0.5, y: 0.5 },
|
|
26
|
+
},
|
|
27
|
+
flipX: false,
|
|
28
|
+
flipY: false,
|
|
29
|
+
altText: '',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: ids.box,
|
|
34
|
+
type: 'geo',
|
|
35
|
+
x: 300,
|
|
36
|
+
y: 0,
|
|
37
|
+
props: { w: 100, h: 100 },
|
|
38
|
+
},
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
|
|
4
42
|
let editor: TestEditor
|
|
5
43
|
|
|
6
44
|
beforeEach(() => {
|
|
@@ -153,4 +191,54 @@ describe('ImageShapeUtil', () => {
|
|
|
153
191
|
expect(geometry.isFilled).toBe(true)
|
|
154
192
|
})
|
|
155
193
|
})
|
|
194
|
+
|
|
195
|
+
describe('flipping a cropped image when its group is resized', () => {
|
|
196
|
+
it('mirrors the crop when a grouped cropped image is flipped by drag resize', () => {
|
|
197
|
+
createCroppedImageWithSibling()
|
|
198
|
+
editor.select(ids.image, ids.box)
|
|
199
|
+
editor.groupShapes(editor.getSelectedShapeIds())
|
|
200
|
+
|
|
201
|
+
// Flip the group horizontally by dragging a corner handle past the
|
|
202
|
+
// opposite edge. The drag scale is not exactly -1, which is the case
|
|
203
|
+
// that previously left the crop un-mirrored.
|
|
204
|
+
editor.resizeSelection({ scaleX: -2, scaleY: 1 }, 'top_left')
|
|
205
|
+
|
|
206
|
+
const image = editor.getShape<TLImageShape>(ids.image)!
|
|
207
|
+
expect(image.props.flipX).toBe(true)
|
|
208
|
+
// The crop should be mirrored horizontally and unchanged vertically.
|
|
209
|
+
expect(image.props.crop).toMatchObject({
|
|
210
|
+
topLeft: { x: 0.5, y: 0 },
|
|
211
|
+
bottomRight: { x: 1, y: 0.5 },
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('mirrors the crop vertically when a grouped cropped image is flipped by drag resize', () => {
|
|
216
|
+
createCroppedImageWithSibling()
|
|
217
|
+
editor.select(ids.image, ids.box)
|
|
218
|
+
editor.groupShapes(editor.getSelectedShapeIds())
|
|
219
|
+
|
|
220
|
+
editor.resizeSelection({ scaleX: 1, scaleY: -2 }, 'top_left')
|
|
221
|
+
|
|
222
|
+
const image = editor.getShape<TLImageShape>(ids.image)!
|
|
223
|
+
expect(image.props.flipY).toBe(true)
|
|
224
|
+
expect(image.props.crop).toMatchObject({
|
|
225
|
+
topLeft: { x: 0, y: 0.5 },
|
|
226
|
+
bottomRight: { x: 0.5, y: 1 },
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('matches the flipShapes command result for a grouped cropped image', () => {
|
|
231
|
+
createCroppedImageWithSibling()
|
|
232
|
+
editor.select(ids.image, ids.box)
|
|
233
|
+
editor.groupShapes(editor.getSelectedShapeIds())
|
|
234
|
+
editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
|
|
235
|
+
|
|
236
|
+
const image = editor.getShape<TLImageShape>(ids.image)!
|
|
237
|
+
expect(image.props.flipX).toBe(true)
|
|
238
|
+
expect(image.props.crop).toMatchObject({
|
|
239
|
+
topLeft: { x: 0.5, y: 0 },
|
|
240
|
+
bottomRight: { x: 1, y: 0.5 },
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
})
|
|
156
244
|
})
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
IndexKey,
|
|
3
|
+
ShapeUtil,
|
|
4
|
+
TLArrowShape,
|
|
5
|
+
TLFrameShape,
|
|
6
|
+
createShapeId,
|
|
7
|
+
toRichText,
|
|
8
|
+
} from '@tldraw/editor'
|
|
2
9
|
import { vi } from 'vitest'
|
|
3
10
|
import { defaultHandleExternalTldrawContent } from '../lib/defaultExternalContentHandlers'
|
|
4
11
|
import { defaultOverlayUtils } from '../lib/defaultOverlayUtils'
|
|
@@ -115,6 +122,44 @@ describe.skip('Edit on type', () => {
|
|
|
115
122
|
})
|
|
116
123
|
})
|
|
117
124
|
|
|
125
|
+
describe('TLSelectTool.PointingShape when the shape is deleted mid-click', () => {
|
|
126
|
+
// Reproduces https://github.com/tldraw/tldraw/issues/8558: a remote user,
|
|
127
|
+
// undo, or other actor can delete the pointed-at shape between pointer down
|
|
128
|
+
// and pointer up. The tool should bail to idle instead of crashing.
|
|
129
|
+
|
|
130
|
+
it('does not crash on pointer up when an already-selected shape is deleted', () => {
|
|
131
|
+
// pre-selecting the shape means didSelectOnEnter is false, so pointer up
|
|
132
|
+
// runs the selection logic that dereferences the (now deleted) shape
|
|
133
|
+
editor.select(ids.box1)
|
|
134
|
+
const shape = editor.getShape(ids.box1)!
|
|
135
|
+
|
|
136
|
+
editor.pointerDown(shape.x + 10, shape.y + 10, { target: 'shape', shape })
|
|
137
|
+
editor.expectToBeIn('select.pointing_shape')
|
|
138
|
+
|
|
139
|
+
editor.deleteShapes([ids.box1])
|
|
140
|
+
|
|
141
|
+
expect(() => editor.pointerUp(shape.x + 10, shape.y + 10)).not.toThrow()
|
|
142
|
+
editor.expectToBeIn('select.idle')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('does not crash on pointer up when a ctrl-clicked shape is deleted', () => {
|
|
146
|
+
// ctrl/accel on enter also leaves didSelectOnEnter false
|
|
147
|
+
const shape = editor.getShape(ids.box1)!
|
|
148
|
+
|
|
149
|
+
editor.pointerDown(shape.x + 10, shape.y + 10, {
|
|
150
|
+
target: 'shape',
|
|
151
|
+
shape,
|
|
152
|
+
accelKey: true,
|
|
153
|
+
})
|
|
154
|
+
editor.expectToBeIn('select.pointing_shape')
|
|
155
|
+
|
|
156
|
+
editor.deleteShapes([ids.box1])
|
|
157
|
+
|
|
158
|
+
expect(() => editor.pointerUp(shape.x + 10, shape.y + 10)).not.toThrow()
|
|
159
|
+
editor.expectToBeIn('select.idle')
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
118
163
|
describe('TLSelectTool.Translating', () => {
|
|
119
164
|
it('Enters from pointing and exits to idle', () => {
|
|
120
165
|
const shape = editor.getShape(ids.box1)
|
|
@@ -510,6 +555,34 @@ describe('When pressing enter on a selected shape', () => {
|
|
|
510
555
|
})
|
|
511
556
|
})
|
|
512
557
|
|
|
558
|
+
describe('When undo/redo restores an invalid editing shape', () => {
|
|
559
|
+
// Regression: https://github.com/tldraw/tldraw/issues/9113
|
|
560
|
+
// Setting the editing shape is not recorded in history, but clearing it on delete is.
|
|
561
|
+
// When create + edit + delete of the same shape collapse into a single history entry,
|
|
562
|
+
// the shape's add/remove cancel out while the editingShapeId update survives. Undoing
|
|
563
|
+
// then restores editingShapeId pointing at a shape that no longer exists, which used to
|
|
564
|
+
// crash with "Entered editing state without an editing shape". The editor must never
|
|
565
|
+
// enter the editing state without a valid editing shape.
|
|
566
|
+
it('does not crash when undo restores editingShapeId for a deleted shape', () => {
|
|
567
|
+
const id = createShapeId()
|
|
568
|
+
editor.markHistoryStoppingPoint('start')
|
|
569
|
+
editor.createShape({ id, type: 'geo', x: 0, y: 0, props: { w: 100, h: 100 } })
|
|
570
|
+
editor.setEditingShape(id)
|
|
571
|
+
editor.deleteShapes([id])
|
|
572
|
+
|
|
573
|
+
expect(() => editor.undo()).not.toThrow()
|
|
574
|
+
|
|
575
|
+
editor.expectToBeIn('select.idle')
|
|
576
|
+
expect(editor.getEditingShapeId()).toBe(null)
|
|
577
|
+
expect(editor.getShape(id)).toBeUndefined()
|
|
578
|
+
|
|
579
|
+
// Redoing back through the same history entry must also stay safe.
|
|
580
|
+
expect(() => editor.redo()).not.toThrow()
|
|
581
|
+
editor.expectToBeIn('select.idle')
|
|
582
|
+
expect(editor.getEditingShapeId()).toBe(null)
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
|
|
513
586
|
// it('selects the child of a group', () => {
|
|
514
587
|
// const id1 = createShapeId()
|
|
515
588
|
// const id2 = createShapeId()
|
|
@@ -643,6 +716,34 @@ describe('When double clicking a selection handle that registers as a canvas eve
|
|
|
643
716
|
expect(spy).toHaveBeenCalledTimes(1)
|
|
644
717
|
expect(spy.mock.calls[0][1]).toMatchObject({ target: 'selection', handle: 'bottom_right' })
|
|
645
718
|
})
|
|
719
|
+
|
|
720
|
+
it('Routes a canvas-targeted double-click on an arrow handle to onDoubleClickHandle', () => {
|
|
721
|
+
const id = createShapeId()
|
|
722
|
+
overlayEditor
|
|
723
|
+
.createShapes([
|
|
724
|
+
{
|
|
725
|
+
id,
|
|
726
|
+
type: 'arrow',
|
|
727
|
+
x: 100,
|
|
728
|
+
y: 100,
|
|
729
|
+
props: { start: { x: 0, y: 0 }, end: { x: 100, y: 100 } },
|
|
730
|
+
},
|
|
731
|
+
])
|
|
732
|
+
.select(id)
|
|
733
|
+
|
|
734
|
+
expect(overlayEditor.getShape<TLArrowShape>(id)!.props.arrowheadEnd).toBe('arrow')
|
|
735
|
+
|
|
736
|
+
// Double-click on the end handle without specifying target — defaults to
|
|
737
|
+
// target: 'canvas', the payload a real DOM double-click produces when the
|
|
738
|
+
// press lands on the handle overlay. This should toggle the arrowhead.
|
|
739
|
+
overlayEditor.doubleClick(200, 200)
|
|
740
|
+
|
|
741
|
+
expect(overlayEditor.getShape<TLArrowShape>(id)!.props.arrowheadEnd).toBe('none')
|
|
742
|
+
|
|
743
|
+
overlayEditor.doubleClick(200, 200)
|
|
744
|
+
|
|
745
|
+
expect(overlayEditor.getShape<TLArrowShape>(id)!.props.arrowheadEnd).toBe('arrow')
|
|
746
|
+
})
|
|
646
747
|
})
|
|
647
748
|
|
|
648
749
|
describe('When editing shapes', () => {
|
|
@@ -19,7 +19,8 @@ exports[`Draws a bunch > draw shape 1`] = `
|
|
|
19
19
|
"scaleY": 1,
|
|
20
20
|
"segments": [
|
|
21
21
|
{
|
|
22
|
-
"
|
|
22
|
+
"dim": 2,
|
|
23
|
+
"path": "AAAAAAAAAAAAPAAAAEIAAABGALyASADCgEkAxgBMAMmASwDJgEqAyoBLAMyASoDMAEaAygBEgMoARQDMADwAygA8AMYAAADIADwAxwC8AMYAwADFAMIAxADFAMAAxgC8AMYAAADIAACAyABEgMgAR4DIgEgAyABKAMQASgDCgEoAvABLAABATAA8QE0ARgBNAEdATABIQEyAScBMgEgATQBIgEwAR8BMAEJATAA8AEwAAIBMAMUATADKAEkAzABIwM+ASIDJADyAyQAAAMkAAIDIAACAyADAAMYAwgDAAMYAAADGAESAyABKgMkAS4DLwEwAzIBNQMxATkDNgE5AzsBMQM3ATIDNgEsAzQBKAM2ASsDNgEgAzQBFwMwAQoDLADyAyQAAAMgAwADGAMUAwgDFALwAxQC8AMgAPADIAEYAxgBJAMYASgDHgEwAx0BNAMVATgDEAE8AwkBPAABATwAAwE4APEBOAEfATQBIAEwASIBJAEmASoBIAEgASABFAEgAQABGAAAASAC8gEgAxYBIAMkAR4DJAEaAygBGQMwARUDMAEJAzABAgMsAPIDIAAAAxgAAAMcAwADEAMIAxADAAAAAwgAAAMIAPADAAEAAwABHAMAASAC8gEkAvIBJAAAASQBCgEoARgBKAEaASABIgEgARQBEAEYAQgBIAEAARgAAAEYAwgBFAMkARQDKAEAAyQBCgMoAQADKAEAAyQA8AMkAPADHADwAxgAAAMIAAADAAAAAvAAAAEIAAABFAAAASAAAgEgAAABHAAAARQAAAEUAPABFAEIARQBAAEIAQgA8AEIAPABEAAAARQDCAEQAxABCAMIAQgDEAEIAwgBAAMAAQADAAEAAAAAAAAAAPAAAAAAAPAA8ADwAPABAAAAAQAA8ADwAAAA8ADwAPAA8AAAAPAAAADwAxgBAgMgAQIDIAEIAyABAAMYAQADHAEAAxgBEAMYAQgDEAEQAvABFAAAARQAAAEcAPIBKAEgASwBKAEmASQBKgEsASYBLAEhATABEAEsAQEBNAECATgAAAE0AxABMAMlATADMgEzAzEBMAMwASQDLAEUAzABFgMoAQADJAAAAyADEAMaAyAC8AMsAAMDOAEkg0MBNYNDATyDRQE9g0GBQwNCgUKDQ4FHg0cBR4NGATcDNgE0AzoBNgM4ASoDLgEgAzABIgMwAPADHAMAAxoDIAMSAzADAgM0AAEDNAEIA0IBKYNDATADPAE0AzsBMgMxATEDMwEyAyYBLAMaASQDAAEgAAABFADwAQgBCADwARgA8AEQAAABCALwARQC8AEAAvABAALwAPAAAADwAvAAAAAAAPAAA",
|
|
23
24
|
"type": "free",
|
|
24
25
|
},
|
|
25
26
|
],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TLNoteShape,
|
|
3
3
|
UserRecordType,
|
|
4
|
+
atom,
|
|
4
5
|
computed,
|
|
5
6
|
createShapeId,
|
|
6
7
|
createUserId,
|
|
@@ -25,7 +26,7 @@ const ids = {
|
|
|
25
26
|
|
|
26
27
|
describe('getAttributionDisplayName', () => {
|
|
27
28
|
it('returns current user name for own userId', () => {
|
|
28
|
-
const userId = editor.user.
|
|
29
|
+
const userId = editor.user.getExternalId()
|
|
29
30
|
const name = editor.getAttributionDisplayName(userId)
|
|
30
31
|
expect(name).toBe(editor.user.getName())
|
|
31
32
|
})
|
|
@@ -40,8 +41,8 @@ describe('getAttributionDisplayName', () => {
|
|
|
40
41
|
})
|
|
41
42
|
})
|
|
42
43
|
|
|
43
|
-
describe('note shape
|
|
44
|
-
it('sets
|
|
44
|
+
describe('note shape textLastEditedBy', () => {
|
|
45
|
+
it('sets textLastEditedBy as user ID string when richText changes', () => {
|
|
45
46
|
editor.createShapes([
|
|
46
47
|
{
|
|
47
48
|
id: ids.note1,
|
|
@@ -51,7 +52,7 @@ describe('note shape textFirstEditedBy', () => {
|
|
|
51
52
|
},
|
|
52
53
|
])
|
|
53
54
|
|
|
54
|
-
const userId = editor.user.
|
|
55
|
+
const userId = editor.user.getExternalId()
|
|
55
56
|
|
|
56
57
|
editor.updateShape<TLNoteShape>({
|
|
57
58
|
id: ids.note1,
|
|
@@ -60,41 +61,51 @@ describe('note shape textFirstEditedBy', () => {
|
|
|
60
61
|
})
|
|
61
62
|
|
|
62
63
|
const note = editor.getShape<TLNoteShape>(ids.note1)!
|
|
63
|
-
expect(note.props.
|
|
64
|
+
expect(note.props.textLastEditedBy).toBe(userId)
|
|
64
65
|
})
|
|
65
66
|
|
|
66
|
-
it('
|
|
67
|
-
|
|
67
|
+
it('updates textLastEditedBy to the most recent editor', () => {
|
|
68
|
+
const alice = UserRecordType.create({ id: createUserId('user-1'), name: 'Alice' })
|
|
69
|
+
const bob = UserRecordType.create({ id: createUserId('user-2'), name: 'Bob' })
|
|
70
|
+
const currentUser = atom('currentUser', alice)
|
|
71
|
+
const customEditor = new TestEditor(
|
|
72
|
+
{},
|
|
68
73
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
users: {
|
|
75
|
+
currentUser,
|
|
76
|
+
resolve: (userId) =>
|
|
77
|
+
computed('resolve-' + userId, () => {
|
|
78
|
+
if (userId === 'user-1') return alice
|
|
79
|
+
if (userId === 'user-2') return bob
|
|
80
|
+
return null
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
)
|
|
75
85
|
|
|
76
|
-
|
|
86
|
+
customEditor.createShapes([{ id: ids.note1, type: 'note', x: 0, y: 0 }])
|
|
77
87
|
|
|
78
|
-
|
|
88
|
+
// Alice edits the note first
|
|
89
|
+
customEditor.updateShape<TLNoteShape>({
|
|
79
90
|
id: ids.note1,
|
|
80
91
|
type: 'note',
|
|
81
92
|
props: { richText: toRichText('Hello') },
|
|
82
93
|
})
|
|
94
|
+
expect(customEditor.getShape<TLNoteShape>(ids.note1)!.props.textLastEditedBy).toBe('user-1')
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
editor.updateShape<TLNoteShape>({
|
|
96
|
+
// Bob edits the note next — the latest editor wins
|
|
97
|
+
currentUser.set(bob)
|
|
98
|
+
customEditor.updateShape<TLNoteShape>({
|
|
88
99
|
id: ids.note1,
|
|
89
100
|
type: 'note',
|
|
90
101
|
props: { richText: toRichText('Hello world') },
|
|
91
102
|
})
|
|
103
|
+
expect(customEditor.getShape<TLNoteShape>(ids.note1)!.props.textLastEditedBy).toBe('user-2')
|
|
92
104
|
|
|
93
|
-
|
|
94
|
-
expect(note2.props.textFirstEditedBy).toBe(userId)
|
|
105
|
+
customEditor.dispose()
|
|
95
106
|
})
|
|
96
107
|
|
|
97
|
-
it('resets
|
|
108
|
+
it('resets textLastEditedBy to null when text is deleted', () => {
|
|
98
109
|
editor.createShapes([
|
|
99
110
|
{
|
|
100
111
|
id: ids.note1,
|
|
@@ -111,7 +122,7 @@ describe('note shape textFirstEditedBy', () => {
|
|
|
111
122
|
})
|
|
112
123
|
|
|
113
124
|
const note1 = editor.getShape<TLNoteShape>(ids.note1)!
|
|
114
|
-
expect(note1.props.
|
|
125
|
+
expect(note1.props.textLastEditedBy).not.toBeNull()
|
|
115
126
|
|
|
116
127
|
editor.updateShape<TLNoteShape>({
|
|
117
128
|
id: ids.note1,
|
|
@@ -120,10 +131,10 @@ describe('note shape textFirstEditedBy', () => {
|
|
|
120
131
|
})
|
|
121
132
|
|
|
122
133
|
const note2 = editor.getShape<TLNoteShape>(ids.note1)!
|
|
123
|
-
expect(note2.props.
|
|
134
|
+
expect(note2.props.textLastEditedBy).toBeNull()
|
|
124
135
|
})
|
|
125
136
|
|
|
126
|
-
it('does not set
|
|
137
|
+
it('does not set textLastEditedBy when only position changes', () => {
|
|
127
138
|
editor.createShapes([
|
|
128
139
|
{
|
|
129
140
|
id: ids.note1,
|
|
@@ -136,10 +147,10 @@ describe('note shape textFirstEditedBy', () => {
|
|
|
136
147
|
editor.updateShape({ id: ids.note1, type: 'note', x: 100 })
|
|
137
148
|
|
|
138
149
|
const note = editor.getShape<TLNoteShape>(ids.note1)!
|
|
139
|
-
expect(note.props.
|
|
150
|
+
expect(note.props.textLastEditedBy).toBeNull()
|
|
140
151
|
})
|
|
141
152
|
|
|
142
|
-
it('does not set
|
|
153
|
+
it('does not set textLastEditedBy when color changes', () => {
|
|
143
154
|
editor.createShapes([
|
|
144
155
|
{
|
|
145
156
|
id: ids.note1,
|
|
@@ -156,7 +167,7 @@ describe('note shape textFirstEditedBy', () => {
|
|
|
156
167
|
})
|
|
157
168
|
|
|
158
169
|
const note = editor.getShape<TLNoteShape>(ids.note1)!
|
|
159
|
-
expect(note.props.
|
|
170
|
+
expect(note.props.textLastEditedBy).toBeNull()
|
|
160
171
|
})
|
|
161
172
|
})
|
|
162
173
|
|
|
@@ -186,7 +197,7 @@ describe('TLUserStore', () => {
|
|
|
186
197
|
})
|
|
187
198
|
|
|
188
199
|
it('falls back to default user store when none provided', () => {
|
|
189
|
-
const userId = editor.user.
|
|
200
|
+
const userId = editor.user.getExternalId()
|
|
190
201
|
expect(editor.getAttributionUserId()).toBe(userId)
|
|
191
202
|
expect(editor.getAttributionDisplayName(userId)).toBe(editor.user.getName())
|
|
192
203
|
})
|