tldraw 4.1.0 → 4.2.0-canary.118fb314f728

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 (131) hide show
  1. package/dist-cjs/index.d.ts +35 -14
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/defaultEmbedDefinitions.js +2 -25
  5. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  6. package/dist-cjs/lib/defaultExternalContentHandlers.js +8 -31
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +31 -101
  9. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/bookmark/bookmarks.js +138 -0
  11. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +7 -0
  12. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +25 -3
  13. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  14. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +20 -4
  15. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
  16. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +23 -11
  17. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
  18. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +18 -5
  19. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  20. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +21 -9
  21. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  22. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +24 -8
  23. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
  24. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +21 -9
  25. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
  26. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +23 -8
  27. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  28. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +21 -9
  29. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  30. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +26 -11
  31. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  32. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js +12 -9
  33. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js.map +2 -2
  34. package/dist-cjs/lib/ui/components/DefaultDebugPanel.js +1 -1
  35. package/dist-cjs/lib/ui/components/DefaultDebugPanel.js.map +2 -2
  36. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js +6 -2
  37. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js.map +2 -2
  38. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +1 -1
  39. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  40. package/dist-cjs/lib/ui/components/menu-items.js +2 -2
  41. package/dist-cjs/lib/ui/components/menu-items.js.map +1 -1
  42. package/dist-cjs/lib/ui/context/actions.js +23 -29
  43. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  44. package/dist-cjs/lib/ui/hooks/useEditorEvents.js +1 -1
  45. package/dist-cjs/lib/ui/hooks/useEditorEvents.js.map +1 -1
  46. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +4 -4
  47. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +1 -1
  48. package/dist-cjs/lib/ui/version.js +3 -3
  49. package/dist-cjs/lib/ui/version.js.map +1 -1
  50. package/dist-cjs/lib/utils/text/richText.js +5 -6
  51. package/dist-cjs/lib/utils/text/richText.js.map +3 -3
  52. package/dist-esm/index.d.mts +35 -14
  53. package/dist-esm/index.mjs +3 -1
  54. package/dist-esm/index.mjs.map +2 -2
  55. package/dist-esm/lib/defaultEmbedDefinitions.mjs +2 -25
  56. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  57. package/dist-esm/lib/defaultExternalContentHandlers.mjs +8 -31
  58. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  59. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +34 -101
  60. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  61. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +124 -0
  62. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +7 -0
  63. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +26 -3
  64. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  65. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +20 -4
  66. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
  67. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +23 -11
  68. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
  69. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +18 -5
  70. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  71. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +21 -9
  72. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  73. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +24 -8
  74. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
  75. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +21 -9
  76. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
  77. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +23 -8
  78. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  79. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +21 -9
  80. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  81. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +26 -11
  82. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  83. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs +12 -9
  84. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs.map +2 -2
  85. package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs +1 -1
  86. package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs.map +2 -2
  87. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs +6 -2
  88. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs.map +2 -2
  89. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +1 -1
  90. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  91. package/dist-esm/lib/ui/components/menu-items.mjs +2 -2
  92. package/dist-esm/lib/ui/components/menu-items.mjs.map +1 -1
  93. package/dist-esm/lib/ui/context/actions.mjs +23 -29
  94. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  95. package/dist-esm/lib/ui/hooks/useEditorEvents.mjs +1 -1
  96. package/dist-esm/lib/ui/hooks/useEditorEvents.mjs.map +1 -1
  97. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +4 -4
  98. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +1 -1
  99. package/dist-esm/lib/ui/version.mjs +3 -3
  100. package/dist-esm/lib/ui/version.mjs.map +1 -1
  101. package/dist-esm/lib/utils/text/richText.mjs +5 -6
  102. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  103. package/package.json +10 -10
  104. package/src/index.ts +4 -0
  105. package/src/lib/defaultEmbedDefinitions.ts +2 -25
  106. package/src/lib/defaultExternalContentHandlers.ts +10 -35
  107. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +39 -133
  108. package/src/lib/shapes/bookmark/bookmarks.ts +170 -0
  109. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +28 -2
  110. package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +23 -6
  111. package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +24 -12
  112. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +21 -10
  113. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +23 -11
  114. package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +26 -9
  115. package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +23 -10
  116. package/src/lib/tools/SelectTool/childStates/Resizing.ts +24 -9
  117. package/src/lib/tools/SelectTool/childStates/Rotating.ts +27 -11
  118. package/src/lib/tools/SelectTool/childStates/Translating.ts +28 -12
  119. package/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx +29 -9
  120. package/src/lib/ui/components/DefaultDebugPanel.tsx +1 -1
  121. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx +6 -2
  122. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +1 -1
  123. package/src/lib/ui/components/menu-items.tsx +2 -2
  124. package/src/lib/ui/context/actions.tsx +27 -31
  125. package/src/lib/ui/hooks/useEditorEvents.ts +1 -1
  126. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +4 -4
  127. package/src/lib/ui/version.ts +3 -3
  128. package/src/lib/utils/embeds/embeds.test.ts +16 -34
  129. package/src/lib/utils/text/richText.ts +5 -5
  130. package/src/test/SelectTool.test.ts +251 -0
  131. package/src/test/bookmark-shapes.test.ts +129 -7
@@ -79,7 +79,9 @@ function useEditingLinkBehavior(textEditor) {
79
79
  };
80
80
  textEditor.view.dom.addEventListener("click", handleClick);
81
81
  return () => {
82
- textEditor.view.dom.removeEventListener("click", handleClick);
82
+ if (textEditor.isInitialized) {
83
+ textEditor.view.dom.removeEventListener("click", handleClick);
84
+ }
83
85
  };
84
86
  }, [textEditor, isEditingLink]);
85
87
  useEffect(() => {
@@ -132,7 +134,9 @@ function useIsMousingDownOnTextEditor(textEditor) {
132
134
  });
133
135
  return () => {
134
136
  touchDownEvents.forEach((eventName) => {
135
- textEditor.view.dom.removeEventListener(eventName, handlePointingDown);
137
+ if (textEditor.isInitialized) {
138
+ textEditor.view.dom.removeEventListener(eventName, handlePointingDown);
139
+ }
136
140
  });
137
141
  touchUpEvents.forEach((eventName) => {
138
142
  document.body.removeEventListener(eventName, handlePointingUp);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx"],
4
- "sourcesContent": ["import { getMarkRange, Range, EditorEvents as TextEditorEvents } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Box, debounce, TiptapEditor, track, useEditor, useValue } from '@tldraw/editor'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { rectToBox, TldrawUiContextualToolbar } from '../primitives/TldrawUiContextualToolbar'\nimport { DefaultRichTextToolbarContent } from './DefaultRichTextToolbarContent'\nimport { LinkEditor } from './LinkEditor'\n\n/** @public */\nexport interface TLUiRichTextToolbarProps {\n\tchildren?: React.ReactNode\n}\n\n/**\n * The default rich text toolbar.\n *\n * @public @react\n */\nexport const DefaultRichTextToolbar = track(function DefaultRichTextToolbar({\n\tchildren,\n}: TLUiRichTextToolbarProps) {\n\tconst editor = useEditor()\n\n\tconst textEditor = useValue('textEditor', () => editor.getRichTextEditor(), [editor])\n\n\tif (editor.getInstanceState().isCoarsePointer || !textEditor) return null\n\n\treturn <ContextualToolbarInner textEditor={textEditor}>{children}</ContextualToolbarInner>\n})\n\nfunction ContextualToolbarInner({\n\ttextEditor,\n\tchildren,\n}: {\n\tchildren?: React.ReactNode\n\ttextEditor: TiptapEditor\n}) {\n\tconst { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor)\n\tconst [currentSelection, setCurrentSelection] = useState<Range | null>(null)\n\tconst previousSelectionBounds = useRef<Box | undefined>()\n\tconst isMousingDown = useIsMousingDownOnTextEditor(textEditor)\n\tconst msg = useTranslation()\n\n\tconst getSelectionBounds = useCallback(() => {\n\t\tif (isEditingLink) {\n\t\t\t// If we're editing a link we don't have selection bounds temporarily.\n\t\t\treturn previousSelectionBounds.current\n\t\t}\n\t\t// Get the text selection rects as a box. This will be undefined if there are no selections.\n\t\tconst selection = window.getSelection()\n\n\t\t// If there are no selections, don't return a box\n\t\tif (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed)\n\t\t\treturn\n\n\t\t// Get a common box from all of the ranges' screen rects\n\t\tconst rangeBoxes: Box[] = []\n\t\tfor (let i = 0; i < selection.rangeCount; i++) {\n\t\t\tconst range = selection.getRangeAt(i)\n\t\t\trangeBoxes.push(rectToBox(range.getBoundingClientRect()))\n\t\t}\n\n\t\tconst bounds = Box.Common(rangeBoxes)\n\t\tpreviousSelectionBounds.current = bounds\n\t\treturn bounds\n\t}, [currentSelection, isEditingLink])\n\n\tuseEffect(() => {\n\t\tconst handleSelectionUpdate = ({ editor: textEditor }: TextEditorEvents['selectionUpdate']) =>\n\t\t\tsetCurrentSelection(textEditor.state.selection)\n\t\ttextEditor.on('selectionUpdate', handleSelectionUpdate)\n\t\t// Need to kick off the selection update manually to get the initial selection, esp. if select-all.\n\t\thandleSelectionUpdate({ editor: textEditor } as TextEditorEvents['selectionUpdate'])\n\t\treturn () => {\n\t\t\ttextEditor.off('selectionUpdate', handleSelectionUpdate)\n\t\t}\n\t}, [textEditor])\n\n\treturn (\n\t\t<TldrawUiContextualToolbar\n\t\t\tclassName=\"tlui-rich-text__toolbar\"\n\t\t\tgetSelectionBounds={getSelectionBounds}\n\t\t\tisMousingDown={isMousingDown}\n\t\t\tchangeOnlyWhenYChanges={true}\n\t\t\tlabel={msg('tool.rich-text-toolbar-title')}\n\t\t>\n\t\t\t{children ? (\n\t\t\t\tchildren\n\t\t\t) : isEditingLink ? (\n\t\t\t\t<LinkEditor\n\t\t\t\t\ttextEditor={textEditor}\n\t\t\t\t\tvalue={textEditor.isActive('link') ? textEditor.getAttributes('link').href : ''}\n\t\t\t\t\tonClose={onEditLinkClose}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<DefaultRichTextToolbarContent textEditor={textEditor} onEditLinkStart={onEditLinkStart} />\n\t\t\t)}\n\t\t</TldrawUiContextualToolbar>\n\t)\n}\n\nfunction useEditingLinkBehavior(textEditor?: TiptapEditor) {\n\tconst [isEditingLink, setIsEditingLink] = useState(false)\n\n\t// Set up text editor event listeners.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\tsetIsEditingLink(false)\n\t\t\treturn\n\t\t}\n\n\t\tconst handleClick = () => {\n\t\t\tconst isLinkActive = textEditor.isActive('link')\n\t\t\tsetIsEditingLink(isLinkActive)\n\t\t}\n\n\t\ttextEditor.view.dom.addEventListener('click', handleClick)\n\t\treturn () => {\n\t\t\ttextEditor.view.dom.removeEventListener('click', handleClick)\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\t// If we're editing a link, select the entire link.\n\t// This can happen via a click or via keyboarding over to the link and then\n\t// clicking the toolbar button.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\treturn\n\t\t}\n\n\t\t// N.B. This specifically isn't checking the isEditingLink state but\n\t\t// the current active state of the text editor. This is because there's\n\t\t// a subtelty where when going edit-to-edit, that is text editor-to-text editor\n\t\t// in different shapes, the isEditingLink state doesn't get reset quickly enough.\n\t\tif (textEditor.isActive('link')) {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = getMarkRange(\n\t\t\t\t\ttextEditor.state.doc.resolve(textEditor.state.selection.from),\n\t\t\t\t\ttextEditor.schema.marks.link as MarkType\n\t\t\t\t) as Range\n\t\t\t\t// Select the entire link if we just clicked on it while in edit mode, but not if there's\n\t\t\t\t// a specific selection.\n\t\t\t\tif (textEditor.state.selection.empty) {\n\t\t\t\t\ttextEditor.commands.setTextSelection({ from, to })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Sometimes getMarkRange throws an error when the selection is the entire document.\n\t\t\t\t// This is somewhat mysterious but it's harmless. We just need to ignore it.\n\t\t\t\t// Also, this seems to have recently broken with the React 19 preparation changes.\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\tconst onEditLinkStart = useCallback(() => {\n\t\tsetIsEditingLink(true)\n\t}, [])\n\n\tconst onEditLinkCancel = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t}, [])\n\n\tconst onEditLinkClose = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t\tif (!textEditor) return\n\t\tconst from = textEditor.state.selection.from\n\t\ttextEditor.commands.setTextSelection({ from, to: from })\n\t}, [textEditor])\n\n\treturn { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }\n}\n\nfunction useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {\n\tconst [isMousingDown, setIsMousingDown] = useState(false)\n\n\t// Set up general event listeners for text selection.\n\tuseEffect(() => {\n\t\tif (!textEditor) return\n\n\t\tconst handlePointingStateChange = debounce(({ isPointing }: { isPointing: boolean }) => {\n\t\t\tsetIsMousingDown(isPointing)\n\t\t}, 16)\n\t\tconst handlePointingDown = () => handlePointingStateChange({ isPointing: true })\n\t\tconst handlePointingUp = () => handlePointingStateChange({ isPointing: false })\n\n\t\tconst touchDownEvents = ['touchstart', 'pointerdown', 'mousedown']\n\t\tconst touchUpEvents = ['touchend', 'pointerup', 'mouseup']\n\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\ttextEditor.view.dom.addEventListener(eventName, handlePointingDown)\n\t\t})\n\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\tdocument.body.addEventListener(eventName, handlePointingUp)\n\t\t})\n\t\treturn () => {\n\t\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\t\ttextEditor.view.dom.removeEventListener(eventName, handlePointingDown)\n\t\t\t})\n\t\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\t\tdocument.body.removeEventListener(eventName, handlePointingUp)\n\t\t\t})\n\t\t}\n\t}, [textEditor])\n\n\treturn isMousingDown\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { getMarkRange, Range, EditorEvents as TextEditorEvents } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Box, debounce, TiptapEditor, track, useEditor, useValue } from '@tldraw/editor'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { rectToBox, TldrawUiContextualToolbar } from '../primitives/TldrawUiContextualToolbar'\nimport { DefaultRichTextToolbarContent } from './DefaultRichTextToolbarContent'\nimport { LinkEditor } from './LinkEditor'\n\n/** @public */\nexport interface TLUiRichTextToolbarProps {\n\tchildren?: React.ReactNode\n}\n\n/**\n * The default rich text toolbar.\n *\n * @public @react\n */\nexport const DefaultRichTextToolbar = track(function DefaultRichTextToolbar({\n\tchildren,\n}: TLUiRichTextToolbarProps) {\n\tconst editor = useEditor()\n\n\tconst textEditor = useValue('textEditor', () => editor.getRichTextEditor(), [editor])\n\n\tif (editor.getInstanceState().isCoarsePointer || !textEditor) return null\n\n\treturn <ContextualToolbarInner textEditor={textEditor}>{children}</ContextualToolbarInner>\n})\n\nfunction ContextualToolbarInner({\n\ttextEditor,\n\tchildren,\n}: {\n\tchildren?: React.ReactNode\n\ttextEditor: TiptapEditor\n}) {\n\tconst { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor)\n\tconst [currentSelection, setCurrentSelection] = useState<Range | null>(null)\n\tconst previousSelectionBounds = useRef<Box | undefined>()\n\tconst isMousingDown = useIsMousingDownOnTextEditor(textEditor)\n\tconst msg = useTranslation()\n\n\tconst getSelectionBounds = useCallback(() => {\n\t\tif (isEditingLink) {\n\t\t\t// If we're editing a link we don't have selection bounds temporarily.\n\t\t\treturn previousSelectionBounds.current\n\t\t}\n\t\t// Get the text selection rects as a box. This will be undefined if there are no selections.\n\t\tconst selection = window.getSelection()\n\n\t\t// If there are no selections, don't return a box\n\t\tif (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed)\n\t\t\treturn\n\n\t\t// Get a common box from all of the ranges' screen rects\n\t\tconst rangeBoxes: Box[] = []\n\t\tfor (let i = 0; i < selection.rangeCount; i++) {\n\t\t\tconst range = selection.getRangeAt(i)\n\t\t\trangeBoxes.push(rectToBox(range.getBoundingClientRect()))\n\t\t}\n\n\t\tconst bounds = Box.Common(rangeBoxes)\n\t\tpreviousSelectionBounds.current = bounds\n\t\treturn bounds\n\t}, [currentSelection, isEditingLink])\n\n\tuseEffect(() => {\n\t\tconst handleSelectionUpdate = ({ editor: textEditor }: TextEditorEvents['selectionUpdate']) =>\n\t\t\tsetCurrentSelection(textEditor.state.selection)\n\t\ttextEditor.on('selectionUpdate', handleSelectionUpdate)\n\t\t// Need to kick off the selection update manually to get the initial selection, esp. if select-all.\n\t\thandleSelectionUpdate({ editor: textEditor } as TextEditorEvents['selectionUpdate'])\n\t\treturn () => {\n\t\t\ttextEditor.off('selectionUpdate', handleSelectionUpdate)\n\t\t}\n\t}, [textEditor])\n\n\treturn (\n\t\t<TldrawUiContextualToolbar\n\t\t\tclassName=\"tlui-rich-text__toolbar\"\n\t\t\tgetSelectionBounds={getSelectionBounds}\n\t\t\tisMousingDown={isMousingDown}\n\t\t\tchangeOnlyWhenYChanges={true}\n\t\t\tlabel={msg('tool.rich-text-toolbar-title')}\n\t\t>\n\t\t\t{children ? (\n\t\t\t\tchildren\n\t\t\t) : isEditingLink ? (\n\t\t\t\t<LinkEditor\n\t\t\t\t\ttextEditor={textEditor}\n\t\t\t\t\tvalue={textEditor.isActive('link') ? textEditor.getAttributes('link').href : ''}\n\t\t\t\t\tonClose={onEditLinkClose}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<DefaultRichTextToolbarContent textEditor={textEditor} onEditLinkStart={onEditLinkStart} />\n\t\t\t)}\n\t\t</TldrawUiContextualToolbar>\n\t)\n}\n\nfunction useEditingLinkBehavior(textEditor?: TiptapEditor) {\n\tconst [isEditingLink, setIsEditingLink] = useState(false)\n\n\t// Set up text editor event listeners.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\tsetIsEditingLink(false)\n\t\t\treturn\n\t\t}\n\n\t\tconst handleClick = () => {\n\t\t\tconst isLinkActive = textEditor.isActive('link')\n\t\t\tsetIsEditingLink(isLinkActive)\n\t\t}\n\n\t\ttextEditor.view.dom.addEventListener('click', handleClick)\n\t\treturn () => {\n\t\t\tif (textEditor.isInitialized) {\n\t\t\t\ttextEditor.view.dom.removeEventListener('click', handleClick)\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\t// If we're editing a link, select the entire link.\n\t// This can happen via a click or via keyboarding over to the link and then\n\t// clicking the toolbar button.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\treturn\n\t\t}\n\n\t\t// N.B. This specifically isn't checking the isEditingLink state but\n\t\t// the current active state of the text editor. This is because there's\n\t\t// a subtelty where when going edit-to-edit, that is text editor-to-text editor\n\t\t// in different shapes, the isEditingLink state doesn't get reset quickly enough.\n\t\tif (textEditor.isActive('link')) {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = getMarkRange(\n\t\t\t\t\ttextEditor.state.doc.resolve(textEditor.state.selection.from),\n\t\t\t\t\ttextEditor.schema.marks.link as MarkType\n\t\t\t\t) as Range\n\t\t\t\t// Select the entire link if we just clicked on it while in edit mode, but not if there's\n\t\t\t\t// a specific selection.\n\t\t\t\tif (textEditor.state.selection.empty) {\n\t\t\t\t\ttextEditor.commands.setTextSelection({ from, to })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Sometimes getMarkRange throws an error when the selection is the entire document.\n\t\t\t\t// This is somewhat mysterious but it's harmless. We just need to ignore it.\n\t\t\t\t// Also, this seems to have recently broken with the React 19 preparation changes.\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\tconst onEditLinkStart = useCallback(() => {\n\t\tsetIsEditingLink(true)\n\t}, [])\n\n\tconst onEditLinkCancel = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t}, [])\n\n\tconst onEditLinkClose = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t\tif (!textEditor) return\n\t\tconst from = textEditor.state.selection.from\n\t\ttextEditor.commands.setTextSelection({ from, to: from })\n\t}, [textEditor])\n\n\treturn { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }\n}\n\nfunction useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {\n\tconst [isMousingDown, setIsMousingDown] = useState(false)\n\n\t// Set up general event listeners for text selection.\n\tuseEffect(() => {\n\t\tif (!textEditor) return\n\n\t\tconst handlePointingStateChange = debounce(({ isPointing }: { isPointing: boolean }) => {\n\t\t\tsetIsMousingDown(isPointing)\n\t\t}, 16)\n\t\tconst handlePointingDown = () => handlePointingStateChange({ isPointing: true })\n\t\tconst handlePointingUp = () => handlePointingStateChange({ isPointing: false })\n\n\t\tconst touchDownEvents = ['touchstart', 'pointerdown', 'mousedown']\n\t\tconst touchUpEvents = ['touchend', 'pointerup', 'mouseup']\n\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\ttextEditor.view.dom.addEventListener(eventName, handlePointingDown)\n\t\t})\n\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\tdocument.body.addEventListener(eventName, handlePointingUp)\n\t\t})\n\t\treturn () => {\n\t\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\t\tif (textEditor.isInitialized) {\n\t\t\t\t\ttextEditor.view.dom.removeEventListener(eventName, handlePointingDown)\n\t\t\t\t}\n\t\t\t})\n\t\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\t\tdocument.body.removeEventListener(eventName, handlePointingUp)\n\t\t\t})\n\t\t}\n\t}, [textEditor])\n\n\treturn isMousingDown\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["DefaultRichTextToolbar", "textEditor"]
7
7
  }
@@ -20,7 +20,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
20
20
  if (!link.startsWith("http://") && !link.startsWith("https://")) {
21
21
  link = `https://${link}`;
22
22
  }
23
- textEditor.commands.setLink({ href: link });
23
+ textEditor.chain().setLink({ href: link }).run();
24
24
  if (editor.getInstanceState().isCoarsePointer) {
25
25
  textEditor.commands.blur();
26
26
  } else {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/LinkEditor.tsx"],
4
- "sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.commands.setLink({ href: link })\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\taria-label=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
5
- "mappings": "AAmEE,mBACC,KADD;AAnEF,SAAS,gBAA8B,iBAAiB;AACxD,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,aAAa,YAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;AAG1C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,YAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,iCACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA,QACZ,cAAW;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,8BAAC,sBAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,8BAAC,sBAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
4
+ "sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.chain().setLink({ href: link }).run()\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\taria-label=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
5
+ "mappings": "AAmEE,mBACC,KADD;AAnEF,SAAS,gBAA8B,iBAAiB;AACxD,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,aAAa,YAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC,EAAE,IAAI;AAG/C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,YAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,iCACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA,QACZ,cAAW;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,8BAAC,sBAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,8BAAC,sBAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
6
6
  "names": ["value"]
7
7
  }
@@ -348,11 +348,11 @@ function MoveToPageMenu() {
348
348
  const toPage = editor.getPage(page.id);
349
349
  if (toPage) {
350
350
  addToast({
351
- title: "Changed Page",
351
+ title: "Changed page",
352
352
  description: `Moved to ${toPage.name}.`,
353
353
  actions: [
354
354
  {
355
- label: "Go Back",
355
+ label: "Go back",
356
356
  type: "primary",
357
357
  onClick: () => {
358
358
  editor.markHistoryStoppingPoint("change-page");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/ui/components/menu-items.tsx"],
4
- "sourcesContent": ["import {\n\tTLBookmarkShape,\n\tTLEmbedShape,\n\tTLFrameShape,\n\tTLImageShape,\n\tTLPageId,\n\tuseEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { supportsDownloadingOriginal } from '../context/actions'\nimport { useUiEvents } from '../context/events'\nimport { useToasts } from '../context/toasts'\nimport {\n\tshowMenuPaste,\n\tuseAllowGroup,\n\tuseAllowUngroup,\n\tuseAnySelectedShapesCount,\n\tuseHasLinkShapeSelected,\n\tuseOnlyFlippableShape,\n\tuseShowAutoSizeToggle,\n\tuseThreeStackableItems,\n\tuseUnlockedSelectedShapesCount,\n} from '../hooks/menu-hooks'\nimport { useGetEmbedDefinition } from '../hooks/useGetEmbedDefinition'\nimport { useReadonly } from '../hooks/useReadonly'\nimport { TldrawUiMenuActionCheckboxItem } from './primitives/menus/TldrawUiMenuActionCheckboxItem'\nimport { TldrawUiMenuActionItem } from './primitives/menus/TldrawUiMenuActionItem'\nimport { TldrawUiMenuGroup } from './primitives/menus/TldrawUiMenuGroup'\nimport { TldrawUiMenuItem } from './primitives/menus/TldrawUiMenuItem'\nimport { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'\n\n/* -------------------- Selection ------------------- */\n\n/** @public @react */\nexport function ToggleAutoSizeMenuItem() {\n\tconst shouldDisplay = useShowAutoSizeToggle()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"toggle-auto-size\" />\n}\n\n/** @public @react */\nexport function EditLinkMenuItem() {\n\tconst shouldDisplay = useHasLinkShapeSelected()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"edit-link\" />\n}\n\n/** @public @react */\nexport function DuplicateMenuItem() {\n\tconst shouldDisplay = useUnlockedSelectedShapesCount(1)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"duplicate\" />\n}\n\n/** @public @react */\nexport function FlattenMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'should display flatten option',\n\t\t() => {\n\t\t\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\t\t\tif (selectedShapeIds.length === 0) return false\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (onlySelectedShape && editor.isShapeOfType<TLImageShape>(onlySelectedShape, 'image')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"flatten-to-image\" />\n}\n\n/** @public @react */\nexport function DownloadOriginalMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'should display download original option',\n\t\t() => {\n\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\tif (selectedShapes.length === 0) return false\n\t\t\treturn selectedShapes.some((shape) => supportsDownloadingOriginal(shape, editor))\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"download-original\" />\n}\n\n/** @public @react */\nexport function GroupMenuItem() {\n\tconst shouldDisplay = useAllowGroup()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"group\" />\n}\n\n/** @public @react */\nexport function UngroupMenuItem() {\n\tconst shouldDisplay = useAllowUngroup()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"ungroup\" />\n}\n\n/** @public @react */\nexport function RemoveFrameMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'allow unframe',\n\t\t() => {\n\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\tif (selectedShapes.length === 0) return false\n\t\t\treturn selectedShapes.every((shape) => editor.isShapeOfType<TLFrameShape>(shape, 'frame'))\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"remove-frame\" />\n}\n\n/** @public @react */\nexport function FitFrameToContentMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'allow fit frame to content',\n\t\t() => {\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (!onlySelectedShape) return false\n\t\t\treturn (\n\t\t\t\teditor.isShapeOfType<TLFrameShape>(onlySelectedShape, 'frame') &&\n\t\t\t\teditor.getSortedChildIdsForParent(onlySelectedShape).length > 0\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"fit-frame-to-content\" />\n}\n\n/** @public @react */\nexport function ToggleLockMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue('selected shapes', () => editor.getSelectedShapes().length > 0, [\n\t\teditor,\n\t])\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"toggle-lock\" />\n}\n\n/** @public @react */\nexport function ToggleTransparentBgMenuItem() {\n\tconst editor = useEditor()\n\tconst isTransparentBg = useValue(\n\t\t'isTransparentBg',\n\t\t() => !editor.getInstanceState().exportBackground,\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-transparent\"\n\t\t\tchecked={isTransparentBg}\n\t\t\ttoggle\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function UnlockAllMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue('any shapes', () => editor.getCurrentPageShapeIds().size > 0, [\n\t\teditor,\n\t])\n\n\treturn <TldrawUiMenuActionItem actionId=\"unlock-all\" disabled={!shouldDisplay} />\n}\n\n/* ---------------------- Zoom ---------------------- */\n\n/** @public @react */\nexport function ZoomTo100MenuItem() {\n\tconst editor = useEditor()\n\tconst isZoomedTo100 = useValue('zoomed to 100', () => editor.getZoomLevel() === 1, [editor])\n\n\treturn <TldrawUiMenuActionItem actionId=\"zoom-to-100\" noClose disabled={isZoomedTo100} />\n}\n\n/** @public @react */\nexport function ZoomToFitMenuItem() {\n\tconst editor = useEditor()\n\tconst hasShapes = useValue('has shapes', () => editor.getCurrentPageShapeIds().size > 0, [editor])\n\n\treturn (\n\t\t<TldrawUiMenuActionItem\n\t\t\tactionId=\"zoom-to-fit\"\n\t\t\tdisabled={!hasShapes}\n\t\t\tdata-testid=\"minimap.zoom-menu.zoom-to-fit\"\n\t\t\tnoClose\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ZoomToSelectionMenuItem() {\n\tconst editor = useEditor()\n\tconst hasSelected = useValue('has shapes', () => editor.getSelectedShapeIds().length > 0, [\n\t\teditor,\n\t])\n\n\treturn (\n\t\t<TldrawUiMenuActionItem\n\t\t\tactionId=\"zoom-to-selection\"\n\t\t\tdisabled={!hasSelected}\n\t\t\tdata-testid=\"minimap.zoom-menu.zoom-to-selection\"\n\t\t\tnoClose\n\t\t/>\n\t)\n}\n\n/* -------------------- Clipboard ------------------- */\n\n/** @public @react */\nexport function ClipboardMenuGroup() {\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"clipboard\">\n\t\t\t<CutMenuItem />\n\t\t\t<CopyMenuItem />\n\t\t\t<PasteMenuItem />\n\t\t\t<DuplicateMenuItem />\n\t\t\t<DeleteMenuItem />\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\n/** @public @react */\nexport function CopyAsMenuGroup() {\n\tconst editor = useEditor()\n\tconst atLeastOneShapeOnPage = useValue(\n\t\t'atLeastOneShapeOnPage',\n\t\t() => editor.getCurrentPageShapeIds().size > 0,\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu\n\t\t\tid=\"copy-as\"\n\t\t\tlabel=\"context-menu.copy-as\"\n\t\t\tsize=\"small\"\n\t\t\tdisabled={!atLeastOneShapeOnPage}\n\t\t>\n\t\t\t<TldrawUiMenuGroup id=\"copy-as-group\">\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"copy-as-svg\" />\n\t\t\t\t{Boolean(window.navigator.clipboard?.write) && (\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"copy-as-png\" />\n\t\t\t\t)}\n\t\t\t</TldrawUiMenuGroup>\n\t\t\t<TldrawUiMenuGroup id=\"copy-as-bg\">\n\t\t\t\t<ToggleTransparentBgMenuItem />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function CutMenuItem() {\n\tconst shouldDisplay = useUnlockedSelectedShapesCount(1)\n\n\treturn <TldrawUiMenuActionItem actionId=\"cut\" disabled={!shouldDisplay} />\n}\n\n/** @public @react */\nexport function CopyMenuItem() {\n\tconst shouldDisplay = useAnySelectedShapesCount(1)\n\n\treturn <TldrawUiMenuActionItem actionId=\"copy\" disabled={!shouldDisplay} />\n}\n\n/** @public @react */\nexport function PasteMenuItem() {\n\tconst shouldDisplay = showMenuPaste\n\n\treturn <TldrawUiMenuActionItem actionId=\"paste\" disabled={!shouldDisplay} />\n}\n\n/* ------------------- Conversions ------------------ */\n\n/** @public @react */\nexport function ConversionsMenuGroup() {\n\tconst editor = useEditor()\n\tconst atLeastOneShapeOnPage = useValue(\n\t\t'atLeastOneShapeOnPage',\n\t\t() => editor.getCurrentPageShapeIds().size > 0,\n\t\t[editor]\n\t)\n\n\tif (!atLeastOneShapeOnPage) return null\n\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"conversions\">\n\t\t\t<CopyAsMenuGroup />\n\t\t\t<TldrawUiMenuSubmenu id=\"export-as\" label=\"context-menu.export-as\" size=\"small\">\n\t\t\t\t<TldrawUiMenuGroup id=\"export-as-group\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"export-as-svg\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"export-as-png\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t<TldrawUiMenuGroup id=\"export-as-bg\">\n\t\t\t\t\t<ToggleTransparentBgMenuItem />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t</TldrawUiMenuSubmenu>\n\t\t\t<DownloadOriginalMenuItem />\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\n/* ------------------ Set Selection ----------------- */\n/** @public @react */\nexport function SelectAllMenuItem() {\n\tconst editor = useEditor()\n\tconst atLeastOneShapeOnPage = useValue(\n\t\t'atLeastOneShapeOnPage',\n\t\t() => editor.getCurrentPageShapeIds().size > 0,\n\t\t[editor]\n\t)\n\n\treturn <TldrawUiMenuActionItem actionId=\"select-all\" disabled={!atLeastOneShapeOnPage} />\n}\n\n/* ------------------ Delete Group ------------------ */\n\n/** @public @react */\nexport function DeleteMenuItem() {\n\tconst oneSelected = useUnlockedSelectedShapesCount(1)\n\n\treturn <TldrawUiMenuActionItem actionId=\"delete\" disabled={!oneSelected} />\n}\n\n/* --------------------- Modify --------------------- */\n\n/** @public @react */\nexport function EditMenuSubmenu() {\n\tconst isReadonlyMode = useReadonly()\n\tif (!useAnySelectedShapesCount(1)) return null\n\tif (isReadonlyMode) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"edit\" label=\"context-menu.edit\" size=\"small\">\n\t\t\t<GroupMenuItem />\n\t\t\t<UngroupMenuItem />\n\t\t\t<FlattenMenuItem />\n\t\t\t<EditLinkMenuItem />\n\t\t\t<FitFrameToContentMenuItem />\n\t\t\t<RemoveFrameMenuItem />\n\t\t\t<ConvertToEmbedMenuItem />\n\t\t\t<ConvertToBookmarkMenuItem />\n\t\t\t<ToggleAutoSizeMenuItem />\n\t\t\t<ToggleLockMenuItem />\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function ArrangeMenuSubmenu() {\n\tconst twoSelected = useUnlockedSelectedShapesCount(2)\n\tconst onlyFlippableShapeSelected = useOnlyFlippableShape()\n\tconst isReadonlyMode = useReadonly()\n\n\tif (isReadonlyMode) return null\n\tif (!(twoSelected || onlyFlippableShapeSelected)) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"arrange\" label=\"context-menu.arrange\" size=\"small\">\n\t\t\t{twoSelected && (\n\t\t\t\t<TldrawUiMenuGroup id=\"align\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-left\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-center-horizontal\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-right\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-top\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-center-vertical\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-bottom\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t)}\n\t\t\t<DistributeMenuGroup />\n\t\t\t{twoSelected && (\n\t\t\t\t<TldrawUiMenuGroup id=\"stretch\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"stretch-horizontal\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"stretch-vertical\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t)}\n\t\t\t{(twoSelected || onlyFlippableShapeSelected) && (\n\t\t\t\t<TldrawUiMenuGroup id=\"flip\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"flip-horizontal\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"flip-vertical\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t)}\n\t\t\t<OrderMenuGroup />\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\nfunction DistributeMenuGroup() {\n\tconst threeSelected = useUnlockedSelectedShapesCount(3)\n\tif (!threeSelected) return null\n\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"distribute\">\n\t\t\t<TldrawUiMenuActionItem actionId=\"distribute-horizontal\" />\n\t\t\t<TldrawUiMenuActionItem actionId=\"distribute-vertical\" />\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\nfunction OrderMenuGroup() {\n\tconst twoSelected = useUnlockedSelectedShapesCount(2)\n\tconst threeStackableItems = useThreeStackableItems()\n\tif (!twoSelected) return null\n\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"order\">\n\t\t\t<TldrawUiMenuActionItem actionId=\"pack\" />\n\t\t\t{threeStackableItems && <TldrawUiMenuActionItem actionId=\"stack-horizontal\" />}\n\t\t\t{threeStackableItems && <TldrawUiMenuActionItem actionId=\"stack-vertical\" />}\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\n/** @public @react */\nexport function ReorderMenuSubmenu() {\n\tconst isReadonlyMode = useReadonly()\n\tconst oneSelected = useUnlockedSelectedShapesCount(1)\n\tif (isReadonlyMode) return null\n\tif (!oneSelected) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"reorder\" label=\"context-menu.reorder\" size=\"small\">\n\t\t\t<TldrawUiMenuGroup id=\"reorder\">\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"bring-to-front\" />\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"bring-forward\" />\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"send-backward\" />\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"send-to-back\" />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function MoveToPageMenu() {\n\tconst editor = useEditor()\n\tconst pages = useValue('pages', () => editor.getPages(), [editor])\n\tconst currentPageId = useValue('current page id', () => editor.getCurrentPageId(), [editor])\n\tconst { addToast } = useToasts()\n\tconst trackEvent = useUiEvents()\n\tconst isReadonlyMode = useReadonly()\n\tconst oneSelected = useUnlockedSelectedShapesCount(1)\n\n\tif (!oneSelected) return null\n\tif (isReadonlyMode) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"move-to-page\" label=\"context-menu.move-to-page\" size=\"small\">\n\t\t\t<TldrawUiMenuGroup id=\"pages\">\n\t\t\t\t{pages.map((page) => (\n\t\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\t\tid={page.id}\n\t\t\t\t\t\tkey={page.id}\n\t\t\t\t\t\tdisabled={currentPageId === page.id}\n\t\t\t\t\t\tlabel={page.name.length > 30 ? `${page.name.slice(0, 30)}\u2026` : page.name}\n\t\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\t\teditor.markHistoryStoppingPoint('move_shapes_to_page')\n\t\t\t\t\t\t\teditor.moveShapesToPage(editor.getSelectedShapeIds(), page.id as TLPageId)\n\n\t\t\t\t\t\t\tconst toPage = editor.getPage(page.id)\n\n\t\t\t\t\t\t\tif (toPage) {\n\t\t\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\t\t\ttitle: 'Changed Page',\n\t\t\t\t\t\t\t\t\tdescription: `Moved to ${toPage.name}.`,\n\t\t\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tlabel: 'Go Back',\n\t\t\t\t\t\t\t\t\t\t\ttype: 'primary',\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\t\t\teditor.markHistoryStoppingPoint('change-page')\n\t\t\t\t\t\t\t\t\t\t\t\teditor.setCurrentPage(currentPageId)\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttrackEvent('move-to-page', { source: 'context-menu' })\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</TldrawUiMenuGroup>\n\t\t\t<TldrawUiMenuGroup id=\"new-page\">\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"move-to-new-page\" />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function ConvertToBookmarkMenuItem() {\n\tconst editor = useEditor()\n\n\tconst oneEmbedSelected = useValue(\n\t\t'oneEmbedSelected',\n\t\t() => {\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (!onlySelectedShape) return false\n\t\t\treturn !!(\n\t\t\t\teditor.isShapeOfType<TLEmbedShape>(onlySelectedShape, 'embed') &&\n\t\t\t\tonlySelectedShape.props.url &&\n\t\t\t\t!editor.isShapeOrAncestorLocked(onlySelectedShape)\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\n\tif (!oneEmbedSelected) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"convert-to-bookmark\" />\n}\n\n/** @public @react */\nexport function ConvertToEmbedMenuItem() {\n\tconst editor = useEditor()\n\tconst getEmbedDefinition = useGetEmbedDefinition()\n\n\tconst oneEmbeddableBookmarkSelected = useValue(\n\t\t'oneEmbeddableBookmarkSelected',\n\t\t() => {\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (!onlySelectedShape) return false\n\t\t\treturn !!(\n\t\t\t\teditor.isShapeOfType<TLBookmarkShape>(onlySelectedShape, 'bookmark') &&\n\t\t\t\tonlySelectedShape.props.url &&\n\t\t\t\tgetEmbedDefinition(onlySelectedShape.props.url) &&\n\t\t\t\t!editor.isShapeOrAncestorLocked(onlySelectedShape)\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\n\tif (!oneEmbeddableBookmarkSelected) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"convert-to-embed\" />\n}\n\n/* ------------------- Preferences ------------------ */\n\n/** @public @react */\nexport function ToggleSnapModeItem() {\n\tconst editor = useEditor()\n\tconst isSnapMode = useValue('isSnapMode', () => editor.user.getIsSnapMode(), [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-snap-mode\" checked={isSnapMode} />\n}\n\n/** @public @react */\nexport function ToggleToolLockItem() {\n\tconst editor = useEditor()\n\tconst isToolLock = useValue('isToolLock', () => editor.getInstanceState().isToolLocked, [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-tool-lock\" checked={isToolLock} />\n}\n\n/** @public @react */\nexport function ToggleGridItem() {\n\tconst editor = useEditor()\n\tconst isGridMode = useValue('isGridMode', () => editor.getInstanceState().isGridMode, [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-grid\" checked={isGridMode} />\n}\n\n/** @public @react */\nexport function ToggleWrapModeItem() {\n\tconst editor = useEditor()\n\tconst isWrapMode = useValue('isWrapMode', () => editor.user.getIsWrapMode(), [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-wrap-mode\" checked={isWrapMode} />\n}\n\n/** @public @react */\nexport function ToggleDarkModeItem() {\n\tconst editor = useEditor()\n\tconst isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-dark-mode\" checked={isDarkMode} />\n}\n\n/** @public @react */\nexport function ToggleFocusModeItem() {\n\tconst editor = useEditor()\n\tconst isFocusMode = useValue('isFocusMode', () => editor.getInstanceState().isFocusMode, [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-focus-mode\" checked={isFocusMode} />\n}\n\n/** @public @react */\nexport function ToggleEdgeScrollingItem() {\n\tconst editor = useEditor()\n\tconst edgeScrollSpeed = useValue('edgeScrollSpeed', () => editor.user.getEdgeScrollSpeed(), [\n\t\teditor,\n\t])\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-edge-scrolling\"\n\t\t\tchecked={edgeScrollSpeed === 1}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ToggleReduceMotionItem() {\n\tconst editor = useEditor()\n\tconst animationSpeed = useValue('animationSpeed', () => editor.user.getAnimationSpeed(), [editor])\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-reduce-motion\"\n\t\t\tchecked={animationSpeed === 0}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ToggleKeyboardShortcutsItem() {\n\tconst editor = useEditor()\n\tconst keyboardShortcuts = useValue(\n\t\t'keyboardShortcuts',\n\t\t() => editor.user.getAreKeyboardShortcutsEnabled(),\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-keyboard-shortcuts\"\n\t\t\tchecked={keyboardShortcuts}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ToggleEnhancedA11yModeItem() {\n\tconst editor = useEditor()\n\tconst enhancedA11yMode = useValue('enhancedA11yMode', () => editor.user.getEnhancedA11yMode(), [\n\t\teditor,\n\t])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"enhanced-a11y-mode\" checked={enhancedA11yMode} />\n}\n\n/** @public @react */\nexport function ToggleDebugModeItem() {\n\tconst editor = useEditor()\n\tconst isDebugMode = useValue('isDebugMode', () => editor.getInstanceState().isDebugMode, [editor])\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-debug-mode\" checked={isDebugMode} />\n}\n\n/** @public @react */\nexport function ToggleDynamicSizeModeItem() {\n\tconst editor = useEditor()\n\tconst isDynamicResizeMode = useValue(\n\t\t'dynamic resize',\n\t\t() => editor.user.getIsDynamicResizeMode(),\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-dynamic-size-mode\"\n\t\t\tchecked={isDynamicResizeMode}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function TogglePasteAtCursorItem() {\n\tconst editor = useEditor()\n\tconst pasteAtCursor = useValue('paste at cursor', () => editor.user.getIsPasteAtCursorMode(), [\n\t\teditor,\n\t])\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem actionId=\"toggle-paste-at-cursor\" checked={pasteAtCursor} />\n\t)\n}\n\n/* ---------------------- Print --------------------- */\n\n/** @public @react */\nexport function PrintItem() {\n\tconst editor = useEditor()\n\tconst emptyPage = useValue('emptyPage', () => editor.getCurrentPageShapeIds().size === 0, [\n\t\teditor,\n\t])\n\n\treturn <TldrawUiMenuActionItem actionId=\"print\" disabled={emptyPage} />\n}\n\n/* ---------------------- Multiplayer --------------------- */\n\n/** @public @react */\nexport function CursorChatItem() {\n\tconst editor = useEditor()\n\tconst shouldShow = useValue(\n\t\t'show cursor chat',\n\t\t() => editor.getCurrentToolId() === 'select' && !editor.getInstanceState().isCoarsePointer,\n\t\t[editor]\n\t)\n\n\tif (!shouldShow) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"open-cursor-chat\" />\n}\n"],
4
+ "sourcesContent": ["import {\n\tTLBookmarkShape,\n\tTLEmbedShape,\n\tTLFrameShape,\n\tTLImageShape,\n\tTLPageId,\n\tuseEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { supportsDownloadingOriginal } from '../context/actions'\nimport { useUiEvents } from '../context/events'\nimport { useToasts } from '../context/toasts'\nimport {\n\tshowMenuPaste,\n\tuseAllowGroup,\n\tuseAllowUngroup,\n\tuseAnySelectedShapesCount,\n\tuseHasLinkShapeSelected,\n\tuseOnlyFlippableShape,\n\tuseShowAutoSizeToggle,\n\tuseThreeStackableItems,\n\tuseUnlockedSelectedShapesCount,\n} from '../hooks/menu-hooks'\nimport { useGetEmbedDefinition } from '../hooks/useGetEmbedDefinition'\nimport { useReadonly } from '../hooks/useReadonly'\nimport { TldrawUiMenuActionCheckboxItem } from './primitives/menus/TldrawUiMenuActionCheckboxItem'\nimport { TldrawUiMenuActionItem } from './primitives/menus/TldrawUiMenuActionItem'\nimport { TldrawUiMenuGroup } from './primitives/menus/TldrawUiMenuGroup'\nimport { TldrawUiMenuItem } from './primitives/menus/TldrawUiMenuItem'\nimport { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'\n\n/* -------------------- Selection ------------------- */\n\n/** @public @react */\nexport function ToggleAutoSizeMenuItem() {\n\tconst shouldDisplay = useShowAutoSizeToggle()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"toggle-auto-size\" />\n}\n\n/** @public @react */\nexport function EditLinkMenuItem() {\n\tconst shouldDisplay = useHasLinkShapeSelected()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"edit-link\" />\n}\n\n/** @public @react */\nexport function DuplicateMenuItem() {\n\tconst shouldDisplay = useUnlockedSelectedShapesCount(1)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"duplicate\" />\n}\n\n/** @public @react */\nexport function FlattenMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'should display flatten option',\n\t\t() => {\n\t\t\tconst selectedShapeIds = editor.getSelectedShapeIds()\n\t\t\tif (selectedShapeIds.length === 0) return false\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (onlySelectedShape && editor.isShapeOfType<TLImageShape>(onlySelectedShape, 'image')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"flatten-to-image\" />\n}\n\n/** @public @react */\nexport function DownloadOriginalMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'should display download original option',\n\t\t() => {\n\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\tif (selectedShapes.length === 0) return false\n\t\t\treturn selectedShapes.some((shape) => supportsDownloadingOriginal(shape, editor))\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"download-original\" />\n}\n\n/** @public @react */\nexport function GroupMenuItem() {\n\tconst shouldDisplay = useAllowGroup()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"group\" />\n}\n\n/** @public @react */\nexport function UngroupMenuItem() {\n\tconst shouldDisplay = useAllowUngroup()\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"ungroup\" />\n}\n\n/** @public @react */\nexport function RemoveFrameMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'allow unframe',\n\t\t() => {\n\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\tif (selectedShapes.length === 0) return false\n\t\t\treturn selectedShapes.every((shape) => editor.isShapeOfType<TLFrameShape>(shape, 'frame'))\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"remove-frame\" />\n}\n\n/** @public @react */\nexport function FitFrameToContentMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue(\n\t\t'allow fit frame to content',\n\t\t() => {\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (!onlySelectedShape) return false\n\t\t\treturn (\n\t\t\t\teditor.isShapeOfType<TLFrameShape>(onlySelectedShape, 'frame') &&\n\t\t\t\teditor.getSortedChildIdsForParent(onlySelectedShape).length > 0\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"fit-frame-to-content\" />\n}\n\n/** @public @react */\nexport function ToggleLockMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue('selected shapes', () => editor.getSelectedShapes().length > 0, [\n\t\teditor,\n\t])\n\tif (!shouldDisplay) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"toggle-lock\" />\n}\n\n/** @public @react */\nexport function ToggleTransparentBgMenuItem() {\n\tconst editor = useEditor()\n\tconst isTransparentBg = useValue(\n\t\t'isTransparentBg',\n\t\t() => !editor.getInstanceState().exportBackground,\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-transparent\"\n\t\t\tchecked={isTransparentBg}\n\t\t\ttoggle\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function UnlockAllMenuItem() {\n\tconst editor = useEditor()\n\tconst shouldDisplay = useValue('any shapes', () => editor.getCurrentPageShapeIds().size > 0, [\n\t\teditor,\n\t])\n\n\treturn <TldrawUiMenuActionItem actionId=\"unlock-all\" disabled={!shouldDisplay} />\n}\n\n/* ---------------------- Zoom ---------------------- */\n\n/** @public @react */\nexport function ZoomTo100MenuItem() {\n\tconst editor = useEditor()\n\tconst isZoomedTo100 = useValue('zoomed to 100', () => editor.getZoomLevel() === 1, [editor])\n\n\treturn <TldrawUiMenuActionItem actionId=\"zoom-to-100\" noClose disabled={isZoomedTo100} />\n}\n\n/** @public @react */\nexport function ZoomToFitMenuItem() {\n\tconst editor = useEditor()\n\tconst hasShapes = useValue('has shapes', () => editor.getCurrentPageShapeIds().size > 0, [editor])\n\n\treturn (\n\t\t<TldrawUiMenuActionItem\n\t\t\tactionId=\"zoom-to-fit\"\n\t\t\tdisabled={!hasShapes}\n\t\t\tdata-testid=\"minimap.zoom-menu.zoom-to-fit\"\n\t\t\tnoClose\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ZoomToSelectionMenuItem() {\n\tconst editor = useEditor()\n\tconst hasSelected = useValue('has shapes', () => editor.getSelectedShapeIds().length > 0, [\n\t\teditor,\n\t])\n\n\treturn (\n\t\t<TldrawUiMenuActionItem\n\t\t\tactionId=\"zoom-to-selection\"\n\t\t\tdisabled={!hasSelected}\n\t\t\tdata-testid=\"minimap.zoom-menu.zoom-to-selection\"\n\t\t\tnoClose\n\t\t/>\n\t)\n}\n\n/* -------------------- Clipboard ------------------- */\n\n/** @public @react */\nexport function ClipboardMenuGroup() {\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"clipboard\">\n\t\t\t<CutMenuItem />\n\t\t\t<CopyMenuItem />\n\t\t\t<PasteMenuItem />\n\t\t\t<DuplicateMenuItem />\n\t\t\t<DeleteMenuItem />\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\n/** @public @react */\nexport function CopyAsMenuGroup() {\n\tconst editor = useEditor()\n\tconst atLeastOneShapeOnPage = useValue(\n\t\t'atLeastOneShapeOnPage',\n\t\t() => editor.getCurrentPageShapeIds().size > 0,\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu\n\t\t\tid=\"copy-as\"\n\t\t\tlabel=\"context-menu.copy-as\"\n\t\t\tsize=\"small\"\n\t\t\tdisabled={!atLeastOneShapeOnPage}\n\t\t>\n\t\t\t<TldrawUiMenuGroup id=\"copy-as-group\">\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"copy-as-svg\" />\n\t\t\t\t{Boolean(window.navigator.clipboard?.write) && (\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"copy-as-png\" />\n\t\t\t\t)}\n\t\t\t</TldrawUiMenuGroup>\n\t\t\t<TldrawUiMenuGroup id=\"copy-as-bg\">\n\t\t\t\t<ToggleTransparentBgMenuItem />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function CutMenuItem() {\n\tconst shouldDisplay = useUnlockedSelectedShapesCount(1)\n\n\treturn <TldrawUiMenuActionItem actionId=\"cut\" disabled={!shouldDisplay} />\n}\n\n/** @public @react */\nexport function CopyMenuItem() {\n\tconst shouldDisplay = useAnySelectedShapesCount(1)\n\n\treturn <TldrawUiMenuActionItem actionId=\"copy\" disabled={!shouldDisplay} />\n}\n\n/** @public @react */\nexport function PasteMenuItem() {\n\tconst shouldDisplay = showMenuPaste\n\n\treturn <TldrawUiMenuActionItem actionId=\"paste\" disabled={!shouldDisplay} />\n}\n\n/* ------------------- Conversions ------------------ */\n\n/** @public @react */\nexport function ConversionsMenuGroup() {\n\tconst editor = useEditor()\n\tconst atLeastOneShapeOnPage = useValue(\n\t\t'atLeastOneShapeOnPage',\n\t\t() => editor.getCurrentPageShapeIds().size > 0,\n\t\t[editor]\n\t)\n\n\tif (!atLeastOneShapeOnPage) return null\n\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"conversions\">\n\t\t\t<CopyAsMenuGroup />\n\t\t\t<TldrawUiMenuSubmenu id=\"export-as\" label=\"context-menu.export-as\" size=\"small\">\n\t\t\t\t<TldrawUiMenuGroup id=\"export-as-group\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"export-as-svg\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"export-as-png\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t<TldrawUiMenuGroup id=\"export-as-bg\">\n\t\t\t\t\t<ToggleTransparentBgMenuItem />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t</TldrawUiMenuSubmenu>\n\t\t\t<DownloadOriginalMenuItem />\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\n/* ------------------ Set Selection ----------------- */\n/** @public @react */\nexport function SelectAllMenuItem() {\n\tconst editor = useEditor()\n\tconst atLeastOneShapeOnPage = useValue(\n\t\t'atLeastOneShapeOnPage',\n\t\t() => editor.getCurrentPageShapeIds().size > 0,\n\t\t[editor]\n\t)\n\n\treturn <TldrawUiMenuActionItem actionId=\"select-all\" disabled={!atLeastOneShapeOnPage} />\n}\n\n/* ------------------ Delete Group ------------------ */\n\n/** @public @react */\nexport function DeleteMenuItem() {\n\tconst oneSelected = useUnlockedSelectedShapesCount(1)\n\n\treturn <TldrawUiMenuActionItem actionId=\"delete\" disabled={!oneSelected} />\n}\n\n/* --------------------- Modify --------------------- */\n\n/** @public @react */\nexport function EditMenuSubmenu() {\n\tconst isReadonlyMode = useReadonly()\n\tif (!useAnySelectedShapesCount(1)) return null\n\tif (isReadonlyMode) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"edit\" label=\"context-menu.edit\" size=\"small\">\n\t\t\t<GroupMenuItem />\n\t\t\t<UngroupMenuItem />\n\t\t\t<FlattenMenuItem />\n\t\t\t<EditLinkMenuItem />\n\t\t\t<FitFrameToContentMenuItem />\n\t\t\t<RemoveFrameMenuItem />\n\t\t\t<ConvertToEmbedMenuItem />\n\t\t\t<ConvertToBookmarkMenuItem />\n\t\t\t<ToggleAutoSizeMenuItem />\n\t\t\t<ToggleLockMenuItem />\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function ArrangeMenuSubmenu() {\n\tconst twoSelected = useUnlockedSelectedShapesCount(2)\n\tconst onlyFlippableShapeSelected = useOnlyFlippableShape()\n\tconst isReadonlyMode = useReadonly()\n\n\tif (isReadonlyMode) return null\n\tif (!(twoSelected || onlyFlippableShapeSelected)) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"arrange\" label=\"context-menu.arrange\" size=\"small\">\n\t\t\t{twoSelected && (\n\t\t\t\t<TldrawUiMenuGroup id=\"align\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-left\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-center-horizontal\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-right\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-top\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-center-vertical\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"align-bottom\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t)}\n\t\t\t<DistributeMenuGroup />\n\t\t\t{twoSelected && (\n\t\t\t\t<TldrawUiMenuGroup id=\"stretch\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"stretch-horizontal\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"stretch-vertical\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t)}\n\t\t\t{(twoSelected || onlyFlippableShapeSelected) && (\n\t\t\t\t<TldrawUiMenuGroup id=\"flip\">\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"flip-horizontal\" />\n\t\t\t\t\t<TldrawUiMenuActionItem actionId=\"flip-vertical\" />\n\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t)}\n\t\t\t<OrderMenuGroup />\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\nfunction DistributeMenuGroup() {\n\tconst threeSelected = useUnlockedSelectedShapesCount(3)\n\tif (!threeSelected) return null\n\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"distribute\">\n\t\t\t<TldrawUiMenuActionItem actionId=\"distribute-horizontal\" />\n\t\t\t<TldrawUiMenuActionItem actionId=\"distribute-vertical\" />\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\nfunction OrderMenuGroup() {\n\tconst twoSelected = useUnlockedSelectedShapesCount(2)\n\tconst threeStackableItems = useThreeStackableItems()\n\tif (!twoSelected) return null\n\n\treturn (\n\t\t<TldrawUiMenuGroup id=\"order\">\n\t\t\t<TldrawUiMenuActionItem actionId=\"pack\" />\n\t\t\t{threeStackableItems && <TldrawUiMenuActionItem actionId=\"stack-horizontal\" />}\n\t\t\t{threeStackableItems && <TldrawUiMenuActionItem actionId=\"stack-vertical\" />}\n\t\t</TldrawUiMenuGroup>\n\t)\n}\n\n/** @public @react */\nexport function ReorderMenuSubmenu() {\n\tconst isReadonlyMode = useReadonly()\n\tconst oneSelected = useUnlockedSelectedShapesCount(1)\n\tif (isReadonlyMode) return null\n\tif (!oneSelected) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"reorder\" label=\"context-menu.reorder\" size=\"small\">\n\t\t\t<TldrawUiMenuGroup id=\"reorder\">\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"bring-to-front\" />\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"bring-forward\" />\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"send-backward\" />\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"send-to-back\" />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function MoveToPageMenu() {\n\tconst editor = useEditor()\n\tconst pages = useValue('pages', () => editor.getPages(), [editor])\n\tconst currentPageId = useValue('current page id', () => editor.getCurrentPageId(), [editor])\n\tconst { addToast } = useToasts()\n\tconst trackEvent = useUiEvents()\n\tconst isReadonlyMode = useReadonly()\n\tconst oneSelected = useUnlockedSelectedShapesCount(1)\n\n\tif (!oneSelected) return null\n\tif (isReadonlyMode) return null\n\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"move-to-page\" label=\"context-menu.move-to-page\" size=\"small\">\n\t\t\t<TldrawUiMenuGroup id=\"pages\">\n\t\t\t\t{pages.map((page) => (\n\t\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\t\tid={page.id}\n\t\t\t\t\t\tkey={page.id}\n\t\t\t\t\t\tdisabled={currentPageId === page.id}\n\t\t\t\t\t\tlabel={page.name.length > 30 ? `${page.name.slice(0, 30)}\u2026` : page.name}\n\t\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\t\teditor.markHistoryStoppingPoint('move_shapes_to_page')\n\t\t\t\t\t\t\teditor.moveShapesToPage(editor.getSelectedShapeIds(), page.id as TLPageId)\n\n\t\t\t\t\t\t\tconst toPage = editor.getPage(page.id)\n\n\t\t\t\t\t\t\tif (toPage) {\n\t\t\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\t\t\ttitle: 'Changed page',\n\t\t\t\t\t\t\t\t\tdescription: `Moved to ${toPage.name}.`,\n\t\t\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tlabel: 'Go back',\n\t\t\t\t\t\t\t\t\t\t\ttype: 'primary',\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\t\t\teditor.markHistoryStoppingPoint('change-page')\n\t\t\t\t\t\t\t\t\t\t\t\teditor.setCurrentPage(currentPageId)\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttrackEvent('move-to-page', { source: 'context-menu' })\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</TldrawUiMenuGroup>\n\t\t\t<TldrawUiMenuGroup id=\"new-page\">\n\t\t\t\t<TldrawUiMenuActionItem actionId=\"move-to-new-page\" />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public @react */\nexport function ConvertToBookmarkMenuItem() {\n\tconst editor = useEditor()\n\n\tconst oneEmbedSelected = useValue(\n\t\t'oneEmbedSelected',\n\t\t() => {\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (!onlySelectedShape) return false\n\t\t\treturn !!(\n\t\t\t\teditor.isShapeOfType<TLEmbedShape>(onlySelectedShape, 'embed') &&\n\t\t\t\tonlySelectedShape.props.url &&\n\t\t\t\t!editor.isShapeOrAncestorLocked(onlySelectedShape)\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\n\tif (!oneEmbedSelected) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"convert-to-bookmark\" />\n}\n\n/** @public @react */\nexport function ConvertToEmbedMenuItem() {\n\tconst editor = useEditor()\n\tconst getEmbedDefinition = useGetEmbedDefinition()\n\n\tconst oneEmbeddableBookmarkSelected = useValue(\n\t\t'oneEmbeddableBookmarkSelected',\n\t\t() => {\n\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\t\t\tif (!onlySelectedShape) return false\n\t\t\treturn !!(\n\t\t\t\teditor.isShapeOfType<TLBookmarkShape>(onlySelectedShape, 'bookmark') &&\n\t\t\t\tonlySelectedShape.props.url &&\n\t\t\t\tgetEmbedDefinition(onlySelectedShape.props.url) &&\n\t\t\t\t!editor.isShapeOrAncestorLocked(onlySelectedShape)\n\t\t\t)\n\t\t},\n\t\t[editor]\n\t)\n\n\tif (!oneEmbeddableBookmarkSelected) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"convert-to-embed\" />\n}\n\n/* ------------------- Preferences ------------------ */\n\n/** @public @react */\nexport function ToggleSnapModeItem() {\n\tconst editor = useEditor()\n\tconst isSnapMode = useValue('isSnapMode', () => editor.user.getIsSnapMode(), [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-snap-mode\" checked={isSnapMode} />\n}\n\n/** @public @react */\nexport function ToggleToolLockItem() {\n\tconst editor = useEditor()\n\tconst isToolLock = useValue('isToolLock', () => editor.getInstanceState().isToolLocked, [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-tool-lock\" checked={isToolLock} />\n}\n\n/** @public @react */\nexport function ToggleGridItem() {\n\tconst editor = useEditor()\n\tconst isGridMode = useValue('isGridMode', () => editor.getInstanceState().isGridMode, [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-grid\" checked={isGridMode} />\n}\n\n/** @public @react */\nexport function ToggleWrapModeItem() {\n\tconst editor = useEditor()\n\tconst isWrapMode = useValue('isWrapMode', () => editor.user.getIsWrapMode(), [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-wrap-mode\" checked={isWrapMode} />\n}\n\n/** @public @react */\nexport function ToggleDarkModeItem() {\n\tconst editor = useEditor()\n\tconst isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-dark-mode\" checked={isDarkMode} />\n}\n\n/** @public @react */\nexport function ToggleFocusModeItem() {\n\tconst editor = useEditor()\n\tconst isFocusMode = useValue('isFocusMode', () => editor.getInstanceState().isFocusMode, [editor])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-focus-mode\" checked={isFocusMode} />\n}\n\n/** @public @react */\nexport function ToggleEdgeScrollingItem() {\n\tconst editor = useEditor()\n\tconst edgeScrollSpeed = useValue('edgeScrollSpeed', () => editor.user.getEdgeScrollSpeed(), [\n\t\teditor,\n\t])\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-edge-scrolling\"\n\t\t\tchecked={edgeScrollSpeed === 1}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ToggleReduceMotionItem() {\n\tconst editor = useEditor()\n\tconst animationSpeed = useValue('animationSpeed', () => editor.user.getAnimationSpeed(), [editor])\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-reduce-motion\"\n\t\t\tchecked={animationSpeed === 0}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ToggleKeyboardShortcutsItem() {\n\tconst editor = useEditor()\n\tconst keyboardShortcuts = useValue(\n\t\t'keyboardShortcuts',\n\t\t() => editor.user.getAreKeyboardShortcutsEnabled(),\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-keyboard-shortcuts\"\n\t\t\tchecked={keyboardShortcuts}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function ToggleEnhancedA11yModeItem() {\n\tconst editor = useEditor()\n\tconst enhancedA11yMode = useValue('enhancedA11yMode', () => editor.user.getEnhancedA11yMode(), [\n\t\teditor,\n\t])\n\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"enhanced-a11y-mode\" checked={enhancedA11yMode} />\n}\n\n/** @public @react */\nexport function ToggleDebugModeItem() {\n\tconst editor = useEditor()\n\tconst isDebugMode = useValue('isDebugMode', () => editor.getInstanceState().isDebugMode, [editor])\n\treturn <TldrawUiMenuActionCheckboxItem actionId=\"toggle-debug-mode\" checked={isDebugMode} />\n}\n\n/** @public @react */\nexport function ToggleDynamicSizeModeItem() {\n\tconst editor = useEditor()\n\tconst isDynamicResizeMode = useValue(\n\t\t'dynamic resize',\n\t\t() => editor.user.getIsDynamicResizeMode(),\n\t\t[editor]\n\t)\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem\n\t\t\tactionId=\"toggle-dynamic-size-mode\"\n\t\t\tchecked={isDynamicResizeMode}\n\t\t/>\n\t)\n}\n\n/** @public @react */\nexport function TogglePasteAtCursorItem() {\n\tconst editor = useEditor()\n\tconst pasteAtCursor = useValue('paste at cursor', () => editor.user.getIsPasteAtCursorMode(), [\n\t\teditor,\n\t])\n\n\treturn (\n\t\t<TldrawUiMenuActionCheckboxItem actionId=\"toggle-paste-at-cursor\" checked={pasteAtCursor} />\n\t)\n}\n\n/* ---------------------- Print --------------------- */\n\n/** @public @react */\nexport function PrintItem() {\n\tconst editor = useEditor()\n\tconst emptyPage = useValue('emptyPage', () => editor.getCurrentPageShapeIds().size === 0, [\n\t\teditor,\n\t])\n\n\treturn <TldrawUiMenuActionItem actionId=\"print\" disabled={emptyPage} />\n}\n\n/* ---------------------- Multiplayer --------------------- */\n\n/** @public @react */\nexport function CursorChatItem() {\n\tconst editor = useEditor()\n\tconst shouldShow = useValue(\n\t\t'show cursor chat',\n\t\t() => editor.getCurrentToolId() === 'select' && !editor.getInstanceState().isCoarsePointer,\n\t\t[editor]\n\t)\n\n\tif (!shouldShow) return null\n\n\treturn <TldrawUiMenuActionItem actionId=\"open-cursor-chat\" />\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -14,6 +14,7 @@ import {
14
14
  useMaybeEditor
15
15
  } from "@tldraw/editor";
16
16
  import * as React from "react";
17
+ import { createBookmarkFromUrl } from "../../shapes/bookmark/bookmarks.mjs";
17
18
  import { fitFrameToContent, removeFrame } from "../../utils/frames/frames.mjs";
18
19
  import { generateShapeAnnouncementMessage } from "../components/A11y.mjs";
19
20
  import { EditLinkDialog } from "../components/EditLinkDialog.mjs";
@@ -309,38 +310,31 @@ function ActionsProvider({ overrides, children }) {
309
310
  {
310
311
  id: "convert-to-bookmark",
311
312
  label: "action.convert-to-bookmark",
312
- onSelect(source) {
313
+ async onSelect(source) {
313
314
  if (!canApplySelectionAction()) return;
314
315
  if (mustGoBackToSelectToolFirst()) return;
315
- editor.run(() => {
316
- trackEvent("convert-to-bookmark", { source });
317
- const shapes = editor.getSelectedShapes();
318
- const createList = [];
319
- const deleteList = [];
320
- for (const shape of shapes) {
321
- if (!shape || !editor.isShapeOfType(shape, "embed") || !shape.props.url)
322
- continue;
323
- const newPos = new Vec(shape.x, shape.y);
324
- newPos.rot(-shape.rotation);
325
- newPos.add(new Vec(shape.props.w / 2 - 300 / 2, shape.props.h / 2 - 320 / 2));
326
- newPos.rot(shape.rotation);
327
- const partial = {
328
- id: createShapeId(),
329
- type: "bookmark",
330
- rotation: shape.rotation,
331
- x: newPos.x,
332
- y: newPos.y,
333
- opacity: 1,
334
- props: {
335
- url: shape.props.url
316
+ trackEvent("convert-to-bookmark", { source });
317
+ const shapes = editor.getSelectedShapes();
318
+ const markId = editor.markHistoryStoppingPoint("convert shapes to bookmark");
319
+ const creationPromises = [];
320
+ for (const shape of shapes) {
321
+ if (!shape || !editor.isShapeOfType(shape, "embed") || !shape.props.url)
322
+ continue;
323
+ const center = editor.getShapePageBounds(shape)?.center;
324
+ if (!center) continue;
325
+ editor.deleteShapes([shape.id]);
326
+ creationPromises.push(
327
+ createBookmarkFromUrl(editor, { url: shape.props.url, center }).then((res) => {
328
+ if (!res.ok) {
329
+ throw new Error(res.error);
336
330
  }
337
- };
338
- createList.push(partial);
339
- deleteList.push(shape.id);
340
- }
341
- editor.markHistoryStoppingPoint("convert shapes to bookmark");
342
- editor.deleteShapes(deleteList);
343
- editor.createShapes(createList);
331
+ return res;
332
+ })
333
+ );
334
+ }
335
+ await Promise.all(creationPromises).catch((error) => {
336
+ editor.bailToMark(markId);
337
+ console.error(error);
344
338
  });
345
339
  }
346
340
  },