tldraw 4.2.0-next.47462e908ff5 → 4.2.0-next.6aa322101785

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 (67) hide show
  1. package/dist-cjs/index.d.ts +21 -4
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +1 -1
  5. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  6. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +14 -6
  7. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  8. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +2 -2
  9. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  10. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js +10 -7
  11. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js.map +2 -2
  12. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js +6 -2
  13. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js.map +2 -2
  14. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js +2 -1
  15. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js.map +2 -2
  16. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +2 -2
  17. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  18. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  19. package/dist-cjs/lib/ui/getLocalFiles.js +18 -3
  20. package/dist-cjs/lib/ui/getLocalFiles.js.map +2 -2
  21. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +18 -16
  22. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +3 -3
  23. package/dist-cjs/lib/ui/version.js +3 -3
  24. package/dist-cjs/lib/ui/version.js.map +1 -1
  25. package/dist-cjs/lib/utils/text/richText.js +5 -6
  26. package/dist-cjs/lib/utils/text/richText.js.map +3 -3
  27. package/dist-esm/index.d.mts +21 -4
  28. package/dist-esm/index.mjs +1 -1
  29. package/dist-esm/index.mjs.map +2 -2
  30. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +2 -1
  31. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  32. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +14 -6
  33. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  34. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +2 -2
  35. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  36. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs +10 -7
  37. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs.map +2 -2
  38. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs +6 -2
  39. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs.map +2 -2
  40. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs +2 -1
  41. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs.map +2 -2
  42. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
  43. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  44. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  45. package/dist-esm/lib/ui/getLocalFiles.mjs +18 -3
  46. package/dist-esm/lib/ui/getLocalFiles.mjs.map +2 -2
  47. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +18 -16
  48. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +3 -3
  49. package/dist-esm/lib/ui/version.mjs +3 -3
  50. package/dist-esm/lib/ui/version.mjs.map +1 -1
  51. package/dist-esm/lib/utils/text/richText.mjs +5 -6
  52. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  53. package/package.json +10 -10
  54. package/src/index.ts +3 -0
  55. package/src/lib/shapes/shared/RichTextLabel.tsx +2 -1
  56. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +19 -8
  57. package/src/lib/tools/SelectTool/childStates/Idle.ts +2 -2
  58. package/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx +27 -7
  59. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx +6 -2
  60. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.tsx +4 -1
  61. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +3 -3
  62. package/src/lib/ui/context/events.tsx +1 -0
  63. package/src/lib/ui/getLocalFiles.ts +20 -3
  64. package/src/lib/ui/hooks/useClipboardEvents.ts +12 -9
  65. package/src/lib/ui/version.ts +3 -3
  66. package/src/lib/utils/text/richText.ts +5 -5
  67. package/src/test/customSnapping.test.tsx +185 -0
@@ -26,7 +26,10 @@ import { TldrawUiMenuCheckboxItem } from "../primitives/menus/TldrawUiMenuCheckb
26
26
  import { TldrawUiMenuGroup } from "../primitives/menus/TldrawUiMenuGroup.mjs";
27
27
  import { TldrawUiMenuItem } from "../primitives/menus/TldrawUiMenuItem.mjs";
28
28
  import { TldrawUiMenuSubmenu } from "../primitives/menus/TldrawUiMenuSubmenu.mjs";
29
- function DefaultDebugMenuContent() {
29
+ function DefaultDebugMenuContent({
30
+ customDebugFlags,
31
+ customFeatureFlags
32
+ }) {
30
33
  const editor = useEditor();
31
34
  const { addToast } = useToasts();
32
35
  const { addDialog } = useDialogs();
@@ -160,18 +163,18 @@ function DefaultDebugMenuContent() {
160
163
  /* @__PURE__ */ jsx(TldrawUiMenuItem, { id: "throw-error", onSelect: () => setError(true), label: "Throw error" })
161
164
  ] }),
162
165
  /* @__PURE__ */ jsxs(TldrawUiMenuGroup, { id: "flags", children: [
163
- /* @__PURE__ */ jsx(DebugFlags, {}),
164
- /* @__PURE__ */ jsx(FeatureFlags, {})
166
+ /* @__PURE__ */ jsx(DebugFlags, { customDebugFlags }),
167
+ /* @__PURE__ */ jsx(FeatureFlags, { customFeatureFlags })
165
168
  ] })
166
169
  ] });
167
170
  }
168
- function DebugFlags() {
169
- const items = Object.values(debugFlags);
171
+ function DebugFlags(props) {
172
+ const items = Object.values(props.customDebugFlags ?? debugFlags);
170
173
  if (!items.length) return null;
171
174
  return /* @__PURE__ */ jsx(TldrawUiMenuSubmenu, { id: "debug flags", label: "Debug flags", children: /* @__PURE__ */ jsx(TldrawUiMenuGroup, { id: "debug flags", children: items.map((flag) => /* @__PURE__ */ jsx(DebugFlagToggle, { flag }, flag.name)) }) });
172
175
  }
173
- function FeatureFlags() {
174
- const items = Object.values(featureFlags);
176
+ function FeatureFlags(props) {
177
+ const items = Object.values(props.customFeatureFlags ?? featureFlags);
175
178
  if (!items.length) return null;
176
179
  return /* @__PURE__ */ jsx(TldrawUiMenuSubmenu, { id: "feature flags", label: "Feature flags", children: /* @__PURE__ */ jsx(TldrawUiMenuGroup, { id: "feature flags", children: items.map((flag) => /* @__PURE__ */ jsx(DebugFlagToggle, { flag }, flag.name)) }) });
177
180
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx"],
4
- "sourcesContent": ["import {\n\tDebugFlag,\n\tEditor,\n\tTLShapePartial,\n\tcreateShapeId,\n\tdebugFlags,\n\tfeatureFlags,\n\thardResetEditor,\n\ttrack,\n\tuniqueId,\n\tuseEditor,\n} from '@tldraw/editor'\nimport React from 'react'\nimport { useDialogs } from '../../context/dialogs'\nimport { useToasts } from '../../context/toasts'\nimport { untranslated } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonCheck } from '../primitives/Button/TldrawUiButtonCheck'\nimport { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'\nimport {\n\tTldrawUiDialogBody,\n\tTldrawUiDialogCloseButton,\n\tTldrawUiDialogFooter,\n\tTldrawUiDialogHeader,\n\tTldrawUiDialogTitle,\n} from '../primitives/TldrawUiDialog'\nimport { TldrawUiMenuCheckboxItem } from '../primitives/menus/TldrawUiMenuCheckboxItem'\nimport { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'\nimport { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'\nimport { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'\n\n/** @public @react */\nexport function DefaultDebugMenuContent() {\n\tconst editor = useEditor()\n\tconst { addToast } = useToasts()\n\tconst { addDialog } = useDialogs()\n\tconst [error, setError] = React.useState<boolean>(false)\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiMenuGroup id=\"items\">\n\t\t\t\t<TldrawUiMenuItem id=\"hard-reset\" onSelect={hardResetEditor} label={'Hard reset'} />\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"add-toast\"\n\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something good happened',\n\t\t\t\t\t\t\tdescription: 'Hey, attend to this thing over here. It might be important!',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t\tseverity: 'success',\n\t\t\t\t\t\t})\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something happened',\n\t\t\t\t\t\t\tdescription: 'Hey, attend to this thing over here. It might be important!',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t\tseverity: 'info',\n\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Primary',\n\t\t\t\t\t\t\t\t\ttype: 'primary',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Normal',\n\t\t\t\t\t\t\t\t\ttype: 'normal',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Danger',\n\t\t\t\t\t\t\t\t\ttype: 'danger',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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})\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something maybe bad happened',\n\t\t\t\t\t\t\tdescription: 'Hey, attend to this thing over here. It might be important!',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Primary',\n\t\t\t\t\t\t\t\t\ttype: 'primary',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Normal',\n\t\t\t\t\t\t\t\t\ttype: 'normal',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Danger',\n\t\t\t\t\t\t\t\t\ttype: 'danger',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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})\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something bad happened',\n\t\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t}}\n\t\t\t\t\tlabel={untranslated('Show toast')}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"show-dialog\"\n\t\t\t\t\tlabel={'Show dialog'}\n\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\taddDialog({\n\t\t\t\t\t\t\tcomponent: ({ onClose }) => (\n\t\t\t\t\t\t\t\t<ExampleDialog\n\t\t\t\t\t\t\t\t\tdisplayDontShowAgain\n\t\t\t\t\t\t\t\t\tonCancel={() => onClose()}\n\t\t\t\t\t\t\t\t\tonContinue={() => onClose()}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tonClose: () => {\n\t\t\t\t\t\t\t\tvoid null\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"create-shapes\"\n\t\t\t\t\tlabel={'Create 100 shapes'}\n\t\t\t\t\tonSelect={() => createNShapes(editor, 100)}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"count-nodes\"\n\t\t\t\t\tlabel={'Count shapes / nodes'}\n\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\t\t\t\tconst shapes =\n\t\t\t\t\t\t\tselectedShapes.length === 0 ? editor.getRenderingShapes() : selectedShapes\n\t\t\t\t\t\twindow.alert(\n\t\t\t\t\t\t\t`Shapes ${shapes.length}, DOM nodes:${document.querySelector('.tl-shapes')!.querySelectorAll('*')?.length}`\n\t\t\t\t\t\t)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t{(() => {\n\t\t\t\t\tif (error) throw Error('oh no!')\n\t\t\t\t\treturn null\n\t\t\t\t})()}\n\t\t\t\t<TldrawUiMenuItem id=\"throw-error\" onSelect={() => setError(true)} label={'Throw error'} />\n\t\t\t</TldrawUiMenuGroup>\n\t\t\t<TldrawUiMenuGroup id=\"flags\">\n\t\t\t\t<DebugFlags />\n\t\t\t\t<FeatureFlags />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</>\n\t)\n}\n/** @public @react */\nexport function DebugFlags() {\n\tconst items = Object.values(debugFlags)\n\tif (!items.length) return null\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"debug flags\" label=\"Debug flags\">\n\t\t\t<TldrawUiMenuGroup id=\"debug flags\">\n\t\t\t\t{items.map((flag) => (\n\t\t\t\t\t<DebugFlagToggle key={flag.name} flag={flag} />\n\t\t\t\t))}\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n/** @public @react */\nexport function FeatureFlags() {\n\tconst items = Object.values(featureFlags)\n\tif (!items.length) return null\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"feature flags\" label=\"Feature flags\">\n\t\t\t<TldrawUiMenuGroup id=\"feature flags\">\n\t\t\t\t{items.map((flag) => (\n\t\t\t\t\t<DebugFlagToggle key={flag.name} flag={flag} />\n\t\t\t\t))}\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public */\nexport interface ExampleDialogProps {\n\ttitle?: string\n\tbody?: React.ReactNode\n\tcancel?: string\n\tconfirm?: string\n\tdisplayDontShowAgain?: boolean\n\tmaxWidth?: string\n\tonCancel(): void\n\tonContinue(): void\n}\n\n/** @public @react */\nexport function ExampleDialog({\n\ttitle = 'title',\n\tbody = 'hello hello hello',\n\tcancel = 'Cancel',\n\tconfirm = 'Continue',\n\tdisplayDontShowAgain = false,\n\tmaxWidth = '350',\n\tonCancel,\n\tonContinue,\n}: ExampleDialogProps) {\n\tconst [dontShowAgain, setDontShowAgain] = React.useState(false)\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiDialogHeader>\n\t\t\t\t<TldrawUiDialogTitle>{title}</TldrawUiDialogTitle>\n\t\t\t\t<TldrawUiDialogCloseButton />\n\t\t\t</TldrawUiDialogHeader>\n\t\t\t<TldrawUiDialogBody style={{ maxWidth }}>{body}</TldrawUiDialogBody>\n\t\t\t<TldrawUiDialogFooter className=\"tlui-dialog__footer__actions\">\n\t\t\t\t{displayDontShowAgain && (\n\t\t\t\t\t<TldrawUiButton\n\t\t\t\t\t\ttype=\"normal\"\n\t\t\t\t\t\tonClick={() => setDontShowAgain(!dontShowAgain)}\n\t\t\t\t\t\tstyle={{ marginRight: 'auto' }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<TldrawUiButtonCheck checked={dontShowAgain} />\n\t\t\t\t\t\t<TldrawUiButtonLabel>Don\u2019t show again</TldrawUiButtonLabel>\n\t\t\t\t\t</TldrawUiButton>\n\t\t\t\t)}\n\t\t\t\t<TldrawUiButton type=\"normal\" onClick={onCancel}>\n\t\t\t\t\t<TldrawUiButtonLabel>{cancel}</TldrawUiButtonLabel>\n\t\t\t\t</TldrawUiButton>\n\t\t\t\t<TldrawUiButton type=\"primary\" onClick={async () => onContinue()}>\n\t\t\t\t\t<TldrawUiButtonLabel>{confirm}</TldrawUiButtonLabel>\n\t\t\t\t</TldrawUiButton>\n\t\t\t</TldrawUiDialogFooter>\n\t\t</>\n\t)\n}\n\nconst DebugFlagToggle = track(function DebugFlagToggle({\n\tflag,\n\tonChange,\n}: {\n\tflag: DebugFlag<boolean>\n\tonChange?(newValue: boolean): void\n}) {\n\tconst value = flag.get()\n\treturn (\n\t\t<TldrawUiMenuCheckboxItem\n\t\t\tid={flag.name}\n\t\t\ttitle={flag.name}\n\t\t\tlabel={flag.name\n\t\t\t\t.replace(/([a-z0-9])([A-Z])/g, (m) => `${m[0]} ${m[1].toLowerCase()}`)\n\t\t\t\t.replace(/^[a-z]/, (m) => m.toUpperCase())}\n\t\t\tchecked={value}\n\t\t\tonSelect={() => {\n\t\t\t\tflag.set(!value)\n\t\t\t\tonChange?.(!value)\n\t\t\t}}\n\t\t/>\n\t)\n})\n\nlet t = 0\n\nfunction createNShapes(editor: Editor, n: number) {\n\tconst gap = editor.options.adjacentShapeMargin\n\tconst shapesToCreate: TLShapePartial[] = Array(n)\n\tconst cols = Math.floor(Math.sqrt(n))\n\n\tfor (let i = 0; i < n; i++) {\n\t\tt++\n\t\tshapesToCreate[i] = {\n\t\t\tid: createShapeId('box' + t),\n\t\t\ttype: 'geo',\n\t\t\tx: (i % cols) * (100 + gap),\n\t\t\ty: Math.floor(i / cols) * (100 + gap),\n\t\t}\n\t}\n\n\teditor.run(() => {\n\t\t// allow this to trigger the max shapes alert\n\t\teditor.createShapes(shapesToCreate).setSelectedShapes(shapesToCreate.map((s) => s.id))\n\t})\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tDebugFlag,\n\tEditor,\n\tTLShapePartial,\n\tcreateShapeId,\n\tdebugFlags,\n\tfeatureFlags,\n\thardResetEditor,\n\ttrack,\n\tuniqueId,\n\tuseEditor,\n} from '@tldraw/editor'\nimport React from 'react'\nimport { useDialogs } from '../../context/dialogs'\nimport { useToasts } from '../../context/toasts'\nimport { untranslated } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonCheck } from '../primitives/Button/TldrawUiButtonCheck'\nimport { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'\nimport {\n\tTldrawUiDialogBody,\n\tTldrawUiDialogCloseButton,\n\tTldrawUiDialogFooter,\n\tTldrawUiDialogHeader,\n\tTldrawUiDialogTitle,\n} from '../primitives/TldrawUiDialog'\nimport { TldrawUiMenuCheckboxItem } from '../primitives/menus/TldrawUiMenuCheckboxItem'\nimport { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'\nimport { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'\nimport { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'\n\n/** @public */\nexport interface CustomDebugFlags {\n\tcustomDebugFlags?: Record<string, DebugFlag<boolean>>\n\tcustomFeatureFlags?: Record<string, DebugFlag<boolean>>\n}\n\n/** @public @react */\nexport function DefaultDebugMenuContent({\n\tcustomDebugFlags,\n\tcustomFeatureFlags,\n}: CustomDebugFlags) {\n\tconst editor = useEditor()\n\tconst { addToast } = useToasts()\n\tconst { addDialog } = useDialogs()\n\tconst [error, setError] = React.useState<boolean>(false)\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiMenuGroup id=\"items\">\n\t\t\t\t<TldrawUiMenuItem id=\"hard-reset\" onSelect={hardResetEditor} label={'Hard reset'} />\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"add-toast\"\n\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something good happened',\n\t\t\t\t\t\t\tdescription: 'Hey, attend to this thing over here. It might be important!',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t\tseverity: 'success',\n\t\t\t\t\t\t})\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something happened',\n\t\t\t\t\t\t\tdescription: 'Hey, attend to this thing over here. It might be important!',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t\tseverity: 'info',\n\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Primary',\n\t\t\t\t\t\t\t\t\ttype: 'primary',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Normal',\n\t\t\t\t\t\t\t\t\ttype: 'normal',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Danger',\n\t\t\t\t\t\t\t\t\ttype: 'danger',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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})\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something maybe bad happened',\n\t\t\t\t\t\t\tdescription: 'Hey, attend to this thing over here. It might be important!',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Primary',\n\t\t\t\t\t\t\t\t\ttype: 'primary',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Normal',\n\t\t\t\t\t\t\t\t\ttype: 'normal',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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\t{\n\t\t\t\t\t\t\t\t\tlabel: 'Danger',\n\t\t\t\t\t\t\t\t\ttype: 'danger',\n\t\t\t\t\t\t\t\t\tonClick: () => {\n\t\t\t\t\t\t\t\t\t\tvoid null\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})\n\t\t\t\t\t\taddToast({\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\ttitle: 'Something bad happened',\n\t\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t\t\tkeepOpen: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t}}\n\t\t\t\t\tlabel={untranslated('Show toast')}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"show-dialog\"\n\t\t\t\t\tlabel={'Show dialog'}\n\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\taddDialog({\n\t\t\t\t\t\t\tcomponent: ({ onClose }) => (\n\t\t\t\t\t\t\t\t<ExampleDialog\n\t\t\t\t\t\t\t\t\tdisplayDontShowAgain\n\t\t\t\t\t\t\t\t\tonCancel={() => onClose()}\n\t\t\t\t\t\t\t\t\tonContinue={() => onClose()}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tonClose: () => {\n\t\t\t\t\t\t\t\tvoid null\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"create-shapes\"\n\t\t\t\t\tlabel={'Create 100 shapes'}\n\t\t\t\t\tonSelect={() => createNShapes(editor, 100)}\n\t\t\t\t/>\n\t\t\t\t<TldrawUiMenuItem\n\t\t\t\t\tid=\"count-nodes\"\n\t\t\t\t\tlabel={'Count shapes / nodes'}\n\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\t\t\t\tconst shapes =\n\t\t\t\t\t\t\tselectedShapes.length === 0 ? editor.getRenderingShapes() : selectedShapes\n\t\t\t\t\t\twindow.alert(\n\t\t\t\t\t\t\t`Shapes ${shapes.length}, DOM nodes:${document.querySelector('.tl-shapes')!.querySelectorAll('*')?.length}`\n\t\t\t\t\t\t)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t\t{(() => {\n\t\t\t\t\tif (error) throw Error('oh no!')\n\t\t\t\t\treturn null\n\t\t\t\t})()}\n\t\t\t\t<TldrawUiMenuItem id=\"throw-error\" onSelect={() => setError(true)} label={'Throw error'} />\n\t\t\t</TldrawUiMenuGroup>\n\t\t\t<TldrawUiMenuGroup id=\"flags\">\n\t\t\t\t<DebugFlags customDebugFlags={customDebugFlags} />\n\t\t\t\t<FeatureFlags customFeatureFlags={customFeatureFlags} />\n\t\t\t</TldrawUiMenuGroup>\n\t\t</>\n\t)\n}\n\n/** @public */\nexport interface DebugFlagsProps {\n\tcustomDebugFlags?: Record<string, DebugFlag<boolean>> | undefined\n}\n\n/** @public @react */\nexport function DebugFlags(props: DebugFlagsProps) {\n\tconst items = Object.values(props.customDebugFlags ?? debugFlags)\n\tif (!items.length) return null\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"debug flags\" label=\"Debug flags\">\n\t\t\t<TldrawUiMenuGroup id=\"debug flags\">\n\t\t\t\t{items.map((flag) => (\n\t\t\t\t\t<DebugFlagToggle key={flag.name} flag={flag} />\n\t\t\t\t))}\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n/** @public */\nexport interface FeatureFlagsProps {\n\tcustomFeatureFlags?: Record<string, DebugFlag<boolean>> | undefined\n}\n\n/** @public @react */\nexport function FeatureFlags(props: FeatureFlagsProps) {\n\tconst items = Object.values(props.customFeatureFlags ?? featureFlags)\n\tif (!items.length) return null\n\treturn (\n\t\t<TldrawUiMenuSubmenu id=\"feature flags\" label=\"Feature flags\">\n\t\t\t<TldrawUiMenuGroup id=\"feature flags\">\n\t\t\t\t{items.map((flag) => (\n\t\t\t\t\t<DebugFlagToggle key={flag.name} flag={flag} />\n\t\t\t\t))}\n\t\t\t</TldrawUiMenuGroup>\n\t\t</TldrawUiMenuSubmenu>\n\t)\n}\n\n/** @public */\nexport interface ExampleDialogProps {\n\ttitle?: string\n\tbody?: React.ReactNode\n\tcancel?: string\n\tconfirm?: string\n\tdisplayDontShowAgain?: boolean\n\tmaxWidth?: string\n\tonCancel(): void\n\tonContinue(): void\n}\n\n/** @public @react */\nexport function ExampleDialog({\n\ttitle = 'title',\n\tbody = 'hello hello hello',\n\tcancel = 'Cancel',\n\tconfirm = 'Continue',\n\tdisplayDontShowAgain = false,\n\tmaxWidth = '350',\n\tonCancel,\n\tonContinue,\n}: ExampleDialogProps) {\n\tconst [dontShowAgain, setDontShowAgain] = React.useState(false)\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiDialogHeader>\n\t\t\t\t<TldrawUiDialogTitle>{title}</TldrawUiDialogTitle>\n\t\t\t\t<TldrawUiDialogCloseButton />\n\t\t\t</TldrawUiDialogHeader>\n\t\t\t<TldrawUiDialogBody style={{ maxWidth }}>{body}</TldrawUiDialogBody>\n\t\t\t<TldrawUiDialogFooter className=\"tlui-dialog__footer__actions\">\n\t\t\t\t{displayDontShowAgain && (\n\t\t\t\t\t<TldrawUiButton\n\t\t\t\t\t\ttype=\"normal\"\n\t\t\t\t\t\tonClick={() => setDontShowAgain(!dontShowAgain)}\n\t\t\t\t\t\tstyle={{ marginRight: 'auto' }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<TldrawUiButtonCheck checked={dontShowAgain} />\n\t\t\t\t\t\t<TldrawUiButtonLabel>Don\u2019t show again</TldrawUiButtonLabel>\n\t\t\t\t\t</TldrawUiButton>\n\t\t\t\t)}\n\t\t\t\t<TldrawUiButton type=\"normal\" onClick={onCancel}>\n\t\t\t\t\t<TldrawUiButtonLabel>{cancel}</TldrawUiButtonLabel>\n\t\t\t\t</TldrawUiButton>\n\t\t\t\t<TldrawUiButton type=\"primary\" onClick={async () => onContinue()}>\n\t\t\t\t\t<TldrawUiButtonLabel>{confirm}</TldrawUiButtonLabel>\n\t\t\t\t</TldrawUiButton>\n\t\t\t</TldrawUiDialogFooter>\n\t\t</>\n\t)\n}\n\nconst DebugFlagToggle = track(function DebugFlagToggle({\n\tflag,\n\tonChange,\n}: {\n\tflag: DebugFlag<boolean>\n\tonChange?(newValue: boolean): void\n}) {\n\tconst value = flag.get()\n\treturn (\n\t\t<TldrawUiMenuCheckboxItem\n\t\t\tid={flag.name}\n\t\t\ttitle={flag.name}\n\t\t\tlabel={flag.name\n\t\t\t\t.replace(/([a-z0-9])([A-Z])/g, (m) => `${m[0]} ${m[1].toLowerCase()}`)\n\t\t\t\t.replace(/^[a-z]/, (m) => m.toUpperCase())}\n\t\t\tchecked={value}\n\t\t\tonSelect={() => {\n\t\t\t\tflag.set(!value)\n\t\t\t\tonChange?.(!value)\n\t\t\t}}\n\t\t/>\n\t)\n})\n\nlet t = 0\n\nfunction createNShapes(editor: Editor, n: number) {\n\tconst gap = editor.options.adjacentShapeMargin\n\tconst shapesToCreate: TLShapePartial[] = Array(n)\n\tconst cols = Math.floor(Math.sqrt(n))\n\n\tfor (let i = 0; i < n; i++) {\n\t\tt++\n\t\tshapesToCreate[i] = {\n\t\t\tid: createShapeId('box' + t),\n\t\t\ttype: 'geo',\n\t\t\tx: (i % cols) * (100 + gap),\n\t\t\ty: Math.floor(i / cols) * (100 + gap),\n\t\t}\n\t}\n\n\teditor.run(() => {\n\t\t// allow this to trigger the max shapes alert\n\t\teditor.createShapes(shapesToCreate).setSelectedShapes(shapesToCreate.map((s) => s.id))\n\t})\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["DebugFlagToggle"]
7
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
  }
@@ -37,6 +37,7 @@ function DefaultRichTextToolbarContent({
37
37
  }, [onEditLinkStart]);
38
38
  const actions = useMemo(() => {
39
39
  function handleOp(name, op) {
40
+ if (!textEditor.view) return;
40
41
  trackEvent("rich-text", { operation: name, source });
41
42
  textEditor.chain().focus()[op]().run();
42
43
  }
@@ -84,7 +85,7 @@ function DefaultRichTextToolbarContent({
84
85
  ].filter(Boolean);
85
86
  }, [textEditor, trackEvent, onEditLinkStart]);
86
87
  return actions.map(({ name, attrs, onSelect }) => {
87
- const isActive = textEditor.isActive(name, attrs);
88
+ const isActive = textEditor.view ? textEditor.isActive(name, attrs) : false;
88
89
  return /* @__PURE__ */ jsx(
89
90
  TldrawUiToolbarButton,
90
91
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.tsx"],
4
- "sourcesContent": ["import { isAccelKey, preventDefault, TiptapEditor } from '@tldraw/editor'\nimport { useEffect, useMemo, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\n\n/** @public */\nexport interface DefaultRichTextToolbarContentProps {\n\tonEditLinkStart?(): void\n\ttextEditor: TiptapEditor\n}\n\n/**\n * Rich text toolbar items that have the basics.\n *\n * @public @react\n */\nexport function DefaultRichTextToolbarContent({\n\ttextEditor,\n\tonEditLinkStart,\n}: DefaultRichTextToolbarContentProps) {\n\tconst trackEvent = useUiEvents()\n\tconst msg = useTranslation()\n\tconst source = 'rich-text-menu'\n\n\t// We need to force this one to update when the editor updates or when selection changes\n\tconst [_, set] = useState(0)\n\tuseEffect(\n\t\tfunction forceUpdateWhenContentChanges() {\n\t\t\tfunction forceUpdate() {\n\t\t\t\tset((t) => t + 1)\n\t\t\t}\n\t\t\ttextEditor.on('update', forceUpdate)\n\t\t\ttextEditor.on('selectionUpdate', forceUpdate)\n\t\t},\n\t\t[textEditor]\n\t)\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (onEditLinkStart && isAccelKey(event) && event.shiftKey && event.key === 'k') {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tonEditLinkStart()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown)\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown)\n\t\t}\n\t}, [onEditLinkStart])\n\n\t// todo: we could make this a prop\n\tconst actions = useMemo(() => {\n\t\tfunction handleOp(name: string, op: string) {\n\t\t\ttrackEvent('rich-text', { operation: name as any, source })\n\t\t\t// @ts-expect-error typing this is annoying at the moment.\n\t\t\ttextEditor.chain().focus()[op]().run()\n\t\t}\n\n\t\treturn [\n\t\t\t// { name: 'heading', attrs: { level: 3 }, onSelect() { textEditor.chain().focus().toggleHeading({ level: 3}).run() }},\n\t\t\t{\n\t\t\t\tname: 'bold',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bold', 'toggleBold')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'italic',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bold', 'toggleItalic')\n\t\t\t\t},\n\t\t\t},\n\t\t\t// { name: 'underline', onSelect() { handleOp('underline', 'toggleUnderline') }},\n\t\t\t// { name: 'strike', onSelect() { handleOp('strike', 'toggleStrike') }},\n\t\t\t{\n\t\t\t\tname: 'code',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bold', 'toggleCode')\n\t\t\t\t},\n\t\t\t},\n\t\t\tonEditLinkStart\n\t\t\t\t? {\n\t\t\t\t\t\tname: 'link',\n\t\t\t\t\t\tonSelect() {\n\t\t\t\t\t\t\tonEditLinkStart()\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: undefined, // ? is this really optional?\n\t\t\t{\n\t\t\t\tname: 'bulletList',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bulletList', 'toggleBulletList')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'highlight',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bulletList', 'toggleHighlight')\n\t\t\t\t},\n\t\t\t},\n\t\t].filter(Boolean) as {\n\t\t\tname: string\n\t\t\tattrs?: string\n\t\t\tonSelect(): void\n\t\t}[]\n\t}, [textEditor, trackEvent, onEditLinkStart])\n\n\treturn actions.map(({ name, attrs, onSelect }) => {\n\t\tconst isActive = textEditor.isActive(name, attrs)\n\t\treturn (\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\tkey={name}\n\t\t\t\ttitle={msg(`tool.rich-text-${name}`)}\n\t\t\t\tdata-testid={`rich-text.${name}`}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tisActive={isActive} // todo: we need to update this only when the text editor \"settles\", ie not during a change of selection\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={onSelect}\n\t\t\t\trole=\"option\"\n\t\t\t\taria-pressed={isActive}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon={name} />\n\t\t\t</TldrawUiToolbarButton>\n\t\t)\n\t})\n}\n"],
5
- "mappings": "AA4HI;AA5HJ,SAAS,YAAY,sBAAoC;AACzD,SAAS,WAAW,SAAS,gBAAgB;AAC7C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AAa/B,SAAS,8BAA8B;AAAA,EAC7C;AAAA,EACA;AACD,GAAuC;AACtC,QAAM,aAAa,YAAY;AAC/B,QAAM,MAAM,eAAe;AAC3B,QAAM,SAAS;AAGf,QAAM,CAAC,GAAG,GAAG,IAAI,SAAS,CAAC;AAC3B;AAAA,IACC,SAAS,gCAAgC;AACxC,eAAS,cAAc;AACtB,YAAI,CAAC,MAAM,IAAI,CAAC;AAAA,MACjB;AACA,iBAAW,GAAG,UAAU,WAAW;AACnC,iBAAW,GAAG,mBAAmB,WAAW;AAAA,IAC7C;AAAA,IACA,CAAC,UAAU;AAAA,EACZ;AAEA,YAAU,MAAM;AACf,aAAS,cAAc,OAAsB;AAC5C,UAAI,mBAAmB,WAAW,KAAK,KAAK,MAAM,YAAY,MAAM,QAAQ,KAAK;AAChF,cAAM,eAAe;AACrB,wBAAgB;AAAA,MACjB;AAAA,IACD;AAEA,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM;AACZ,eAAS,oBAAoB,WAAW,aAAa;AAAA,IACtD;AAAA,EACD,GAAG,CAAC,eAAe,CAAC;AAGpB,QAAM,UAAU,QAAQ,MAAM;AAC7B,aAAS,SAAS,MAAc,IAAY;AAC3C,iBAAW,aAAa,EAAE,WAAW,MAAa,OAAO,CAAC;AAE1D,iBAAW,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI;AAAA,IACtC;AAEA,WAAO;AAAA;AAAA,MAEN;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,QAAQ,YAAY;AAAA,QAC9B;AAAA,MACD;AAAA,MACA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,QAAQ,cAAc;AAAA,QAChC;AAAA,MACD;AAAA;AAAA;AAAA,MAGA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,QAAQ,YAAY;AAAA,QAC9B;AAAA,MACD;AAAA,MACA,kBACG;AAAA,QACA,MAAM;AAAA,QACN,WAAW;AACV,0BAAgB;AAAA,QACjB;AAAA,MACD,IACC;AAAA;AAAA,MACH;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,cAAc,kBAAkB;AAAA,QAC1C;AAAA,MACD;AAAA,MACA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,cAAc,iBAAiB;AAAA,QACzC;AAAA,MACD;AAAA,IACD,EAAE,OAAO,OAAO;AAAA,EAKjB,GAAG,CAAC,YAAY,YAAY,eAAe,CAAC;AAE5C,SAAO,QAAQ,IAAI,CAAC,EAAE,MAAM,OAAO,SAAS,MAAM;AACjD,UAAM,WAAW,WAAW,SAAS,MAAM,KAAK;AAChD,WACC;AAAA,MAAC;AAAA;AAAA,QAEA,OAAO,IAAI,kBAAkB,IAAI,EAAE;AAAA,QACnC,eAAa,aAAa,IAAI;AAAA,QAC9B,MAAK;AAAA,QACL;AAAA,QACA,eAAe;AAAA,QACf,SAAS;AAAA,QACT,MAAK;AAAA,QACL,gBAAc;AAAA,QAEd,8BAAC,sBAAmB,OAAK,MAAC,MAAM,MAAM;AAAA;AAAA,MAVjC;AAAA,IAWN;AAAA,EAEF,CAAC;AACF;",
4
+ "sourcesContent": ["import { isAccelKey, preventDefault, TiptapEditor } from '@tldraw/editor'\nimport { useEffect, useMemo, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\n\n/** @public */\nexport interface DefaultRichTextToolbarContentProps {\n\tonEditLinkStart?(): void\n\ttextEditor: TiptapEditor\n}\n\n/**\n * Rich text toolbar items that have the basics.\n *\n * @public @react\n */\nexport function DefaultRichTextToolbarContent({\n\ttextEditor,\n\tonEditLinkStart,\n}: DefaultRichTextToolbarContentProps) {\n\tconst trackEvent = useUiEvents()\n\tconst msg = useTranslation()\n\tconst source = 'rich-text-menu'\n\n\t// We need to force this one to update when the editor updates or when selection changes\n\tconst [_, set] = useState(0)\n\tuseEffect(\n\t\tfunction forceUpdateWhenContentChanges() {\n\t\t\tfunction forceUpdate() {\n\t\t\t\tset((t) => t + 1)\n\t\t\t}\n\t\t\ttextEditor.on('update', forceUpdate)\n\t\t\ttextEditor.on('selectionUpdate', forceUpdate)\n\t\t},\n\t\t[textEditor]\n\t)\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (onEditLinkStart && isAccelKey(event) && event.shiftKey && event.key === 'k') {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tonEditLinkStart()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown)\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown)\n\t\t}\n\t}, [onEditLinkStart])\n\n\t// todo: we could make this a prop\n\tconst actions = useMemo(() => {\n\t\tfunction handleOp(name: string, op: string) {\n\t\t\t// Check if the editor view is available before calling operations\n\t\t\tif (!textEditor.view) return\n\n\t\t\ttrackEvent('rich-text', { operation: name as any, source })\n\t\t\t// @ts-expect-error typing this is annoying at the moment.\n\t\t\ttextEditor.chain().focus()[op]().run()\n\t\t}\n\n\t\treturn [\n\t\t\t// { name: 'heading', attrs: { level: 3 }, onSelect() { textEditor.chain().focus().toggleHeading({ level: 3}).run() }},\n\t\t\t{\n\t\t\t\tname: 'bold',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bold', 'toggleBold')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'italic',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bold', 'toggleItalic')\n\t\t\t\t},\n\t\t\t},\n\t\t\t// { name: 'underline', onSelect() { handleOp('underline', 'toggleUnderline') }},\n\t\t\t// { name: 'strike', onSelect() { handleOp('strike', 'toggleStrike') }},\n\t\t\t{\n\t\t\t\tname: 'code',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bold', 'toggleCode')\n\t\t\t\t},\n\t\t\t},\n\t\t\tonEditLinkStart\n\t\t\t\t? {\n\t\t\t\t\t\tname: 'link',\n\t\t\t\t\t\tonSelect() {\n\t\t\t\t\t\t\tonEditLinkStart()\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: undefined, // ? is this really optional?\n\t\t\t{\n\t\t\t\tname: 'bulletList',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bulletList', 'toggleBulletList')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'highlight',\n\t\t\t\tonSelect() {\n\t\t\t\t\thandleOp('bulletList', 'toggleHighlight')\n\t\t\t\t},\n\t\t\t},\n\t\t].filter(Boolean) as {\n\t\t\tname: string\n\t\t\tattrs?: string\n\t\t\tonSelect(): void\n\t\t}[]\n\t}, [textEditor, trackEvent, onEditLinkStart])\n\n\treturn actions.map(({ name, attrs, onSelect }) => {\n\t\tconst isActive = textEditor.view ? textEditor.isActive(name, attrs) : false\n\t\treturn (\n\t\t\t<TldrawUiToolbarButton\n\t\t\t\tkey={name}\n\t\t\t\ttitle={msg(`tool.rich-text-${name}`)}\n\t\t\t\tdata-testid={`rich-text.${name}`}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tisActive={isActive} // todo: we need to update this only when the text editor \"settles\", ie not during a change of selection\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={onSelect}\n\t\t\t\trole=\"option\"\n\t\t\t\taria-pressed={isActive}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon={name} />\n\t\t\t</TldrawUiToolbarButton>\n\t\t)\n\t})\n}\n"],
5
+ "mappings": "AA+HI;AA/HJ,SAAS,YAAY,sBAAoC;AACzD,SAAS,WAAW,SAAS,gBAAgB;AAC7C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AAa/B,SAAS,8BAA8B;AAAA,EAC7C;AAAA,EACA;AACD,GAAuC;AACtC,QAAM,aAAa,YAAY;AAC/B,QAAM,MAAM,eAAe;AAC3B,QAAM,SAAS;AAGf,QAAM,CAAC,GAAG,GAAG,IAAI,SAAS,CAAC;AAC3B;AAAA,IACC,SAAS,gCAAgC;AACxC,eAAS,cAAc;AACtB,YAAI,CAAC,MAAM,IAAI,CAAC;AAAA,MACjB;AACA,iBAAW,GAAG,UAAU,WAAW;AACnC,iBAAW,GAAG,mBAAmB,WAAW;AAAA,IAC7C;AAAA,IACA,CAAC,UAAU;AAAA,EACZ;AAEA,YAAU,MAAM;AACf,aAAS,cAAc,OAAsB;AAC5C,UAAI,mBAAmB,WAAW,KAAK,KAAK,MAAM,YAAY,MAAM,QAAQ,KAAK;AAChF,cAAM,eAAe;AACrB,wBAAgB;AAAA,MACjB;AAAA,IACD;AAEA,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM;AACZ,eAAS,oBAAoB,WAAW,aAAa;AAAA,IACtD;AAAA,EACD,GAAG,CAAC,eAAe,CAAC;AAGpB,QAAM,UAAU,QAAQ,MAAM;AAC7B,aAAS,SAAS,MAAc,IAAY;AAE3C,UAAI,CAAC,WAAW,KAAM;AAEtB,iBAAW,aAAa,EAAE,WAAW,MAAa,OAAO,CAAC;AAE1D,iBAAW,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI;AAAA,IACtC;AAEA,WAAO;AAAA;AAAA,MAEN;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,QAAQ,YAAY;AAAA,QAC9B;AAAA,MACD;AAAA,MACA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,QAAQ,cAAc;AAAA,QAChC;AAAA,MACD;AAAA;AAAA;AAAA,MAGA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,QAAQ,YAAY;AAAA,QAC9B;AAAA,MACD;AAAA,MACA,kBACG;AAAA,QACA,MAAM;AAAA,QACN,WAAW;AACV,0BAAgB;AAAA,QACjB;AAAA,MACD,IACC;AAAA;AAAA,MACH;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,cAAc,kBAAkB;AAAA,QAC1C;AAAA,MACD;AAAA,MACA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AACV,mBAAS,cAAc,iBAAiB;AAAA,QACzC;AAAA,MACD;AAAA,IACD,EAAE,OAAO,OAAO;AAAA,EAKjB,GAAG,CAAC,YAAY,YAAY,eAAe,CAAC;AAE5C,SAAO,QAAQ,IAAI,CAAC,EAAE,MAAM,OAAO,SAAS,MAAM;AACjD,UAAM,WAAW,WAAW,OAAO,WAAW,SAAS,MAAM,KAAK,IAAI;AACtE,WACC;AAAA,MAAC;AAAA;AAAA,QAEA,OAAO,IAAI,kBAAkB,IAAI,EAAE;AAAA,QACnC,eAAa,aAAa,IAAI;AAAA,QAC9B,MAAK;AAAA,QACL;AAAA,QACA,eAAe;AAAA,QACf,SAAS;AAAA,QACT,MAAK;AAAA,QACL,gBAAc;AAAA,QAEd,8BAAC,sBAAmB,OAAK,MAAC,MAAM,MAAM;AAAA;AAAA,MAVjC;AAAA,IAWN;AAAA,EAEF,CAAC;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { preventDefault, useEditor } from "@tldraw/editor";
2
+ import { openWindow, preventDefault, useEditor } from "@tldraw/editor";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { useUiEvents } from "../../context/events.mjs";
5
5
  import { useTranslation } from "../../hooks/useTranslation/useTranslation.mjs";
@@ -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 {
@@ -30,7 +30,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
30
30
  };
31
31
  const handleVisitLink = () => {
32
32
  trackEvent("rich-text", { operation: "link-visit", source });
33
- window.open(linkifiedValue, "_blank", "noopener, noreferrer");
33
+ openWindow(linkifiedValue, "_blank");
34
34
  onClose();
35
35
  };
36
36
  const handleRemoveLink = () => {
@@ -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 { openWindow, 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\topenWindow(linkifiedValue, '_blank')\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,YAAY,gBAA8B,iBAAiB;AACpE,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,eAAW,gBAAgB,QAAQ;AACnC,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
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/ui/context/events.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\n\n/** @public */\nexport type TLUiEventSource =\n\t| 'menu'\n\t| 'main-menu'\n\t| 'context-menu'\n\t| 'zoom-menu'\n\t| 'document-name'\n\t| 'navigation-zone'\n\t| 'quick-actions'\n\t| 'actions-menu'\n\t| 'kbd'\n\t| 'debug-panel'\n\t| 'page-menu'\n\t| 'share-menu'\n\t| 'export-menu'\n\t| 'toolbar'\n\t| 'people-menu'\n\t| 'dialog'\n\t| 'help-menu'\n\t| 'helper-buttons'\n\t| 'style-panel'\n\t| 'rich-text-menu'\n\t| 'image-toolbar'\n\t| 'video-toolbar'\n\t| 'unknown'\n\n/** @public */\nexport interface TLUiEventMap {\n\t// Actions\n\tundo: null\n\tredo: null\n\t'change-language': { locale: string }\n\t'change-page': { direction?: 'prev' | 'next' }\n\t'select-adjacent-shape': { direction: 'prev' | 'next' | 'left' | 'right' | 'up' | 'down' }\n\t'delete-page': null\n\t'duplicate-page': null\n\t'move-page': null\n\t'new-page': null\n\t'rename-page': null\n\t'move-to-page': null\n\t'move-to-new-page': null\n\t'rename-document': null\n\t'group-shapes': null\n\t'ungroup-shapes': null\n\t'remove-frame': null\n\t'fit-frame-to-content': null\n\t'convert-to-embed': null\n\t'convert-to-bookmark': null\n\t'open-embed-link': null\n\t'toggle-auto-size': null\n\t'copy-as': { format: 'svg' | 'png' | 'json' }\n\t'export-as': { format: 'svg' | 'png' | 'json' }\n\t'export-all-as': { format: 'svg' | 'png' | 'json' }\n\t'download-original': null\n\t'edit-link': null\n\t'insert-embed': null\n\t'insert-media': null\n\t'replace-media': null\n\t'image-manipulate': null\n\t'alt-text-start': null\n\t'set-alt-text': null\n\t'align-shapes': {\n\t\toperation: 'left' | 'center-horizontal' | 'right' | 'top' | 'center-vertical' | 'bottom'\n\t}\n\t'duplicate-shapes': null\n\t'pack-shapes': null\n\t'stack-shapes': { operation: 'horizontal' | 'vertical' }\n\t'flip-shapes': { operation: 'horizontal' | 'vertical' }\n\t'distribute-shapes': { operation: 'horizontal' | 'vertical' }\n\t'stretch-shapes': { operation: 'horizontal' | 'vertical' }\n\t'reorder-shapes': {\n\t\toperation: 'toBack' | 'toFront' | 'forward' | 'backward'\n\t}\n\t'delete-shapes': null\n\t'select-all-shapes': null\n\t'select-none-shapes': null\n\t'rotate-ccw': { fine: boolean }\n\t'rotate-cw': { fine: boolean }\n\t'zoom-in': { towardsCursor: boolean }\n\t'zoom-out': { towardsCursor: boolean }\n\t'zoom-to-fit': null\n\t'zoom-to-selection': null\n\t'reset-zoom': null\n\t'zoom-into-view': null\n\t'zoom-to-content': null\n\t'open-menu': { id: string }\n\t'close-menu': { id: string }\n\t'create-new-project': null\n\t'save-project-to-file': null\n\t'open-file': null\n\t'select-tool': { id: string }\n\tprint: null\n\tcopy: null\n\tpaste: null\n\tcut: null\n\t'set-style': { id: string; value: string | number }\n\t'toggle-transparent': null\n\t'toggle-snap-mode': null\n\t'toggle-tool-lock': null\n\t'toggle-grid-mode': null\n\t'toggle-wrap-mode': null\n\t'toggle-focus-mode': null\n\t'input-mode': { value: string }\n\t'toggle-debug-mode': null\n\t'toggle-dynamic-size-mode': null\n\t'toggle-paste-at-cursor': null\n\t'toggle-lock': null\n\t'toggle-reduce-motion': null\n\t'toggle-keyboard-shortcuts': null\n\t'enhanced-a11y-mode': null\n\t'toggle-edge-scrolling': null\n\t'color-scheme': { value: string }\n\t'exit-pen-mode': null\n\t'start-following': null\n\t'stop-following': null\n\t'set-color': null\n\t'change-user-name': null\n\t'open-cursor-chat': null\n\t'zoom-tool': null\n\t'unlock-all': null\n\t'enlarge-shapes': null\n\t'shrink-shapes': null\n\t'flatten-to-image': null\n\t'a11y-repeat-shape-announce': null\n\t'open-url': { destinationUrl: string }\n\t'open-context-menu': null\n\t'adjust-shape-styles': null\n\t'copy-link': null\n\t'drag-tool': { id: string }\n\t'image-replace': null\n\t'video-replace': null\n\t'open-kbd-shortcuts': null\n\t'rich-text': {\n\t\toperation:\n\t\t\t| 'bold'\n\t\t\t| 'strike'\n\t\t\t| 'link'\n\t\t\t| 'link-edit'\n\t\t\t| 'link-visit'\n\t\t\t| 'link-remove'\n\t\t\t| 'heading'\n\t\t\t| 'bulletList'\n\t}\n\tedit: null\n}\n\n/** @public */\nexport type TLUiEventData<K> = K extends null\n\t? { source: TLUiEventSource }\n\t: { source: TLUiEventSource } & K\n\n/** @public */\nexport type TLUiEventHandler = <T extends keyof TLUiEventMap>(\n\tname: T,\n\tdata: TLUiEventData<TLUiEventMap[T]>\n) => void\n\n/** @public */\nexport type TLUiEventContextType = TLUiEventHandler\n\n/** @internal */\nconst defaultEventHandler: TLUiEventContextType = () => void null\n\n/** @internal */\nexport const EventsContext = React.createContext<TLUiEventContextType | null>(null)\n\n/** @public */\nexport interface EventsProviderProps {\n\tonEvent?: TLUiEventHandler\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiEventsProvider({ onEvent, children }: EventsProviderProps) {\n\treturn (\n\t\t<EventsContext.Provider value={onEvent ?? defaultEventHandler}>\n\t\t\t{children}\n\t\t</EventsContext.Provider>\n\t)\n}\n\n/** @public */\nexport function useUiEvents(): TLUiEventContextType {\n\tconst eventHandler = React.useContext(EventsContext)\n\treturn eventHandler ?? defaultEventHandler\n}\n"],
5
- "mappings": "AAiLE;AAjLF,YAAY,WAAW;AAmKvB,MAAM,sBAA4C,MAAM;AAGjD,MAAM,gBAAgB,MAAM,cAA2C,IAAI;AAS3E,SAAS,uBAAuB,EAAE,SAAS,SAAS,GAAwB;AAClF,SACC,oBAAC,cAAc,UAAd,EAAuB,OAAO,WAAW,qBACxC,UACF;AAEF;AAGO,SAAS,cAAoC;AACnD,QAAM,eAAe,MAAM,WAAW,aAAa;AACnD,SAAO,gBAAgB;AACxB;",
4
+ "sourcesContent": ["import * as React from 'react'\n\n/** @public */\nexport type TLUiEventSource =\n\t| 'menu'\n\t| 'main-menu'\n\t| 'context-menu'\n\t| 'zoom-menu'\n\t| 'document-name'\n\t| 'navigation-zone'\n\t| 'quick-actions'\n\t| 'actions-menu'\n\t| 'kbd'\n\t| 'debug-panel'\n\t| 'page-menu'\n\t| 'share-menu'\n\t| 'export-menu'\n\t| 'toolbar'\n\t| 'people-menu'\n\t| 'dialog'\n\t| 'help-menu'\n\t| 'helper-buttons'\n\t| 'style-panel'\n\t| 'rich-text-menu'\n\t| 'image-toolbar'\n\t| 'video-toolbar'\n\t| 'fairy-panel'\n\t| 'unknown'\n\n/** @public */\nexport interface TLUiEventMap {\n\t// Actions\n\tundo: null\n\tredo: null\n\t'change-language': { locale: string }\n\t'change-page': { direction?: 'prev' | 'next' }\n\t'select-adjacent-shape': { direction: 'prev' | 'next' | 'left' | 'right' | 'up' | 'down' }\n\t'delete-page': null\n\t'duplicate-page': null\n\t'move-page': null\n\t'new-page': null\n\t'rename-page': null\n\t'move-to-page': null\n\t'move-to-new-page': null\n\t'rename-document': null\n\t'group-shapes': null\n\t'ungroup-shapes': null\n\t'remove-frame': null\n\t'fit-frame-to-content': null\n\t'convert-to-embed': null\n\t'convert-to-bookmark': null\n\t'open-embed-link': null\n\t'toggle-auto-size': null\n\t'copy-as': { format: 'svg' | 'png' | 'json' }\n\t'export-as': { format: 'svg' | 'png' | 'json' }\n\t'export-all-as': { format: 'svg' | 'png' | 'json' }\n\t'download-original': null\n\t'edit-link': null\n\t'insert-embed': null\n\t'insert-media': null\n\t'replace-media': null\n\t'image-manipulate': null\n\t'alt-text-start': null\n\t'set-alt-text': null\n\t'align-shapes': {\n\t\toperation: 'left' | 'center-horizontal' | 'right' | 'top' | 'center-vertical' | 'bottom'\n\t}\n\t'duplicate-shapes': null\n\t'pack-shapes': null\n\t'stack-shapes': { operation: 'horizontal' | 'vertical' }\n\t'flip-shapes': { operation: 'horizontal' | 'vertical' }\n\t'distribute-shapes': { operation: 'horizontal' | 'vertical' }\n\t'stretch-shapes': { operation: 'horizontal' | 'vertical' }\n\t'reorder-shapes': {\n\t\toperation: 'toBack' | 'toFront' | 'forward' | 'backward'\n\t}\n\t'delete-shapes': null\n\t'select-all-shapes': null\n\t'select-none-shapes': null\n\t'rotate-ccw': { fine: boolean }\n\t'rotate-cw': { fine: boolean }\n\t'zoom-in': { towardsCursor: boolean }\n\t'zoom-out': { towardsCursor: boolean }\n\t'zoom-to-fit': null\n\t'zoom-to-selection': null\n\t'reset-zoom': null\n\t'zoom-into-view': null\n\t'zoom-to-content': null\n\t'open-menu': { id: string }\n\t'close-menu': { id: string }\n\t'create-new-project': null\n\t'save-project-to-file': null\n\t'open-file': null\n\t'select-tool': { id: string }\n\tprint: null\n\tcopy: null\n\tpaste: null\n\tcut: null\n\t'set-style': { id: string; value: string | number }\n\t'toggle-transparent': null\n\t'toggle-snap-mode': null\n\t'toggle-tool-lock': null\n\t'toggle-grid-mode': null\n\t'toggle-wrap-mode': null\n\t'toggle-focus-mode': null\n\t'input-mode': { value: string }\n\t'toggle-debug-mode': null\n\t'toggle-dynamic-size-mode': null\n\t'toggle-paste-at-cursor': null\n\t'toggle-lock': null\n\t'toggle-reduce-motion': null\n\t'toggle-keyboard-shortcuts': null\n\t'enhanced-a11y-mode': null\n\t'toggle-edge-scrolling': null\n\t'color-scheme': { value: string }\n\t'exit-pen-mode': null\n\t'start-following': null\n\t'stop-following': null\n\t'set-color': null\n\t'change-user-name': null\n\t'open-cursor-chat': null\n\t'zoom-tool': null\n\t'unlock-all': null\n\t'enlarge-shapes': null\n\t'shrink-shapes': null\n\t'flatten-to-image': null\n\t'a11y-repeat-shape-announce': null\n\t'open-url': { destinationUrl: string }\n\t'open-context-menu': null\n\t'adjust-shape-styles': null\n\t'copy-link': null\n\t'drag-tool': { id: string }\n\t'image-replace': null\n\t'video-replace': null\n\t'open-kbd-shortcuts': null\n\t'rich-text': {\n\t\toperation:\n\t\t\t| 'bold'\n\t\t\t| 'strike'\n\t\t\t| 'link'\n\t\t\t| 'link-edit'\n\t\t\t| 'link-visit'\n\t\t\t| 'link-remove'\n\t\t\t| 'heading'\n\t\t\t| 'bulletList'\n\t}\n\tedit: null\n}\n\n/** @public */\nexport type TLUiEventData<K> = K extends null\n\t? { source: TLUiEventSource }\n\t: { source: TLUiEventSource } & K\n\n/** @public */\nexport type TLUiEventHandler = <T extends keyof TLUiEventMap>(\n\tname: T,\n\tdata: TLUiEventData<TLUiEventMap[T]>\n) => void\n\n/** @public */\nexport type TLUiEventContextType = TLUiEventHandler\n\n/** @internal */\nconst defaultEventHandler: TLUiEventContextType = () => void null\n\n/** @internal */\nexport const EventsContext = React.createContext<TLUiEventContextType | null>(null)\n\n/** @public */\nexport interface EventsProviderProps {\n\tonEvent?: TLUiEventHandler\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiEventsProvider({ onEvent, children }: EventsProviderProps) {\n\treturn (\n\t\t<EventsContext.Provider value={onEvent ?? defaultEventHandler}>\n\t\t\t{children}\n\t\t</EventsContext.Provider>\n\t)\n}\n\n/** @public */\nexport function useUiEvents(): TLUiEventContextType {\n\tconst eventHandler = React.useContext(EventsContext)\n\treturn eventHandler ?? defaultEventHandler\n}\n"],
5
+ "mappings": "AAkLE;AAlLF,YAAY,WAAW;AAoKvB,MAAM,sBAA4C,MAAM;AAGjD,MAAM,gBAAgB,MAAM,cAA2C,IAAI;AAS3E,SAAS,uBAAuB,EAAE,SAAS,SAAS,GAAwB;AAClF,SACC,oBAAC,cAAc,UAAd,EAAuB,OAAO,WAAW,qBACxC,UACF;AAEF;AAGO,SAAS,cAAoC;AACnD,QAAM,eAAe,MAAM,WAAW,aAAa;AACnD,SAAO,gBAAgB;AACxB;",
6
6
  "names": []
7
7
  }
@@ -5,15 +5,30 @@ function getLocalFiles(options) {
5
5
  input.type = "file";
6
6
  input.accept = mimeTypes?.join(",");
7
7
  input.multiple = allowMultiple;
8
+ input.style.display = "none";
9
+ function dispose() {
10
+ input.removeEventListener("change", onchange);
11
+ input.removeEventListener("cancel", oncancel);
12
+ input.remove();
13
+ }
8
14
  async function onchange(e) {
9
15
  const fileList = e.target.files;
10
- if (!fileList || fileList.length === 0) return;
16
+ if (!fileList || fileList.length === 0) {
17
+ resolve([]);
18
+ dispose();
19
+ return;
20
+ }
11
21
  const files = Array.from(fileList);
12
22
  input.value = "";
13
23
  resolve(files);
14
- input.removeEventListener("change", onchange);
15
- input.remove();
24
+ dispose();
25
+ }
26
+ function oncancel() {
27
+ resolve([]);
28
+ dispose();
16
29
  }
30
+ document.body.appendChild(input);
31
+ input.addEventListener("cancel", oncancel);
17
32
  input.addEventListener("change", onchange);
18
33
  input?.click();
19
34
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/ui/getLocalFiles.ts"],
4
- "sourcesContent": ["export function getLocalFiles(options?: {\n\tallowMultiple?: boolean\n\tmimeTypes?: string[] | readonly string[]\n}) {\n\treturn new Promise<File[]>((resolve) => {\n\t\tconst { allowMultiple = true, mimeTypes = [] } = options || {}\n\n\t\tconst input = document.createElement('input')\n\t\tinput.type = 'file'\n\t\tinput.accept = mimeTypes?.join(',')\n\t\tinput.multiple = allowMultiple\n\n\t\tasync function onchange(e: Event) {\n\t\t\tconst fileList = (e.target as HTMLInputElement).files\n\t\t\tif (!fileList || fileList.length === 0) return\n\t\t\tconst files = Array.from(fileList)\n\t\t\tinput.value = ''\n\t\t\tresolve(files)\n\t\t\tinput.removeEventListener('change', onchange)\n\t\t\tinput.remove()\n\t\t}\n\n\t\tinput.addEventListener('change', onchange)\n\t\tinput?.click()\n\t})\n}\n"],
5
- "mappings": "AAAO,SAAS,cAAc,SAG3B;AACF,SAAO,IAAI,QAAgB,CAAC,YAAY;AACvC,UAAM,EAAE,gBAAgB,MAAM,YAAY,CAAC,EAAE,IAAI,WAAW,CAAC;AAE7D,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,OAAO;AACb,UAAM,SAAS,WAAW,KAAK,GAAG;AAClC,UAAM,WAAW;AAEjB,mBAAe,SAAS,GAAU;AACjC,YAAM,WAAY,EAAE,OAA4B;AAChD,UAAI,CAAC,YAAY,SAAS,WAAW,EAAG;AACxC,YAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,YAAM,QAAQ;AACd,cAAQ,KAAK;AACb,YAAM,oBAAoB,UAAU,QAAQ;AAC5C,YAAM,OAAO;AAAA,IACd;AAEA,UAAM,iBAAiB,UAAU,QAAQ;AACzC,WAAO,MAAM;AAAA,EACd,CAAC;AACF;",
4
+ "sourcesContent": ["export function getLocalFiles(options?: {\n\tallowMultiple?: boolean\n\tmimeTypes?: string[] | readonly string[]\n}) {\n\treturn new Promise<File[]>((resolve) => {\n\t\tconst { allowMultiple = true, mimeTypes = [] } = options || {}\n\n\t\tconst input = document.createElement('input')\n\t\tinput.type = 'file'\n\t\tinput.accept = mimeTypes?.join(',')\n\t\tinput.multiple = allowMultiple\n\t\tinput.style.display = 'none'\n\n\t\tfunction dispose() {\n\t\t\tinput.removeEventListener('change', onchange)\n\t\t\tinput.removeEventListener('cancel', oncancel)\n\t\t\tinput.remove()\n\t\t}\n\n\t\tasync function onchange(e: Event) {\n\t\t\tconst fileList = (e.target as HTMLInputElement).files\n\t\t\tif (!fileList || fileList.length === 0) {\n\t\t\t\tresolve([])\n\t\t\t\tdispose()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst files = Array.from(fileList)\n\t\t\tinput.value = ''\n\t\t\tresolve(files)\n\t\t\tdispose()\n\t\t}\n\n\t\tfunction oncancel() {\n\t\t\tresolve([])\n\t\t\tdispose()\n\t\t}\n\n\t\tdocument.body.appendChild(input)\n\t\tinput.addEventListener('cancel', oncancel)\n\t\tinput.addEventListener('change', onchange)\n\t\tinput?.click()\n\t})\n}\n"],
5
+ "mappings": "AAAO,SAAS,cAAc,SAG3B;AACF,SAAO,IAAI,QAAgB,CAAC,YAAY;AACvC,UAAM,EAAE,gBAAgB,MAAM,YAAY,CAAC,EAAE,IAAI,WAAW,CAAC;AAE7D,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,OAAO;AACb,UAAM,SAAS,WAAW,KAAK,GAAG;AAClC,UAAM,WAAW;AACjB,UAAM,MAAM,UAAU;AAEtB,aAAS,UAAU;AAClB,YAAM,oBAAoB,UAAU,QAAQ;AAC5C,YAAM,oBAAoB,UAAU,QAAQ;AAC5C,YAAM,OAAO;AAAA,IACd;AAEA,mBAAe,SAAS,GAAU;AACjC,YAAM,WAAY,EAAE,OAA4B;AAChD,UAAI,CAAC,YAAY,SAAS,WAAW,GAAG;AACvC,gBAAQ,CAAC,CAAC;AACV,gBAAQ;AACR;AAAA,MACD;AACA,YAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,YAAM,QAAQ;AACd,cAAQ,KAAK;AACb,cAAQ;AAAA,IACT;AAEA,aAAS,WAAW;AACnB,cAAQ,CAAC,CAAC;AACV,cAAQ;AAAA,IACT;AAEA,aAAS,KAAK,YAAY,KAAK;AAC/B,UAAM,iBAAiB,UAAU,QAAQ;AACzC,UAAM,iBAAiB,UAAU,QAAQ;AACzC,WAAO,MAAM;AAAA,EACd,CAAC;AACF;",
6
6
  "names": []
7
7
  }
@@ -363,12 +363,13 @@ async function handleClipboardThings(editor, things, point) {
363
363
  }
364
364
  }
365
365
  const handleNativeOrMenuCopy = async (editor) => {
366
+ const navigator2 = editor.getContainer().ownerDocument?.defaultView?.navigator ?? globalThis.navigator;
366
367
  const content = await editor.resolveAssetsInContent(
367
368
  editor.getContentFromCurrentPage(editor.getSelectedShapeIds())
368
369
  );
369
370
  if (!content) {
370
- if (navigator && navigator.clipboard) {
371
- navigator.clipboard.writeText("");
371
+ if (navigator2 && navigator2.clipboard) {
372
+ navigator2.clipboard.writeText("");
372
373
  }
373
374
  return;
374
375
  }
@@ -385,14 +386,14 @@ const handleNativeOrMenuCopy = async (editor) => {
385
386
  }
386
387
  };
387
388
  const stringifiedClipboard = JSON.stringify(clipboardData);
388
- if (typeof navigator === "undefined") {
389
+ if (typeof navigator2 === "undefined") {
389
390
  return;
390
391
  } else {
391
392
  const textItems = content.shapes.map((shape) => {
392
393
  const util = editor.getShapeUtil(shape);
393
394
  return util.getText(shape);
394
395
  }).filter(isDefined);
395
- if (navigator.clipboard?.write) {
396
+ if (navigator2.clipboard?.write) {
396
397
  const htmlBlob = new Blob([`<div data-tldraw>${stringifiedClipboard}</div>`], {
397
398
  type: "text/html"
398
399
  });
@@ -400,15 +401,15 @@ const handleNativeOrMenuCopy = async (editor) => {
400
401
  if (textContent === "") {
401
402
  textContent = " ";
402
403
  }
403
- navigator.clipboard.write([
404
+ navigator2.clipboard.write([
404
405
  new ClipboardItem({
405
406
  "text/html": htmlBlob,
406
407
  // What is this second blob used for?
407
408
  "text/plain": new Blob([textContent], { type: "text/plain" })
408
409
  })
409
410
  ]);
410
- } else if (navigator.clipboard.writeText) {
411
- navigator.clipboard.writeText(`<div data-tldraw>${stringifiedClipboard}</div>`);
411
+ } else if (navigator2.clipboard.writeText) {
412
+ navigator2.clipboard.writeText(`<div data-tldraw>${stringifiedClipboard}</div>`);
412
413
  }
413
414
  }
414
415
  };
@@ -457,6 +458,7 @@ function useMenuClipboardEvents() {
457
458
  }
458
459
  function useNativeClipboardEvents() {
459
460
  const editor = useEditor();
461
+ const ownerDocument = editor.getContainer().ownerDocument;
460
462
  const trackEvent = useUiEvents();
461
463
  const appIsFocused = useValue("editor.isFocused", () => editor.getInstanceState().isFocused, [
462
464
  editor
@@ -523,17 +525,17 @@ function useNativeClipboardEvents() {
523
525
  preventDefault(e);
524
526
  trackEvent("paste", { source: "kbd" });
525
527
  };
526
- document.addEventListener("copy", copy);
527
- document.addEventListener("cut", cut);
528
- document.addEventListener("paste", paste);
529
- document.addEventListener("pointerup", pointerUpHandler);
528
+ ownerDocument?.addEventListener("copy", copy);
529
+ ownerDocument?.addEventListener("cut", cut);
530
+ ownerDocument?.addEventListener("paste", paste);
531
+ ownerDocument?.addEventListener("pointerup", pointerUpHandler);
530
532
  return () => {
531
- document.removeEventListener("copy", copy);
532
- document.removeEventListener("cut", cut);
533
- document.removeEventListener("paste", paste);
534
- document.removeEventListener("pointerup", pointerUpHandler);
533
+ ownerDocument?.removeEventListener("copy", copy);
534
+ ownerDocument?.removeEventListener("cut", cut);
535
+ ownerDocument?.removeEventListener("paste", paste);
536
+ ownerDocument?.removeEventListener("pointerup", pointerUpHandler);
535
537
  };
536
- }, [editor, trackEvent, appIsFocused]);
538
+ }, [editor, trackEvent, appIsFocused, ownerDocument]);
537
539
  }
538
540
  export {
539
541
  isValidHttpURL,