tldraw 4.2.0-canary.f16b0170a319 → 4.2.0-canary.fb5d6ddcf79c

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 (49) hide show
  1. package/dist-cjs/index.d.ts +1 -1
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +1 -1
  4. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  5. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +14 -6
  6. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  7. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +2 -2
  8. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  9. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js +2 -1
  10. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.js.map +2 -2
  11. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +1 -1
  12. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  13. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  14. package/dist-cjs/lib/ui/getLocalFiles.js +18 -3
  15. package/dist-cjs/lib/ui/getLocalFiles.js.map +2 -2
  16. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +18 -16
  17. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +3 -3
  18. package/dist-cjs/lib/ui/version.js +3 -3
  19. package/dist-cjs/lib/ui/version.js.map +1 -1
  20. package/dist-esm/index.d.mts +1 -1
  21. package/dist-esm/index.mjs +1 -1
  22. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +2 -1
  23. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  24. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +14 -6
  25. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  26. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +2 -2
  27. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  28. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs +2 -1
  29. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.mjs.map +2 -2
  30. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +2 -2
  31. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  32. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  33. package/dist-esm/lib/ui/getLocalFiles.mjs +18 -3
  34. package/dist-esm/lib/ui/getLocalFiles.mjs.map +2 -2
  35. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +18 -16
  36. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +3 -3
  37. package/dist-esm/lib/ui/version.mjs +3 -3
  38. package/dist-esm/lib/ui/version.mjs.map +1 -1
  39. package/package.json +3 -3
  40. package/src/lib/shapes/shared/RichTextLabel.tsx +2 -1
  41. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +19 -8
  42. package/src/lib/tools/SelectTool/childStates/Idle.ts +2 -2
  43. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbarContent.tsx +4 -1
  44. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +2 -2
  45. package/src/lib/ui/context/events.tsx +1 -0
  46. package/src/lib/ui/getLocalFiles.ts +20 -3
  47. package/src/lib/ui/hooks/useClipboardEvents.ts +12 -9
  48. package/src/lib/ui/version.ts +3 -3
  49. package/src/test/customSnapping.test.tsx +185 -0
@@ -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": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;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": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;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
  }
@@ -388,12 +388,13 @@ async function handleClipboardThings(editor, things, point) {
388
388
  }
389
389
  }
390
390
  const handleNativeOrMenuCopy = async (editor) => {
391
+ const navigator2 = editor.getContainer().ownerDocument?.defaultView?.navigator ?? globalThis.navigator;
391
392
  const content = await editor.resolveAssetsInContent(
392
393
  editor.getContentFromCurrentPage(editor.getSelectedShapeIds())
393
394
  );
394
395
  if (!content) {
395
- if (navigator && navigator.clipboard) {
396
- navigator.clipboard.writeText("");
396
+ if (navigator2 && navigator2.clipboard) {
397
+ navigator2.clipboard.writeText("");
397
398
  }
398
399
  return;
399
400
  }
@@ -410,14 +411,14 @@ const handleNativeOrMenuCopy = async (editor) => {
410
411
  }
411
412
  };
412
413
  const stringifiedClipboard = JSON.stringify(clipboardData);
413
- if (typeof navigator === "undefined") {
414
+ if (typeof navigator2 === "undefined") {
414
415
  return;
415
416
  } else {
416
417
  const textItems = content.shapes.map((shape) => {
417
418
  const util = editor.getShapeUtil(shape);
418
419
  return util.getText(shape);
419
420
  }).filter(import_editor.isDefined);
420
- if (navigator.clipboard?.write) {
421
+ if (navigator2.clipboard?.write) {
421
422
  const htmlBlob = new Blob([`<div data-tldraw>${stringifiedClipboard}</div>`], {
422
423
  type: "text/html"
423
424
  });
@@ -425,15 +426,15 @@ const handleNativeOrMenuCopy = async (editor) => {
425
426
  if (textContent === "") {
426
427
  textContent = " ";
427
428
  }
428
- navigator.clipboard.write([
429
+ navigator2.clipboard.write([
429
430
  new ClipboardItem({
430
431
  "text/html": htmlBlob,
431
432
  // What is this second blob used for?
432
433
  "text/plain": new Blob([textContent], { type: "text/plain" })
433
434
  })
434
435
  ]);
435
- } else if (navigator.clipboard.writeText) {
436
- navigator.clipboard.writeText(`<div data-tldraw>${stringifiedClipboard}</div>`);
436
+ } else if (navigator2.clipboard.writeText) {
437
+ navigator2.clipboard.writeText(`<div data-tldraw>${stringifiedClipboard}</div>`);
437
438
  }
438
439
  }
439
440
  };
@@ -482,6 +483,7 @@ function useMenuClipboardEvents() {
482
483
  }
483
484
  function useNativeClipboardEvents() {
484
485
  const editor = (0, import_editor.useEditor)();
486
+ const ownerDocument = editor.getContainer().ownerDocument;
485
487
  const trackEvent = (0, import_events.useUiEvents)();
486
488
  const appIsFocused = (0, import_editor.useValue)("editor.isFocused", () => editor.getInstanceState().isFocused, [
487
489
  editor
@@ -548,16 +550,16 @@ function useNativeClipboardEvents() {
548
550
  (0, import_editor.preventDefault)(e);
549
551
  trackEvent("paste", { source: "kbd" });
550
552
  };
551
- document.addEventListener("copy", copy);
552
- document.addEventListener("cut", cut);
553
- document.addEventListener("paste", paste);
554
- document.addEventListener("pointerup", pointerUpHandler);
553
+ ownerDocument?.addEventListener("copy", copy);
554
+ ownerDocument?.addEventListener("cut", cut);
555
+ ownerDocument?.addEventListener("paste", paste);
556
+ ownerDocument?.addEventListener("pointerup", pointerUpHandler);
555
557
  return () => {
556
- document.removeEventListener("copy", copy);
557
- document.removeEventListener("cut", cut);
558
- document.removeEventListener("paste", paste);
559
- document.removeEventListener("pointerup", pointerUpHandler);
558
+ ownerDocument?.removeEventListener("copy", copy);
559
+ ownerDocument?.removeEventListener("cut", cut);
560
+ ownerDocument?.removeEventListener("paste", paste);
561
+ ownerDocument?.removeEventListener("pointerup", pointerUpHandler);
560
562
  };
561
- }, [editor, trackEvent, appIsFocused]);
563
+ }, [editor, trackEvent, appIsFocused, ownerDocument]);
562
564
  }
563
565
  //# sourceMappingURL=useClipboardEvents.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/ui/hooks/useClipboardEvents.ts"],
4
- "sourcesContent": ["import {\n\tEditor,\n\tFileHelpers,\n\tTLExternalContentSource,\n\tVec,\n\tVecLike,\n\tassert,\n\tcompact,\n\tisDefined,\n\tpreventDefault,\n\tuniq,\n\tuseEditor,\n\tuseMaybeEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport lz from 'lz-string'\nimport { useCallback, useEffect } from 'react'\nimport { TLDRAW_CUSTOM_PNG_MIME_TYPE, getCanonicalClipboardReadType } from '../../utils/clipboard'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { pasteFiles } from './clipboard/pasteFiles'\nimport { pasteUrl } from './clipboard/pasteUrl'\n\n// Expected paste mime types. The earlier in this array they appear, the higher preference we give\n// them. For example, we prefer the `web image/png+tldraw` type to plain `image/png` as it does not\n// strip some of the extra metadata we write into it.\nconst expectedPasteFileMimeTypes = [\n\tTLDRAW_CUSTOM_PNG_MIME_TYPE,\n\t'image/png',\n\t'image/jpeg',\n\t'image/webp',\n\t'image/svg+xml',\n] satisfies string[]\n\n/**\n * Strip HTML tags from a string.\n * @param html - The HTML to strip.\n * @internal\n */\nfunction stripHtml(html: string) {\n\t// See <https://github.com/developit/preact-markup/blob/4788b8d61b4e24f83688710746ee36e7464f7bbc/src/parse-markup.js#L60-L69>\n\tconst doc = document.implementation.createHTMLDocument('')\n\tdoc.documentElement.innerHTML = html.trim()\n\treturn doc.body.textContent || doc.body.innerText || ''\n}\n\n/** @public */\nexport const isValidHttpURL = (url: string) => {\n\ttry {\n\t\tconst u = new URL(url)\n\t\treturn u.protocol === 'http:' || u.protocol === 'https:'\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/** @public */\nconst getValidHttpURLList = (url: string) => {\n\tconst urls = url.split(/[\\n\\s]/)\n\tfor (const url of urls) {\n\t\ttry {\n\t\t\tconst u = new URL(url)\n\t\t\tif (!(u.protocol === 'http:' || u.protocol === 'https:')) {\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t}\n\treturn uniq(urls)\n}\n\n/** @public */\nconst isSvgText = (text: string) => {\n\treturn /^<svg/.test(text)\n}\n\nconst INPUTS = ['input', 'select', 'textarea']\n\n/**\n * Get whether to disallow clipboard events.\n *\n * @internal\n */\nfunction areShortcutsDisabled(editor: Editor) {\n\tconst { activeElement } = document\n\n\treturn (\n\t\teditor.menus.hasAnyOpenMenus() ||\n\t\t(activeElement &&\n\t\t\t((activeElement as HTMLElement).isContentEditable ||\n\t\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1))\n\t)\n}\n\n/**\n * Handle text pasted into the editor.\n * @param editor - The editor instance.\n * @param data - The text to paste.\n * @param point - The point at which to paste the text.\n * @internal\n */\nconst handleText = (\n\teditor: Editor,\n\tdata: string,\n\tpoint?: VecLike,\n\tsources?: TLExternalContentSource[]\n) => {\n\tconst validUrlList = getValidHttpURLList(data)\n\tif (validUrlList) {\n\t\tfor (const url of validUrlList) {\n\t\t\tpasteUrl(editor, url, point)\n\t\t}\n\t} else if (isValidHttpURL(data)) {\n\t\tpasteUrl(editor, data, point)\n\t} else if (isSvgText(data)) {\n\t\teditor.markHistoryStoppingPoint('paste')\n\t\teditor.putExternalContent({\n\t\t\ttype: 'svg-text',\n\t\t\ttext: data,\n\t\t\tpoint,\n\t\t\tsources,\n\t\t})\n\t} else {\n\t\teditor.markHistoryStoppingPoint('paste')\n\t\teditor.putExternalContent({\n\t\t\ttype: 'text',\n\t\t\ttext: data,\n\t\t\tpoint,\n\t\t\tsources,\n\t\t})\n\t}\n}\n\n/**\n * Something found on the clipboard, either through the event's clipboard data or the browser's clipboard API.\n * @internal\n */\ntype ClipboardThing =\n\t| {\n\t\t\ttype: 'file'\n\t\t\tsource: Promise<File | null>\n\t }\n\t| {\n\t\t\ttype: 'blob'\n\t\t\tsource: Promise<Blob | null>\n\t }\n\t| {\n\t\t\ttype: 'url'\n\t\t\tsource: Promise<string>\n\t }\n\t| {\n\t\t\ttype: 'html'\n\t\t\tsource: Promise<string>\n\t }\n\t| {\n\t\t\ttype: 'text'\n\t\t\tsource: Promise<string>\n\t }\n\t| {\n\t\t\ttype: string\n\t\t\tsource: Promise<string>\n\t }\n\n/**\n * Handle a paste using event clipboard data. This is the \"original\"\n * paste method that uses the clipboard data from the paste event.\n * https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData\n *\n * @param editor - The editor\n * @param clipboardData - The clipboard data\n * @param point - The point to paste at\n * @internal\n */\nconst handlePasteFromEventClipboardData = async (\n\teditor: Editor,\n\tclipboardData: DataTransfer,\n\tpoint?: VecLike\n) => {\n\t// Do not paste while in any editing state\n\tif (editor.getEditingShapeId() !== null) return\n\n\tif (!clipboardData) {\n\t\tthrow Error('No clipboard data')\n\t}\n\n\tconst things: ClipboardThing[] = []\n\n\tfor (const item of Object.values(clipboardData.items)) {\n\t\tswitch (item.kind) {\n\t\t\tcase 'file': {\n\t\t\t\t// files are always blobs\n\t\t\t\tthings.push({\n\t\t\t\t\ttype: 'file',\n\t\t\t\t\tsource: new Promise((r) => r(item.getAsFile())) as Promise<File | null>,\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'string': {\n\t\t\t\t// strings can be text or html\n\t\t\t\tif (item.type === 'text/html') {\n\t\t\t\t\tthings.push({\n\t\t\t\t\t\ttype: 'html',\n\t\t\t\t\t\tsource: new Promise((r) => item.getAsString(r)) as Promise<string>,\n\t\t\t\t\t})\n\t\t\t\t} else if (item.type === 'text/plain') {\n\t\t\t\t\tthings.push({\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\tsource: new Promise((r) => item.getAsString(r)) as Promise<string>,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tthings.push({ type: item.type, source: new Promise((r) => item.getAsString(r)) })\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\thandleClipboardThings(editor, things, point)\n}\n\n/**\n * Handle a paste using items retrieved from the Clipboard API.\n * https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem\n *\n * @param editor - The editor\n * @param clipboardItems - The clipboard items to handle\n * @param point - The point to paste at\n * @internal\n */\nconst handlePasteFromClipboardApi = async ({\n\teditor,\n\tclipboardItems,\n\tpoint,\n\tfallbackFiles,\n}: {\n\teditor: Editor\n\tclipboardItems: ClipboardItem[]\n\tpoint?: VecLike\n\tfallbackFiles?: File[]\n}) => {\n\t// We need to populate the array of clipboard things\n\t// based on the ClipboardItems from the Clipboard API.\n\t// This is done in a different way than when using\n\t// the clipboard data from the paste event.\n\n\tconst things: ClipboardThing[] = []\n\n\tfor (const item of clipboardItems) {\n\t\tfor (const type of expectedPasteFileMimeTypes) {\n\t\t\tif (item.types.includes(type)) {\n\t\t\t\tconst blobPromise = item\n\t\t\t\t\t.getType(type)\n\t\t\t\t\t.then((blob) => FileHelpers.rewriteMimeType(blob, getCanonicalClipboardReadType(type)))\n\t\t\t\tthings.push({\n\t\t\t\t\ttype: 'blob',\n\t\t\t\t\tsource: blobPromise,\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (item.types.includes('text/html')) {\n\t\t\tthings.push({\n\t\t\t\ttype: 'html',\n\t\t\t\tsource: (async () => {\n\t\t\t\t\tconst blob = await item.getType('text/html')\n\t\t\t\t\treturn await FileHelpers.blobToText(blob)\n\t\t\t\t})(),\n\t\t\t})\n\t\t}\n\n\t\tif (item.types.includes('text/uri-list')) {\n\t\t\tthings.push({\n\t\t\t\ttype: 'url',\n\t\t\t\tsource: (async () => {\n\t\t\t\t\tconst blob = await item.getType('text/uri-list')\n\t\t\t\t\treturn await FileHelpers.blobToText(blob)\n\t\t\t\t})(),\n\t\t\t})\n\t\t}\n\n\t\tif (item.types.includes('text/plain')) {\n\t\t\tthings.push({\n\t\t\t\ttype: 'text',\n\t\t\t\tsource: (async () => {\n\t\t\t\t\tconst blob = await item.getType('text/plain')\n\t\t\t\t\treturn await FileHelpers.blobToText(blob)\n\t\t\t\t})(),\n\t\t\t})\n\t\t}\n\t}\n\n\tif (fallbackFiles?.length && things.length === 1 && things[0].type === 'text') {\n\t\tthings.pop()\n\t\tthings.push(\n\t\t\t...fallbackFiles.map((f): ClipboardThing => ({ type: 'file', source: Promise.resolve(f) }))\n\t\t)\n\t} else if (fallbackFiles?.length && things.length === 0) {\n\t\t// Files pasted in Safari from your computer don't have types, so we need to use the fallback files directly\n\t\t// if they're available. This only works if pasted keyboard shortcuts. Pasting from the menu in Safari seems to never\n\t\t// let you access files that are copied from your computer.\n\t\tthings.push(\n\t\t\t...fallbackFiles.map((f): ClipboardThing => ({ type: 'file', source: Promise.resolve(f) }))\n\t\t)\n\t}\n\n\treturn await handleClipboardThings(editor, things, point)\n}\n\nasync function handleClipboardThings(editor: Editor, things: ClipboardThing[], point?: VecLike) {\n\t// 1. Handle files\n\t//\n\t// We need to handle files separately because if we want them to\n\t// be placed next to each other, we need to create them all at once.\n\n\tconst files = things.filter(\n\t\t(t) => (t.type === 'file' || t.type === 'blob') && t.source !== null\n\t) as Extract<ClipboardThing, { type: 'file' } | { type: 'blob' }>[]\n\n\t// Just paste the files, nothing else\n\tif (files.length) {\n\t\tif (files.length > editor.options.maxFilesAtOnce) {\n\t\t\tthrow Error('Too many files')\n\t\t}\n\t\tconst fileBlobs = compact(await Promise.all(files.map((t) => t.source)))\n\t\treturn await pasteFiles(editor, fileBlobs, point)\n\t}\n\n\t// 2. Generate clipboard results for non-file things\n\t//\n\t// Getting the source from the items is async, however they must be accessed syncronously;\n\t// we can't await them in a loop. So we'll map them to promises and await them all at once,\n\t// then make decisions based on what we find.\n\n\tconst results = await Promise.all<TLExternalContentSource>(\n\t\tthings\n\t\t\t.filter((t) => t.type !== 'file')\n\t\t\t.map(\n\t\t\t\t(t) =>\n\t\t\t\t\tnew Promise((r) => {\n\t\t\t\t\t\tconst thing = t as Exclude<ClipboardThing, { type: 'file' } | { type: 'blob' }>\n\n\t\t\t\t\t\tif (thing.type === 'file') {\n\t\t\t\t\t\t\tr({ type: 'error', data: null, reason: 'unexpected file' })\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthing.source.then((text) => {\n\t\t\t\t\t\t\t// first, see if we can find tldraw content, which is JSON inside of an html comment\n\t\t\t\t\t\t\tconst tldrawHtmlComment = text.match(/<div data-tldraw[^>]*>(.*)<\\/div>/)?.[1]\n\n\t\t\t\t\t\t\tif (tldrawHtmlComment) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t// First try parsing as plain JSON (version 2/3 formats)\n\t\t\t\t\t\t\t\t\tlet json\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tjson = JSON.parse(tldrawHtmlComment)\n\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t// Fall back to LZ decompression (legacy format)\n\t\t\t\t\t\t\t\t\t\tconst jsonComment = lz.decompressFromBase64(tldrawHtmlComment)\n\t\t\t\t\t\t\t\t\t\tif (jsonComment === null) {\n\t\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\t\tdata: null,\n\t\t\t\t\t\t\t\t\t\t\t\treason: `found tldraw data comment but could not parse`,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tjson = JSON.parse(jsonComment)\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (json.type !== 'application/tldraw') {\n\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\tdata: json,\n\t\t\t\t\t\t\t\t\t\t\treason: `found tldraw data comment but JSON was of a different type: ${json.type}`,\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Handle versioned clipboard format\n\t\t\t\t\t\t\t\t\tif (json.version === 3) {\n\t\t\t\t\t\t\t\t\t\t// Version 3: Assets are plain, decompress only other data\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tconst otherData = JSON.parse(\n\t\t\t\t\t\t\t\t\t\t\t\tlz.decompressFromBase64(json.data.otherCompressed) || '{}'\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tconst reconstructedData = {\n\t\t\t\t\t\t\t\t\t\t\t\tassets: json.data.assets || [],\n\t\t\t\t\t\t\t\t\t\t\t\t...otherData,\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tr({ type: 'tldraw', data: reconstructedData })\n\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\t\tdata: json,\n\t\t\t\t\t\t\t\t\t\t\t\treason: `failed to decompress version 2 clipboard data: ${error}`,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\treturn\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\tif (json.version === 2) {\n\t\t\t\t\t\t\t\t\t\t// Version 2: Everything is plain, this had issues with encoding... :-/\n\t\t\t\t\t\t\t\t\t\t// TODO: nix this support after some time.\n\t\t\t\t\t\t\t\t\t\tr({ type: 'tldraw', data: json.data })\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Version 1 or no version: Legacy format\n\t\t\t\t\t\t\t\t\t\tif (typeof json.data === 'string') {\n\t\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\t\tdata: json,\n\t\t\t\t\t\t\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\t\t\t\t\t\t'found tldraw json but data was a string instead of a TLClipboardModel object',\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tr({ type: 'tldraw', data: json.data })\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\tdata: tldrawHtmlComment,\n\t\t\t\t\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\t\t\t\t'found tldraw json but data was a string instead of a TLClipboardModel object',\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (thing.type === 'html') {\n\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'html' })\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (thing.type === 'url') {\n\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'url' })\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// if we have not found a tldraw comment, Otherwise, try to parse the text as JSON directly.\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst json = JSON.parse(text)\n\t\t\t\t\t\t\t\t\tif (json.type === 'excalidraw/clipboard') {\n\t\t\t\t\t\t\t\t\t\t// If the clipboard contains content copied from excalidraw, then paste that\n\t\t\t\t\t\t\t\t\t\tr({ type: 'excalidraw', data: json })\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'json' })\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t// If we could not parse the text as JSON, then it's just text\n\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'text' })\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tr({ type: 'error', data: text, reason: 'unhandled case' })\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t)\n\t)\n\n\t// 3.\n\t//\n\t// Now that we know what kind of stuff we're dealing with, we can actual create some content.\n\t// There are priorities here, so order matters: we've already handled images and files, which\n\t// take first priority; then we want to handle tldraw content, then excalidraw content, then\n\t// html content, then links, and finally text content.\n\n\t// Try to paste tldraw content\n\tfor (const result of results) {\n\t\tif (result.type === 'tldraw') {\n\t\t\teditor.markHistoryStoppingPoint('paste')\n\t\t\teditor.putExternalContent({ type: 'tldraw', content: result.data, point })\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Try to paste excalidraw content\n\tfor (const result of results) {\n\t\tif (result.type === 'excalidraw') {\n\t\t\teditor.markHistoryStoppingPoint('paste')\n\t\t\teditor.putExternalContent({ type: 'excalidraw', content: result.data, point })\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Try to paste html content\n\tfor (const result of results) {\n\t\tif (result.type === 'text' && result.subtype === 'html') {\n\t\t\t// try to find a link\n\t\t\tconst rootNode = new DOMParser().parseFromString(result.data, 'text/html')\n\t\t\tconst bodyNode = rootNode.querySelector('body')\n\n\t\t\t// Edge on Windows 11 home appears to paste a link as a single <a/> in\n\t\t\t// the HTML document. If we're pasting a single like tag we'll just\n\t\t\t// assume the user meant to paste the URL.\n\t\t\tconst isHtmlSingleLink =\n\t\t\t\tbodyNode &&\n\t\t\t\tArray.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 &&\n\t\t\t\tbodyNode.firstElementChild &&\n\t\t\t\tbodyNode.firstElementChild.tagName === 'A' &&\n\t\t\t\tbodyNode.firstElementChild.hasAttribute('href') &&\n\t\t\t\tbodyNode.firstElementChild.getAttribute('href') !== ''\n\n\t\t\tif (isHtmlSingleLink) {\n\t\t\t\tconst href = bodyNode.firstElementChild.getAttribute('href')!\n\t\t\t\thandleText(editor, href, point, results)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If the html is NOT a link, and we have NO OTHER texty content, then paste the html as text\n\t\t\tif (!results.some((r) => r.type === 'text' && r.subtype !== 'html') && result.data.trim()) {\n\t\t\t\tconst html = stripHtml(result.data) ?? ''\n\t\t\t\tif (html) {\n\t\t\t\t\thandleText(editor, stripHtml(result.data), point, results)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If the html is NOT a link, and we have other texty content, then paste the html as a text shape\n\t\t\tif (results.some((r) => r.type === 'text' && r.subtype !== 'html')) {\n\t\t\t\tconst html = stripHtml(result.data) ?? ''\n\t\t\t\tif (html) {\n\t\t\t\t\teditor.markHistoryStoppingPoint('paste')\n\t\t\t\t\teditor.putExternalContent({\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\thtml: result.data,\n\t\t\t\t\t\tpoint,\n\t\t\t\t\t\tsources: results,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Allow you to paste YouTube or Google Maps embeds, for example.\n\t\tif (result.type === 'text' && result.subtype === 'text' && result.data.startsWith('<iframe ')) {\n\t\t\t// try to find an iframe\n\t\t\tconst rootNode = new DOMParser().parseFromString(result.data, 'text/html')\n\t\t\tconst bodyNode = rootNode.querySelector('body')\n\n\t\t\tconst isSingleIframe =\n\t\t\t\tbodyNode &&\n\t\t\t\tArray.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 &&\n\t\t\t\tbodyNode.firstElementChild &&\n\t\t\t\tbodyNode.firstElementChild.tagName === 'IFRAME' &&\n\t\t\t\tbodyNode.firstElementChild.hasAttribute('src') &&\n\t\t\t\tbodyNode.firstElementChild.getAttribute('src') !== ''\n\n\t\t\tif (isSingleIframe) {\n\t\t\t\tconst src = bodyNode.firstElementChild.getAttribute('src')!\n\t\t\t\thandleText(editor, src, point, results)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t// Try to paste a link\n\tfor (const result of results) {\n\t\tif (result.type === 'text' && result.subtype === 'url') {\n\t\t\tpasteUrl(editor, result.data, point, results)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Finally, if we haven't bailed on anything yet, we can paste text content\n\tfor (const result of results) {\n\t\tif (result.type === 'text' && result.subtype === 'text' && result.data.trim()) {\n\t\t\t// The clipboard may include multiple text items, but we only want to paste the first one\n\t\t\thandleText(editor, result.data, point, results)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n/**\n * When the user copies, write the contents to local storage and to the clipboard\n *\n * @param editor - The editor instance.\n * @public\n */\nconst handleNativeOrMenuCopy = async (editor: Editor) => {\n\tconst content = await editor.resolveAssetsInContent(\n\t\teditor.getContentFromCurrentPage(editor.getSelectedShapeIds())\n\t)\n\tif (!content) {\n\t\tif (navigator && navigator.clipboard) {\n\t\t\tnavigator.clipboard.writeText('')\n\t\t}\n\t\treturn\n\t}\n\n\t// Use versioned clipboard format for better compression\n\t// Version 3: Don't compress assets, only compress other data\n\tconst { assets, ...otherData } = content\n\tconst clipboardData = {\n\t\ttype: 'application/tldraw',\n\t\tkind: 'content',\n\t\tversion: 3,\n\t\tdata: {\n\t\t\tassets: assets || [], // Plain JSON, no compression\n\t\t\totherCompressed: lz.compressToBase64(JSON.stringify(otherData)), // Only compress non-asset data\n\t\t},\n\t}\n\n\t// Don't compress the final structure - just use plain JSON\n\tconst stringifiedClipboard = JSON.stringify(clipboardData)\n\n\tif (typeof navigator === 'undefined') {\n\t\treturn\n\t} else {\n\t\t// Extract the text from the clipboard\n\t\tconst textItems = content.shapes\n\t\t\t.map((shape) => {\n\t\t\t\tconst util = editor.getShapeUtil(shape)\n\t\t\t\treturn util.getText(shape)\n\t\t\t})\n\t\t\t.filter(isDefined)\n\n\t\tif (navigator.clipboard?.write) {\n\t\t\tconst htmlBlob = new Blob([`<div data-tldraw>${stringifiedClipboard}</div>`], {\n\t\t\t\ttype: 'text/html',\n\t\t\t})\n\n\t\t\tlet textContent = textItems.join(' ')\n\n\t\t\t// This is a bug in chrome android where it won't paste content if\n\t\t\t// the text/plain content is \"\" so we need to always add an empty\n\t\t\t// space \uD83E\uDD2C\n\t\t\tif (textContent === '') {\n\t\t\t\ttextContent = ' '\n\t\t\t}\n\n\t\t\tnavigator.clipboard.write([\n\t\t\t\tnew ClipboardItem({\n\t\t\t\t\t'text/html': htmlBlob,\n\t\t\t\t\t// What is this second blob used for?\n\t\t\t\t\t'text/plain': new Blob([textContent], { type: 'text/plain' }),\n\t\t\t\t}),\n\t\t\t])\n\t\t} else if (navigator.clipboard.writeText) {\n\t\t\tnavigator.clipboard.writeText(`<div data-tldraw>${stringifiedClipboard}</div>`)\n\t\t}\n\t}\n}\n\n/** @public */\nexport function useMenuClipboardEvents() {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst copy = useCallback(\n\t\tasync function onCopy(source: TLUiEventSource) {\n\t\t\tassert(editor, 'editor is required for copy')\n\t\t\tif (editor.getSelectedShapeIds().length === 0) return\n\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\ttrackEvent('copy', { source })\n\t\t},\n\t\t[editor, trackEvent]\n\t)\n\n\tconst cut = useCallback(\n\t\tasync function onCut(source: TLUiEventSource) {\n\t\t\tif (!editor) return\n\t\t\tif (editor.getSelectedShapeIds().length === 0) return\n\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\teditor.deleteShapes(editor.getSelectedShapeIds())\n\t\t\ttrackEvent('cut', { source })\n\t\t},\n\t\t[editor, trackEvent]\n\t)\n\n\tconst paste = useCallback(\n\t\tasync function onPaste(\n\t\t\tdata: DataTransfer | ClipboardItem[],\n\t\t\tsource: TLUiEventSource,\n\t\t\tpoint?: VecLike\n\t\t) {\n\t\t\tif (!editor) return\n\t\t\t// If we're editing a shape, or we are focusing an editable input, then\n\t\t\t// we would want the user's paste interaction to go to that element or\n\t\t\t// input instead; e.g. when pasting text into a text shape's content\n\t\t\tif (editor.getEditingShapeId() !== null) return\n\n\t\t\tif (Array.isArray(data) && data[0] instanceof ClipboardItem) {\n\t\t\t\thandlePasteFromClipboardApi({ editor, clipboardItems: data, point })\n\t\t\t\ttrackEvent('paste', { source: 'menu' })\n\t\t\t} else {\n\t\t\t\t// Read it first and then recurse, kind of weird\n\t\t\t\tnavigator.clipboard.read().then((clipboardItems) => {\n\t\t\t\t\tpaste(clipboardItems, source, point)\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\t\t[editor, trackEvent]\n\t)\n\n\treturn {\n\t\tcopy,\n\t\tcut,\n\t\tpaste,\n\t}\n}\n\n/** @public */\nexport function useNativeClipboardEvents() {\n\tconst editor = useEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst appIsFocused = useValue('editor.isFocused', () => editor.getInstanceState().isFocused, [\n\t\teditor,\n\t])\n\n\tuseEffect(() => {\n\t\tif (!appIsFocused) return\n\t\tconst copy = async (e: ClipboardEvent) => {\n\t\t\tif (\n\t\t\t\teditor.getSelectedShapeIds().length === 0 ||\n\t\t\t\teditor.getEditingShapeId() !== null ||\n\t\t\t\tareShortcutsDisabled(editor)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\ttrackEvent('copy', { source: 'kbd' })\n\t\t}\n\n\t\tasync function cut(e: ClipboardEvent) {\n\t\t\tif (\n\t\t\t\teditor.getSelectedShapeIds().length === 0 ||\n\t\t\t\teditor.getEditingShapeId() !== null ||\n\t\t\t\tareShortcutsDisabled(editor)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpreventDefault(e)\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\teditor.deleteShapes(editor.getSelectedShapeIds())\n\t\t\ttrackEvent('cut', { source: 'kbd' })\n\t\t}\n\n\t\tlet disablingMiddleClickPaste = false\n\t\tconst pointerUpHandler = (e: PointerEvent) => {\n\t\t\tif (e.button === 1) {\n\t\t\t\t// middle mouse button\n\t\t\t\tdisablingMiddleClickPaste = true\n\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\tdisablingMiddleClickPaste = false\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tconst paste = (e: ClipboardEvent) => {\n\t\t\tif (disablingMiddleClickPaste) {\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If we're editing a shape, or we are focusing an editable input, then\n\t\t\t// we would want the user's paste interaction to go to that element or\n\t\t\t// input instead; e.g. when pasting text into a text shape's content\n\t\t\tif (editor.getEditingShapeId() !== null || areShortcutsDisabled(editor)) return\n\n\t\t\t// Where should the shapes go?\n\t\t\tlet point: Vec | undefined = undefined\n\t\t\tlet pasteAtCursor = false\n\n\t\t\t// | Shiftkey | Paste at cursor mode | Paste at point? |\n\t\t\t// | N \t\t| N | N \t\t\t\t |\n\t\t\t// | Y \t\t| N | Y \t\t\t\t |\n\t\t\t// | N \t\t| Y | Y \t\t\t\t |\n\t\t\t// | Y \t\t| Y | N \t\t\t\t |\n\t\t\tif (editor.inputs.shiftKey) pasteAtCursor = true\n\t\t\tif (editor.user.getIsPasteAtCursorMode()) pasteAtCursor = !pasteAtCursor\n\t\t\tif (pasteAtCursor) point = editor.inputs.currentPagePoint\n\n\t\t\tconst pasteFromEvent = () => {\n\t\t\t\tif (e.clipboardData) {\n\t\t\t\t\thandlePasteFromEventClipboardData(editor, e.clipboardData, point)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if we can read from the clipboard API, we want to try using that first. that allows\n\t\t\t// us to access most things, and doesn't strip out metadata added to tldraw's own\n\t\t\t// copy-as-png features - so copied shapes come back in at the correct size.\n\t\t\tif (navigator.clipboard?.read) {\n\t\t\t\t// We can't read files from the filesystem using the clipboard API though - they'll\n\t\t\t\t// just come in as the file names instead. So we'll use the clipboard event's files\n\t\t\t\t// as a fallback - if we only got text, but do have files, we use those instead.\n\t\t\t\tconst fallbackFiles = Array.from(e.clipboardData?.files || [])\n\t\t\t\tnavigator.clipboard.read().then(\n\t\t\t\t\t(clipboardItems) => {\n\t\t\t\t\t\tif (Array.isArray(clipboardItems) && clipboardItems[0] instanceof ClipboardItem) {\n\t\t\t\t\t\t\thandlePasteFromClipboardApi({ editor, clipboardItems, point, fallbackFiles })\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t() => {\n\t\t\t\t\t\t// if reading from the clipboard fails, try to use the event clipboard data\n\t\t\t\t\t\tpasteFromEvent()\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tpasteFromEvent()\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\ttrackEvent('paste', { source: 'kbd' })\n\t\t}\n\n\t\tdocument.addEventListener('copy', copy)\n\t\tdocument.addEventListener('cut', cut)\n\t\tdocument.addEventListener('paste', paste)\n\t\tdocument.addEventListener('pointerup', pointerUpHandler)\n\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('copy', copy)\n\t\t\tdocument.removeEventListener('cut', cut)\n\t\t\tdocument.removeEventListener('paste', paste)\n\t\t\tdocument.removeEventListener('pointerup', pointerUpHandler)\n\t\t}\n\t}, [editor, trackEvent, appIsFocused])\n}\n"],
5
- "mappings": "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",
6
- "names": ["url", "lz"]
4
+ "sourcesContent": ["import {\n\tEditor,\n\tFileHelpers,\n\tTLExternalContentSource,\n\tVec,\n\tVecLike,\n\tassert,\n\tcompact,\n\tisDefined,\n\tpreventDefault,\n\tuniq,\n\tuseEditor,\n\tuseMaybeEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport lz from 'lz-string'\nimport { useCallback, useEffect } from 'react'\nimport { TLDRAW_CUSTOM_PNG_MIME_TYPE, getCanonicalClipboardReadType } from '../../utils/clipboard'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { pasteFiles } from './clipboard/pasteFiles'\nimport { pasteUrl } from './clipboard/pasteUrl'\n\n// Expected paste mime types. The earlier in this array they appear, the higher preference we give\n// them. For example, we prefer the `web image/png+tldraw` type to plain `image/png` as it does not\n// strip some of the extra metadata we write into it.\nconst expectedPasteFileMimeTypes = [\n\tTLDRAW_CUSTOM_PNG_MIME_TYPE,\n\t'image/png',\n\t'image/jpeg',\n\t'image/webp',\n\t'image/svg+xml',\n] satisfies string[]\n\n/**\n * Strip HTML tags from a string.\n * @param html - The HTML to strip.\n * @internal\n */\nfunction stripHtml(html: string) {\n\t// See <https://github.com/developit/preact-markup/blob/4788b8d61b4e24f83688710746ee36e7464f7bbc/src/parse-markup.js#L60-L69>\n\tconst doc = document.implementation.createHTMLDocument('')\n\tdoc.documentElement.innerHTML = html.trim()\n\treturn doc.body.textContent || doc.body.innerText || ''\n}\n\n/** @public */\nexport const isValidHttpURL = (url: string) => {\n\ttry {\n\t\tconst u = new URL(url)\n\t\treturn u.protocol === 'http:' || u.protocol === 'https:'\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/** @public */\nconst getValidHttpURLList = (url: string) => {\n\tconst urls = url.split(/[\\n\\s]/)\n\tfor (const url of urls) {\n\t\ttry {\n\t\t\tconst u = new URL(url)\n\t\t\tif (!(u.protocol === 'http:' || u.protocol === 'https:')) {\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t}\n\treturn uniq(urls)\n}\n\n/** @public */\nconst isSvgText = (text: string) => {\n\treturn /^<svg/.test(text)\n}\n\nconst INPUTS = ['input', 'select', 'textarea']\n\n/**\n * Get whether to disallow clipboard events.\n *\n * @internal\n */\nfunction areShortcutsDisabled(editor: Editor) {\n\tconst { activeElement } = document\n\n\treturn (\n\t\teditor.menus.hasAnyOpenMenus() ||\n\t\t(activeElement &&\n\t\t\t((activeElement as HTMLElement).isContentEditable ||\n\t\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1))\n\t)\n}\n\n/**\n * Handle text pasted into the editor.\n * @param editor - The editor instance.\n * @param data - The text to paste.\n * @param point - The point at which to paste the text.\n * @internal\n */\nconst handleText = (\n\teditor: Editor,\n\tdata: string,\n\tpoint?: VecLike,\n\tsources?: TLExternalContentSource[]\n) => {\n\tconst validUrlList = getValidHttpURLList(data)\n\tif (validUrlList) {\n\t\tfor (const url of validUrlList) {\n\t\t\tpasteUrl(editor, url, point)\n\t\t}\n\t} else if (isValidHttpURL(data)) {\n\t\tpasteUrl(editor, data, point)\n\t} else if (isSvgText(data)) {\n\t\teditor.markHistoryStoppingPoint('paste')\n\t\teditor.putExternalContent({\n\t\t\ttype: 'svg-text',\n\t\t\ttext: data,\n\t\t\tpoint,\n\t\t\tsources,\n\t\t})\n\t} else {\n\t\teditor.markHistoryStoppingPoint('paste')\n\t\teditor.putExternalContent({\n\t\t\ttype: 'text',\n\t\t\ttext: data,\n\t\t\tpoint,\n\t\t\tsources,\n\t\t})\n\t}\n}\n\n/**\n * Something found on the clipboard, either through the event's clipboard data or the browser's clipboard API.\n * @internal\n */\ntype ClipboardThing =\n\t| {\n\t\t\ttype: 'file'\n\t\t\tsource: Promise<File | null>\n\t }\n\t| {\n\t\t\ttype: 'blob'\n\t\t\tsource: Promise<Blob | null>\n\t }\n\t| {\n\t\t\ttype: 'url'\n\t\t\tsource: Promise<string>\n\t }\n\t| {\n\t\t\ttype: 'html'\n\t\t\tsource: Promise<string>\n\t }\n\t| {\n\t\t\ttype: 'text'\n\t\t\tsource: Promise<string>\n\t }\n\t| {\n\t\t\ttype: string\n\t\t\tsource: Promise<string>\n\t }\n\n/**\n * Handle a paste using event clipboard data. This is the \"original\"\n * paste method that uses the clipboard data from the paste event.\n * https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData\n *\n * @param editor - The editor\n * @param clipboardData - The clipboard data\n * @param point - The point to paste at\n * @internal\n */\nconst handlePasteFromEventClipboardData = async (\n\teditor: Editor,\n\tclipboardData: DataTransfer,\n\tpoint?: VecLike\n) => {\n\t// Do not paste while in any editing state\n\tif (editor.getEditingShapeId() !== null) return\n\n\tif (!clipboardData) {\n\t\tthrow Error('No clipboard data')\n\t}\n\n\tconst things: ClipboardThing[] = []\n\n\tfor (const item of Object.values(clipboardData.items)) {\n\t\tswitch (item.kind) {\n\t\t\tcase 'file': {\n\t\t\t\t// files are always blobs\n\t\t\t\tthings.push({\n\t\t\t\t\ttype: 'file',\n\t\t\t\t\tsource: new Promise((r) => r(item.getAsFile())) as Promise<File | null>,\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'string': {\n\t\t\t\t// strings can be text or html\n\t\t\t\tif (item.type === 'text/html') {\n\t\t\t\t\tthings.push({\n\t\t\t\t\t\ttype: 'html',\n\t\t\t\t\t\tsource: new Promise((r) => item.getAsString(r)) as Promise<string>,\n\t\t\t\t\t})\n\t\t\t\t} else if (item.type === 'text/plain') {\n\t\t\t\t\tthings.push({\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\tsource: new Promise((r) => item.getAsString(r)) as Promise<string>,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tthings.push({ type: item.type, source: new Promise((r) => item.getAsString(r)) })\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\thandleClipboardThings(editor, things, point)\n}\n\n/**\n * Handle a paste using items retrieved from the Clipboard API.\n * https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem\n *\n * @param editor - The editor\n * @param clipboardItems - The clipboard items to handle\n * @param point - The point to paste at\n * @internal\n */\nconst handlePasteFromClipboardApi = async ({\n\teditor,\n\tclipboardItems,\n\tpoint,\n\tfallbackFiles,\n}: {\n\teditor: Editor\n\tclipboardItems: ClipboardItem[]\n\tpoint?: VecLike\n\tfallbackFiles?: File[]\n}) => {\n\t// We need to populate the array of clipboard things\n\t// based on the ClipboardItems from the Clipboard API.\n\t// This is done in a different way than when using\n\t// the clipboard data from the paste event.\n\n\tconst things: ClipboardThing[] = []\n\n\tfor (const item of clipboardItems) {\n\t\tfor (const type of expectedPasteFileMimeTypes) {\n\t\t\tif (item.types.includes(type)) {\n\t\t\t\tconst blobPromise = item\n\t\t\t\t\t.getType(type)\n\t\t\t\t\t.then((blob) => FileHelpers.rewriteMimeType(blob, getCanonicalClipboardReadType(type)))\n\t\t\t\tthings.push({\n\t\t\t\t\ttype: 'blob',\n\t\t\t\t\tsource: blobPromise,\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (item.types.includes('text/html')) {\n\t\t\tthings.push({\n\t\t\t\ttype: 'html',\n\t\t\t\tsource: (async () => {\n\t\t\t\t\tconst blob = await item.getType('text/html')\n\t\t\t\t\treturn await FileHelpers.blobToText(blob)\n\t\t\t\t})(),\n\t\t\t})\n\t\t}\n\n\t\tif (item.types.includes('text/uri-list')) {\n\t\t\tthings.push({\n\t\t\t\ttype: 'url',\n\t\t\t\tsource: (async () => {\n\t\t\t\t\tconst blob = await item.getType('text/uri-list')\n\t\t\t\t\treturn await FileHelpers.blobToText(blob)\n\t\t\t\t})(),\n\t\t\t})\n\t\t}\n\n\t\tif (item.types.includes('text/plain')) {\n\t\t\tthings.push({\n\t\t\t\ttype: 'text',\n\t\t\t\tsource: (async () => {\n\t\t\t\t\tconst blob = await item.getType('text/plain')\n\t\t\t\t\treturn await FileHelpers.blobToText(blob)\n\t\t\t\t})(),\n\t\t\t})\n\t\t}\n\t}\n\n\tif (fallbackFiles?.length && things.length === 1 && things[0].type === 'text') {\n\t\tthings.pop()\n\t\tthings.push(\n\t\t\t...fallbackFiles.map((f): ClipboardThing => ({ type: 'file', source: Promise.resolve(f) }))\n\t\t)\n\t} else if (fallbackFiles?.length && things.length === 0) {\n\t\t// Files pasted in Safari from your computer don't have types, so we need to use the fallback files directly\n\t\t// if they're available. This only works if pasted keyboard shortcuts. Pasting from the menu in Safari seems to never\n\t\t// let you access files that are copied from your computer.\n\t\tthings.push(\n\t\t\t...fallbackFiles.map((f): ClipboardThing => ({ type: 'file', source: Promise.resolve(f) }))\n\t\t)\n\t}\n\n\treturn await handleClipboardThings(editor, things, point)\n}\n\nasync function handleClipboardThings(editor: Editor, things: ClipboardThing[], point?: VecLike) {\n\t// 1. Handle files\n\t//\n\t// We need to handle files separately because if we want them to\n\t// be placed next to each other, we need to create them all at once.\n\n\tconst files = things.filter(\n\t\t(t) => (t.type === 'file' || t.type === 'blob') && t.source !== null\n\t) as Extract<ClipboardThing, { type: 'file' } | { type: 'blob' }>[]\n\n\t// Just paste the files, nothing else\n\tif (files.length) {\n\t\tif (files.length > editor.options.maxFilesAtOnce) {\n\t\t\tthrow Error('Too many files')\n\t\t}\n\t\tconst fileBlobs = compact(await Promise.all(files.map((t) => t.source)))\n\t\treturn await pasteFiles(editor, fileBlobs, point)\n\t}\n\n\t// 2. Generate clipboard results for non-file things\n\t//\n\t// Getting the source from the items is async, however they must be accessed syncronously;\n\t// we can't await them in a loop. So we'll map them to promises and await them all at once,\n\t// then make decisions based on what we find.\n\n\tconst results = await Promise.all<TLExternalContentSource>(\n\t\tthings\n\t\t\t.filter((t) => t.type !== 'file')\n\t\t\t.map(\n\t\t\t\t(t) =>\n\t\t\t\t\tnew Promise((r) => {\n\t\t\t\t\t\tconst thing = t as Exclude<ClipboardThing, { type: 'file' } | { type: 'blob' }>\n\n\t\t\t\t\t\tif (thing.type === 'file') {\n\t\t\t\t\t\t\tr({ type: 'error', data: null, reason: 'unexpected file' })\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthing.source.then((text) => {\n\t\t\t\t\t\t\t// first, see if we can find tldraw content, which is JSON inside of an html comment\n\t\t\t\t\t\t\tconst tldrawHtmlComment = text.match(/<div data-tldraw[^>]*>(.*)<\\/div>/)?.[1]\n\n\t\t\t\t\t\t\tif (tldrawHtmlComment) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t// First try parsing as plain JSON (version 2/3 formats)\n\t\t\t\t\t\t\t\t\tlet json\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tjson = JSON.parse(tldrawHtmlComment)\n\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t// Fall back to LZ decompression (legacy format)\n\t\t\t\t\t\t\t\t\t\tconst jsonComment = lz.decompressFromBase64(tldrawHtmlComment)\n\t\t\t\t\t\t\t\t\t\tif (jsonComment === null) {\n\t\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\t\tdata: null,\n\t\t\t\t\t\t\t\t\t\t\t\treason: `found tldraw data comment but could not parse`,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tjson = JSON.parse(jsonComment)\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (json.type !== 'application/tldraw') {\n\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\tdata: json,\n\t\t\t\t\t\t\t\t\t\t\treason: `found tldraw data comment but JSON was of a different type: ${json.type}`,\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Handle versioned clipboard format\n\t\t\t\t\t\t\t\t\tif (json.version === 3) {\n\t\t\t\t\t\t\t\t\t\t// Version 3: Assets are plain, decompress only other data\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tconst otherData = JSON.parse(\n\t\t\t\t\t\t\t\t\t\t\t\tlz.decompressFromBase64(json.data.otherCompressed) || '{}'\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tconst reconstructedData = {\n\t\t\t\t\t\t\t\t\t\t\t\tassets: json.data.assets || [],\n\t\t\t\t\t\t\t\t\t\t\t\t...otherData,\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tr({ type: 'tldraw', data: reconstructedData })\n\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\t\tdata: json,\n\t\t\t\t\t\t\t\t\t\t\t\treason: `failed to decompress version 2 clipboard data: ${error}`,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\treturn\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\tif (json.version === 2) {\n\t\t\t\t\t\t\t\t\t\t// Version 2: Everything is plain, this had issues with encoding... :-/\n\t\t\t\t\t\t\t\t\t\t// TODO: nix this support after some time.\n\t\t\t\t\t\t\t\t\t\tr({ type: 'tldraw', data: json.data })\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Version 1 or no version: Legacy format\n\t\t\t\t\t\t\t\t\t\tif (typeof json.data === 'string') {\n\t\t\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\t\t\tdata: json,\n\t\t\t\t\t\t\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\t\t\t\t\t\t'found tldraw json but data was a string instead of a TLClipboardModel object',\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tr({ type: 'tldraw', data: json.data })\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\tr({\n\t\t\t\t\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\t\t\t\t\tdata: tldrawHtmlComment,\n\t\t\t\t\t\t\t\t\t\treason:\n\t\t\t\t\t\t\t\t\t\t\t'found tldraw json but data was a string instead of a TLClipboardModel object',\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (thing.type === 'html') {\n\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'html' })\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (thing.type === 'url') {\n\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'url' })\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// if we have not found a tldraw comment, Otherwise, try to parse the text as JSON directly.\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst json = JSON.parse(text)\n\t\t\t\t\t\t\t\t\tif (json.type === 'excalidraw/clipboard') {\n\t\t\t\t\t\t\t\t\t\t// If the clipboard contains content copied from excalidraw, then paste that\n\t\t\t\t\t\t\t\t\t\tr({ type: 'excalidraw', data: json })\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'json' })\n\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t// If we could not parse the text as JSON, then it's just text\n\t\t\t\t\t\t\t\t\tr({ type: 'text', data: text, subtype: 'text' })\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tr({ type: 'error', data: text, reason: 'unhandled case' })\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t)\n\t)\n\n\t// 3.\n\t//\n\t// Now that we know what kind of stuff we're dealing with, we can actual create some content.\n\t// There are priorities here, so order matters: we've already handled images and files, which\n\t// take first priority; then we want to handle tldraw content, then excalidraw content, then\n\t// html content, then links, and finally text content.\n\n\t// Try to paste tldraw content\n\tfor (const result of results) {\n\t\tif (result.type === 'tldraw') {\n\t\t\teditor.markHistoryStoppingPoint('paste')\n\t\t\teditor.putExternalContent({ type: 'tldraw', content: result.data, point })\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Try to paste excalidraw content\n\tfor (const result of results) {\n\t\tif (result.type === 'excalidraw') {\n\t\t\teditor.markHistoryStoppingPoint('paste')\n\t\t\teditor.putExternalContent({ type: 'excalidraw', content: result.data, point })\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Try to paste html content\n\tfor (const result of results) {\n\t\tif (result.type === 'text' && result.subtype === 'html') {\n\t\t\t// try to find a link\n\t\t\tconst rootNode = new DOMParser().parseFromString(result.data, 'text/html')\n\t\t\tconst bodyNode = rootNode.querySelector('body')\n\n\t\t\t// Edge on Windows 11 home appears to paste a link as a single <a/> in\n\t\t\t// the HTML document. If we're pasting a single like tag we'll just\n\t\t\t// assume the user meant to paste the URL.\n\t\t\tconst isHtmlSingleLink =\n\t\t\t\tbodyNode &&\n\t\t\t\tArray.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 &&\n\t\t\t\tbodyNode.firstElementChild &&\n\t\t\t\tbodyNode.firstElementChild.tagName === 'A' &&\n\t\t\t\tbodyNode.firstElementChild.hasAttribute('href') &&\n\t\t\t\tbodyNode.firstElementChild.getAttribute('href') !== ''\n\n\t\t\tif (isHtmlSingleLink) {\n\t\t\t\tconst href = bodyNode.firstElementChild.getAttribute('href')!\n\t\t\t\thandleText(editor, href, point, results)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If the html is NOT a link, and we have NO OTHER texty content, then paste the html as text\n\t\t\tif (!results.some((r) => r.type === 'text' && r.subtype !== 'html') && result.data.trim()) {\n\t\t\t\tconst html = stripHtml(result.data) ?? ''\n\t\t\t\tif (html) {\n\t\t\t\t\thandleText(editor, stripHtml(result.data), point, results)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If the html is NOT a link, and we have other texty content, then paste the html as a text shape\n\t\t\tif (results.some((r) => r.type === 'text' && r.subtype !== 'html')) {\n\t\t\t\tconst html = stripHtml(result.data) ?? ''\n\t\t\t\tif (html) {\n\t\t\t\t\teditor.markHistoryStoppingPoint('paste')\n\t\t\t\t\teditor.putExternalContent({\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\thtml: result.data,\n\t\t\t\t\t\tpoint,\n\t\t\t\t\t\tsources: results,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Allow you to paste YouTube or Google Maps embeds, for example.\n\t\tif (result.type === 'text' && result.subtype === 'text' && result.data.startsWith('<iframe ')) {\n\t\t\t// try to find an iframe\n\t\t\tconst rootNode = new DOMParser().parseFromString(result.data, 'text/html')\n\t\t\tconst bodyNode = rootNode.querySelector('body')\n\n\t\t\tconst isSingleIframe =\n\t\t\t\tbodyNode &&\n\t\t\t\tArray.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 &&\n\t\t\t\tbodyNode.firstElementChild &&\n\t\t\t\tbodyNode.firstElementChild.tagName === 'IFRAME' &&\n\t\t\t\tbodyNode.firstElementChild.hasAttribute('src') &&\n\t\t\t\tbodyNode.firstElementChild.getAttribute('src') !== ''\n\n\t\t\tif (isSingleIframe) {\n\t\t\t\tconst src = bodyNode.firstElementChild.getAttribute('src')!\n\t\t\t\thandleText(editor, src, point, results)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t// Try to paste a link\n\tfor (const result of results) {\n\t\tif (result.type === 'text' && result.subtype === 'url') {\n\t\t\tpasteUrl(editor, result.data, point, results)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Finally, if we haven't bailed on anything yet, we can paste text content\n\tfor (const result of results) {\n\t\tif (result.type === 'text' && result.subtype === 'text' && result.data.trim()) {\n\t\t\t// The clipboard may include multiple text items, but we only want to paste the first one\n\t\t\thandleText(editor, result.data, point, results)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n/**\n * When the user copies, write the contents to local storage and to the clipboard\n *\n * @param editor - The editor instance.\n * @public\n */\nconst handleNativeOrMenuCopy = async (editor: Editor) => {\n\tconst navigator =\n\t\teditor.getContainer().ownerDocument?.defaultView?.navigator ?? globalThis.navigator\n\tconst content = await editor.resolveAssetsInContent(\n\t\teditor.getContentFromCurrentPage(editor.getSelectedShapeIds())\n\t)\n\tif (!content) {\n\t\tif (navigator && navigator.clipboard) {\n\t\t\tnavigator.clipboard.writeText('')\n\t\t}\n\t\treturn\n\t}\n\n\t// Use versioned clipboard format for better compression\n\t// Version 3: Don't compress assets, only compress other data\n\tconst { assets, ...otherData } = content\n\tconst clipboardData = {\n\t\ttype: 'application/tldraw',\n\t\tkind: 'content',\n\t\tversion: 3,\n\t\tdata: {\n\t\t\tassets: assets || [], // Plain JSON, no compression\n\t\t\totherCompressed: lz.compressToBase64(JSON.stringify(otherData)), // Only compress non-asset data\n\t\t},\n\t}\n\n\t// Don't compress the final structure - just use plain JSON\n\tconst stringifiedClipboard = JSON.stringify(clipboardData)\n\n\tif (typeof navigator === 'undefined') {\n\t\treturn\n\t} else {\n\t\t// Extract the text from the clipboard\n\t\tconst textItems = content.shapes\n\t\t\t.map((shape) => {\n\t\t\t\tconst util = editor.getShapeUtil(shape)\n\t\t\t\treturn util.getText(shape)\n\t\t\t})\n\t\t\t.filter(isDefined)\n\n\t\tif (navigator.clipboard?.write) {\n\t\t\tconst htmlBlob = new Blob([`<div data-tldraw>${stringifiedClipboard}</div>`], {\n\t\t\t\ttype: 'text/html',\n\t\t\t})\n\n\t\t\tlet textContent = textItems.join(' ')\n\n\t\t\t// This is a bug in chrome android where it won't paste content if\n\t\t\t// the text/plain content is \"\" so we need to always add an empty\n\t\t\t// space \uD83E\uDD2C\n\t\t\tif (textContent === '') {\n\t\t\t\ttextContent = ' '\n\t\t\t}\n\n\t\t\tnavigator.clipboard.write([\n\t\t\t\tnew ClipboardItem({\n\t\t\t\t\t'text/html': htmlBlob,\n\t\t\t\t\t// What is this second blob used for?\n\t\t\t\t\t'text/plain': new Blob([textContent], { type: 'text/plain' }),\n\t\t\t\t}),\n\t\t\t])\n\t\t} else if (navigator.clipboard.writeText) {\n\t\t\tnavigator.clipboard.writeText(`<div data-tldraw>${stringifiedClipboard}</div>`)\n\t\t}\n\t}\n}\n\n/** @public */\nexport function useMenuClipboardEvents() {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst copy = useCallback(\n\t\tasync function onCopy(source: TLUiEventSource) {\n\t\t\tassert(editor, 'editor is required for copy')\n\t\t\tif (editor.getSelectedShapeIds().length === 0) return\n\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\ttrackEvent('copy', { source })\n\t\t},\n\t\t[editor, trackEvent]\n\t)\n\n\tconst cut = useCallback(\n\t\tasync function onCut(source: TLUiEventSource) {\n\t\t\tif (!editor) return\n\t\t\tif (editor.getSelectedShapeIds().length === 0) return\n\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\teditor.deleteShapes(editor.getSelectedShapeIds())\n\t\t\ttrackEvent('cut', { source })\n\t\t},\n\t\t[editor, trackEvent]\n\t)\n\n\tconst paste = useCallback(\n\t\tasync function onPaste(\n\t\t\tdata: DataTransfer | ClipboardItem[],\n\t\t\tsource: TLUiEventSource,\n\t\t\tpoint?: VecLike\n\t\t) {\n\t\t\tif (!editor) return\n\t\t\t// If we're editing a shape, or we are focusing an editable input, then\n\t\t\t// we would want the user's paste interaction to go to that element or\n\t\t\t// input instead; e.g. when pasting text into a text shape's content\n\t\t\tif (editor.getEditingShapeId() !== null) return\n\n\t\t\tif (Array.isArray(data) && data[0] instanceof ClipboardItem) {\n\t\t\t\thandlePasteFromClipboardApi({ editor, clipboardItems: data, point })\n\t\t\t\ttrackEvent('paste', { source: 'menu' })\n\t\t\t} else {\n\t\t\t\t// Read it first and then recurse, kind of weird\n\t\t\t\tnavigator.clipboard.read().then((clipboardItems) => {\n\t\t\t\t\tpaste(clipboardItems, source, point)\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\t\t[editor, trackEvent]\n\t)\n\n\treturn {\n\t\tcopy,\n\t\tcut,\n\t\tpaste,\n\t}\n}\n\n/** @public */\nexport function useNativeClipboardEvents() {\n\tconst editor = useEditor()\n\tconst ownerDocument = editor.getContainer().ownerDocument\n\tconst trackEvent = useUiEvents()\n\n\tconst appIsFocused = useValue('editor.isFocused', () => editor.getInstanceState().isFocused, [\n\t\teditor,\n\t])\n\n\tuseEffect(() => {\n\t\tif (!appIsFocused) return\n\t\tconst copy = async (e: ClipboardEvent) => {\n\t\t\tif (\n\t\t\t\teditor.getSelectedShapeIds().length === 0 ||\n\t\t\t\teditor.getEditingShapeId() !== null ||\n\t\t\t\tareShortcutsDisabled(editor)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\ttrackEvent('copy', { source: 'kbd' })\n\t\t}\n\n\t\tasync function cut(e: ClipboardEvent) {\n\t\t\tif (\n\t\t\t\teditor.getSelectedShapeIds().length === 0 ||\n\t\t\t\teditor.getEditingShapeId() !== null ||\n\t\t\t\tareShortcutsDisabled(editor)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpreventDefault(e)\n\t\t\tawait handleNativeOrMenuCopy(editor)\n\t\t\teditor.deleteShapes(editor.getSelectedShapeIds())\n\t\t\ttrackEvent('cut', { source: 'kbd' })\n\t\t}\n\n\t\tlet disablingMiddleClickPaste = false\n\t\tconst pointerUpHandler = (e: PointerEvent) => {\n\t\t\tif (e.button === 1) {\n\t\t\t\t// middle mouse button\n\t\t\t\tdisablingMiddleClickPaste = true\n\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\tdisablingMiddleClickPaste = false\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tconst paste = (e: ClipboardEvent) => {\n\t\t\tif (disablingMiddleClickPaste) {\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If we're editing a shape, or we are focusing an editable input, then\n\t\t\t// we would want the user's paste interaction to go to that element or\n\t\t\t// input instead; e.g. when pasting text into a text shape's content\n\t\t\tif (editor.getEditingShapeId() !== null || areShortcutsDisabled(editor)) return\n\n\t\t\t// Where should the shapes go?\n\t\t\tlet point: Vec | undefined = undefined\n\t\t\tlet pasteAtCursor = false\n\n\t\t\t// | Shiftkey | Paste at cursor mode | Paste at point? |\n\t\t\t// | N \t\t| N | N \t\t\t\t |\n\t\t\t// | Y \t\t| N | Y \t\t\t\t |\n\t\t\t// | N \t\t| Y | Y \t\t\t\t |\n\t\t\t// | Y \t\t| Y | N \t\t\t\t |\n\t\t\tif (editor.inputs.shiftKey) pasteAtCursor = true\n\t\t\tif (editor.user.getIsPasteAtCursorMode()) pasteAtCursor = !pasteAtCursor\n\t\t\tif (pasteAtCursor) point = editor.inputs.currentPagePoint\n\n\t\t\tconst pasteFromEvent = () => {\n\t\t\t\tif (e.clipboardData) {\n\t\t\t\t\thandlePasteFromEventClipboardData(editor, e.clipboardData, point)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if we can read from the clipboard API, we want to try using that first. that allows\n\t\t\t// us to access most things, and doesn't strip out metadata added to tldraw's own\n\t\t\t// copy-as-png features - so copied shapes come back in at the correct size.\n\t\t\tif (navigator.clipboard?.read) {\n\t\t\t\t// We can't read files from the filesystem using the clipboard API though - they'll\n\t\t\t\t// just come in as the file names instead. So we'll use the clipboard event's files\n\t\t\t\t// as a fallback - if we only got text, but do have files, we use those instead.\n\t\t\t\tconst fallbackFiles = Array.from(e.clipboardData?.files || [])\n\t\t\t\tnavigator.clipboard.read().then(\n\t\t\t\t\t(clipboardItems) => {\n\t\t\t\t\t\tif (Array.isArray(clipboardItems) && clipboardItems[0] instanceof ClipboardItem) {\n\t\t\t\t\t\t\thandlePasteFromClipboardApi({ editor, clipboardItems, point, fallbackFiles })\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t() => {\n\t\t\t\t\t\t// if reading from the clipboard fails, try to use the event clipboard data\n\t\t\t\t\t\tpasteFromEvent()\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tpasteFromEvent()\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\ttrackEvent('paste', { source: 'kbd' })\n\t\t}\n\n\t\townerDocument?.addEventListener('copy', copy)\n\t\townerDocument?.addEventListener('cut', cut)\n\t\townerDocument?.addEventListener('paste', paste)\n\t\townerDocument?.addEventListener('pointerup', pointerUpHandler)\n\n\t\treturn () => {\n\t\t\townerDocument?.removeEventListener('copy', copy)\n\t\t\townerDocument?.removeEventListener('cut', cut)\n\t\t\townerDocument?.removeEventListener('paste', paste)\n\t\t\townerDocument?.removeEventListener('pointerup', pointerUpHandler)\n\t\t}\n\t}, [editor, trackEvent, appIsFocused, ownerDocument])\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["url", "lz", "navigator"]
7
7
  }
@@ -22,10 +22,10 @@ __export(version_exports, {
22
22
  version: () => version
23
23
  });
24
24
  module.exports = __toCommonJS(version_exports);
25
- const version = "4.2.0-canary.f16b0170a319";
25
+ const version = "4.2.0-canary.fb5d6ddcf79c";
26
26
  const publishDates = {
27
27
  major: "2025-09-18T14:39:22.803Z",
28
- minor: "2025-10-17T14:06:22.995Z",
29
- patch: "2025-10-17T14:06:22.995Z"
28
+ minor: "2025-10-31T12:34:53.548Z",
29
+ patch: "2025-10-31T12:34:53.548Z"
30
30
  };
31
31
  //# sourceMappingURL=version.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/ui/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.2.0-canary.f16b0170a319'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-17T14:06:22.995Z',\n\tpatch: '2025-10-17T14:06:22.995Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.2.0-canary.fb5d6ddcf79c'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-31T12:34:53.548Z',\n\tpatch: '2025-10-31T12:34:53.548Z',\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -3942,7 +3942,7 @@ export declare interface TLUiEventMap {
3942
3942
  }
3943
3943
 
3944
3944
  /** @public */
3945
- export declare type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'document-name' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'image-toolbar' | 'kbd' | 'main-menu' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'rich-text-menu' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'video-toolbar' | 'zoom-menu';
3945
+ export declare type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'document-name' | 'export-menu' | 'fairy-panel' | 'help-menu' | 'helper-buttons' | 'image-toolbar' | 'kbd' | 'main-menu' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'rich-text-menu' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'video-toolbar' | 'zoom-menu';
3946
3946
 
3947
3947
  /** @public */
3948
3948
  export declare interface TLUiHelperButtonsProps {
@@ -533,7 +533,7 @@ import {
533
533
  } from "./lib/utils/tldr/file.mjs";
534
534
  registerTldrawLibraryVersion(
535
535
  "tldraw",
536
- "4.2.0-canary.f16b0170a319",
536
+ "4.2.0-canary.fb5d6ddcf79c",
537
537
  "esm"
538
538
  );
539
539
  export {
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import {
3
3
  DefaultFontFamilies,
4
+ openWindow,
4
5
  preventDefault,
5
6
  useEditor,
6
7
  useReactor,
@@ -62,7 +63,7 @@ const RichTextLabel = React.memo(function RichTextLabel2({
62
63
  const handlePointerUp = (e2) => {
63
64
  if (e2.name !== "pointer_up" || !link) return;
64
65
  if (!isDragging.current) {
65
- window.open(link, "_blank", "noopener, noreferrer");
66
+ openWindow(link, "_blank", false);
66
67
  }
67
68
  editor.off("event", handlePointerUp);
68
69
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/shared/RichTextLabel.tsx"],
4
- "sourcesContent": ["import {\n\tBox,\n\tDefaultFontFamilies,\n\tTLDefaultFillStyle,\n\tTLDefaultFontStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLEventInfo,\n\tTLRichText,\n\tTLShapeId,\n\tpreventDefault,\n\tuseEditor,\n\tuseReactor,\n\tuseValue,\n} from '@tldraw/editor'\nimport React, { useMemo } from 'react'\nimport { renderHtmlFromRichText } from '../../utils/text/richText'\nimport { RichTextArea } from '../text/RichTextArea'\nimport { TEXT_PROPS } from './default-shape-constants'\nimport { isLegacyAlign } from './legacyProps'\nimport { useEditableRichText } from './useEditableRichText'\n\n/** @public */\nexport interface RichTextLabelProps {\n\tshapeId: TLShapeId\n\ttype: string\n\tfont: TLDefaultFontStyle\n\tfontSize: number\n\tlineHeight: number\n\tfill?: TLDefaultFillStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\trichText?: TLRichText\n\tlabelColor: string\n\tbounds?: Box\n\tisSelected: boolean\n\tonKeyDown?(e: KeyboardEvent): void\n\tclassNamePrefix?: string\n\tstyle?: React.CSSProperties\n\ttextWidth?: number\n\ttextHeight?: number\n\tpadding?: number\n\thasCustomTabBehavior?: boolean\n}\n\n/**\n * Renders a text label that can be used inside of shapes.\n * The component has the ability to be edited in place and furthermore\n * supports rich text editing.\n *\n * @public @react\n */\nexport const RichTextLabel = React.memo(function RichTextLabel({\n\tshapeId,\n\ttype,\n\trichText,\n\tlabelColor,\n\tfont,\n\tfontSize,\n\tlineHeight,\n\talign,\n\tverticalAlign,\n\twrap,\n\tisSelected,\n\tpadding = 0,\n\tonKeyDown: handleKeyDownCustom,\n\tclassNamePrefix,\n\tstyle,\n\ttextWidth,\n\ttextHeight,\n\thasCustomTabBehavior,\n}: RichTextLabelProps) {\n\tconst editor = useEditor()\n\tconst isDragging = React.useRef(false)\n\tconst { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =\n\t\tuseEditableRichText(shapeId, type, richText)\n\n\tconst html = useMemo(() => {\n\t\tif (richText) {\n\t\t\treturn renderHtmlFromRichText(editor, richText)\n\t\t}\n\t}, [editor, richText])\n\n\tconst selectToolActive = useValue(\n\t\t'isSelectToolActive',\n\t\t() => editor.getCurrentToolId() === 'select',\n\t\t[editor]\n\t)\n\n\tuseReactor(\n\t\t'isDragging',\n\t\t() => {\n\t\t\teditor.getInstanceState()\n\t\t\tisDragging.current = editor.inputs.isDragging\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst legacyAlign = isLegacyAlign(align)\n\n\tconst handlePointerDown = (e: React.MouseEvent<HTMLDivElement>) => {\n\t\tif (e.target instanceof HTMLElement && (e.target.tagName === 'A' || e.target.closest('a'))) {\n\t\t\t// This mousedown prevent default is to let dragging when over a link work.\n\t\t\tpreventDefault(e)\n\n\t\t\tif (!selectToolActive) return\n\t\t\tconst link = e.target.closest('a')?.getAttribute('href') ?? ''\n\t\t\t// We don't get the mouseup event later because we preventDefault\n\t\t\t// so we have to do it manually.\n\t\t\tconst handlePointerUp = (e: TLEventInfo) => {\n\t\t\t\tif (e.name !== 'pointer_up' || !link) return\n\n\t\t\t\tif (!isDragging.current) {\n\t\t\t\t\twindow.open(link, '_blank', 'noopener, noreferrer')\n\t\t\t\t}\n\t\t\t\teditor.off('event', handlePointerUp)\n\t\t\t}\n\t\t\teditor.on('event', handlePointerUp)\n\t\t}\n\t}\n\n\t// Should be guarded higher up so that this doesn't render... but repeated here. This should never be true.\n\tif (!isEditing && isEmpty) return null\n\n\t// TODO: probably combine tl-text and tl-arrow eventually\n\tconst cssPrefix = classNamePrefix || 'tl-text'\n\treturn (\n\t\t<div\n\t\t\tclassName={`${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`}\n\t\t\taria-hidden={!isEditing}\n\t\t\tdata-font={font}\n\t\t\tdata-align={align}\n\t\t\tdata-hastext={!isEmpty}\n\t\t\tdata-isediting={isEditing}\n\t\t\tdata-textwrap={!!wrap}\n\t\t\tdata-isselected={isSelected}\n\t\t\tstyle={{\n\t\t\t\tjustifyContent: align === 'middle' || legacyAlign ? 'center' : align,\n\t\t\t\talignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,\n\t\t\t\tpadding,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={`${cssPrefix}-label__inner tl-text-content__wrapper`}\n\t\t\t\tstyle={{\n\t\t\t\t\tfontSize,\n\t\t\t\t\tlineHeight: lineHeight.toString(),\n\t\t\t\t\tminHeight: Math.floor(fontSize * lineHeight) + 'px',\n\t\t\t\t\tminWidth: Math.ceil(textWidth || 0),\n\t\t\t\t\tcolor: labelColor,\n\t\t\t\t\twidth: textWidth ? Math.ceil(textWidth) : undefined,\n\t\t\t\t\theight: textHeight ? Math.ceil(textHeight) : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className={`${cssPrefix} tl-text tl-text-content`} dir=\"auto\">\n\t\t\t\t\t{richText && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-rich-text\"\n\t\t\t\t\t\t\tdata-is-select-tool-active={selectToolActive}\n\t\t\t\t\t\t\t// todo: see if I can abuse this\n\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: html || '' }}\n\t\t\t\t\t\t\tonPointerDown={handlePointerDown}\n\t\t\t\t\t\t\tdata-is-ready-for-editing={isReadyForEditing}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{(isReadyForEditing || isSelected) && (\n\t\t\t\t\t<RichTextArea\n\t\t\t\t\t\t// Fudge the ref type because we're using forwardRef and it's not typed correctly.\n\t\t\t\t\t\tref={rInput as any}\n\t\t\t\t\t\trichText={richText}\n\t\t\t\t\t\tisEditing={isEditing}\n\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t{...editableTextRest}\n\t\t\t\t\t\thasCustomTabBehavior={hasCustomTabBehavior}\n\t\t\t\t\t\thandleKeyDown={handleKeyDownCustom ?? editableTextRest.handleKeyDown}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n\n/** @public */\nexport interface RichTextSVGProps {\n\tbounds: Box\n\trichText: TLRichText\n\tfontSize: number\n\tfont: TLDefaultFontStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\tlabelColor: string\n\tpadding: number\n\tshowTextOutline?: boolean\n}\n\n/**\n * Renders a rich text string as SVG given bounds and text properties.\n *\n * @public @react\n */\nexport function RichTextSVG({\n\tbounds,\n\trichText,\n\tfontSize,\n\tfont,\n\talign,\n\tverticalAlign,\n\twrap,\n\tlabelColor,\n\tpadding,\n\tshowTextOutline = true,\n}: RichTextSVGProps) {\n\tconst editor = useEditor()\n\tconst html = renderHtmlFromRichText(editor, richText)\n\tconst textAlign =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('start' as const)\n\t\t\t\t: ('end' as const)\n\tconst justifyContent =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('flex-start' as const)\n\t\t\t\t: ('flex-end' as const)\n\tconst alignItems =\n\t\tverticalAlign === 'middle' ? 'center' : verticalAlign === 'start' ? 'flex-start' : 'flex-end'\n\tconst wrapperStyle = {\n\t\tdisplay: 'flex',\n\t\tfontFamily: DefaultFontFamilies[font],\n\t\theight: `100%`,\n\t\tjustifyContent,\n\t\talignItems,\n\t\tpadding: `${padding}px`,\n\t}\n\tconst style = {\n\t\tfontSize: `${fontSize}px`,\n\t\twrap: wrap ? 'wrap' : 'nowrap',\n\t\tcolor: labelColor,\n\t\tlineHeight: TEXT_PROPS.lineHeight,\n\t\ttextAlign,\n\t\twidth: '100%',\n\t\twordWrap: 'break-word' as const,\n\t\toverflowWrap: 'break-word' as const,\n\t\twhiteSpace: 'pre-wrap',\n\t\ttextShadow: showTextOutline ? 'var(--tl-text-outline)' : 'none',\n\t}\n\n\treturn (\n\t\t<foreignObject\n\t\t\tx={bounds.minX}\n\t\t\ty={bounds.minY}\n\t\t\twidth={bounds.w}\n\t\t\theight={bounds.h}\n\t\t\tclassName=\"tl-export-embed-styles tl-rich-text tl-rich-text-svg\"\n\t\t>\n\t\t\t<div style={wrapperStyle}>\n\t\t\t\t<div dangerouslySetInnerHTML={{ __html: html }} style={style} />\n\t\t\t</div>\n\t\t</foreignObject>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tBox,\n\tDefaultFontFamilies,\n\tTLDefaultFillStyle,\n\tTLDefaultFontStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLEventInfo,\n\tTLRichText,\n\tTLShapeId,\n\topenWindow,\n\tpreventDefault,\n\tuseEditor,\n\tuseReactor,\n\tuseValue,\n} from '@tldraw/editor'\nimport React, { useMemo } from 'react'\nimport { renderHtmlFromRichText } from '../../utils/text/richText'\nimport { RichTextArea } from '../text/RichTextArea'\nimport { TEXT_PROPS } from './default-shape-constants'\nimport { isLegacyAlign } from './legacyProps'\nimport { useEditableRichText } from './useEditableRichText'\n\n/** @public */\nexport interface RichTextLabelProps {\n\tshapeId: TLShapeId\n\ttype: string\n\tfont: TLDefaultFontStyle\n\tfontSize: number\n\tlineHeight: number\n\tfill?: TLDefaultFillStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\trichText?: TLRichText\n\tlabelColor: string\n\tbounds?: Box\n\tisSelected: boolean\n\tonKeyDown?(e: KeyboardEvent): void\n\tclassNamePrefix?: string\n\tstyle?: React.CSSProperties\n\ttextWidth?: number\n\ttextHeight?: number\n\tpadding?: number\n\thasCustomTabBehavior?: boolean\n}\n\n/**\n * Renders a text label that can be used inside of shapes.\n * The component has the ability to be edited in place and furthermore\n * supports rich text editing.\n *\n * @public @react\n */\nexport const RichTextLabel = React.memo(function RichTextLabel({\n\tshapeId,\n\ttype,\n\trichText,\n\tlabelColor,\n\tfont,\n\tfontSize,\n\tlineHeight,\n\talign,\n\tverticalAlign,\n\twrap,\n\tisSelected,\n\tpadding = 0,\n\tonKeyDown: handleKeyDownCustom,\n\tclassNamePrefix,\n\tstyle,\n\ttextWidth,\n\ttextHeight,\n\thasCustomTabBehavior,\n}: RichTextLabelProps) {\n\tconst editor = useEditor()\n\tconst isDragging = React.useRef(false)\n\tconst { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =\n\t\tuseEditableRichText(shapeId, type, richText)\n\n\tconst html = useMemo(() => {\n\t\tif (richText) {\n\t\t\treturn renderHtmlFromRichText(editor, richText)\n\t\t}\n\t}, [editor, richText])\n\n\tconst selectToolActive = useValue(\n\t\t'isSelectToolActive',\n\t\t() => editor.getCurrentToolId() === 'select',\n\t\t[editor]\n\t)\n\n\tuseReactor(\n\t\t'isDragging',\n\t\t() => {\n\t\t\teditor.getInstanceState()\n\t\t\tisDragging.current = editor.inputs.isDragging\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst legacyAlign = isLegacyAlign(align)\n\n\tconst handlePointerDown = (e: React.MouseEvent<HTMLDivElement>) => {\n\t\tif (e.target instanceof HTMLElement && (e.target.tagName === 'A' || e.target.closest('a'))) {\n\t\t\t// This mousedown prevent default is to let dragging when over a link work.\n\t\t\tpreventDefault(e)\n\n\t\t\tif (!selectToolActive) return\n\t\t\tconst link = e.target.closest('a')?.getAttribute('href') ?? ''\n\t\t\t// We don't get the mouseup event later because we preventDefault\n\t\t\t// so we have to do it manually.\n\t\t\tconst handlePointerUp = (e: TLEventInfo) => {\n\t\t\t\tif (e.name !== 'pointer_up' || !link) return\n\n\t\t\t\tif (!isDragging.current) {\n\t\t\t\t\topenWindow(link, '_blank', false)\n\t\t\t\t}\n\t\t\t\teditor.off('event', handlePointerUp)\n\t\t\t}\n\t\t\teditor.on('event', handlePointerUp)\n\t\t}\n\t}\n\n\t// Should be guarded higher up so that this doesn't render... but repeated here. This should never be true.\n\tif (!isEditing && isEmpty) return null\n\n\t// TODO: probably combine tl-text and tl-arrow eventually\n\tconst cssPrefix = classNamePrefix || 'tl-text'\n\treturn (\n\t\t<div\n\t\t\tclassName={`${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`}\n\t\t\taria-hidden={!isEditing}\n\t\t\tdata-font={font}\n\t\t\tdata-align={align}\n\t\t\tdata-hastext={!isEmpty}\n\t\t\tdata-isediting={isEditing}\n\t\t\tdata-textwrap={!!wrap}\n\t\t\tdata-isselected={isSelected}\n\t\t\tstyle={{\n\t\t\t\tjustifyContent: align === 'middle' || legacyAlign ? 'center' : align,\n\t\t\t\talignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,\n\t\t\t\tpadding,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={`${cssPrefix}-label__inner tl-text-content__wrapper`}\n\t\t\t\tstyle={{\n\t\t\t\t\tfontSize,\n\t\t\t\t\tlineHeight: lineHeight.toString(),\n\t\t\t\t\tminHeight: Math.floor(fontSize * lineHeight) + 'px',\n\t\t\t\t\tminWidth: Math.ceil(textWidth || 0),\n\t\t\t\t\tcolor: labelColor,\n\t\t\t\t\twidth: textWidth ? Math.ceil(textWidth) : undefined,\n\t\t\t\t\theight: textHeight ? Math.ceil(textHeight) : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className={`${cssPrefix} tl-text tl-text-content`} dir=\"auto\">\n\t\t\t\t\t{richText && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-rich-text\"\n\t\t\t\t\t\t\tdata-is-select-tool-active={selectToolActive}\n\t\t\t\t\t\t\t// todo: see if I can abuse this\n\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: html || '' }}\n\t\t\t\t\t\t\tonPointerDown={handlePointerDown}\n\t\t\t\t\t\t\tdata-is-ready-for-editing={isReadyForEditing}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{(isReadyForEditing || isSelected) && (\n\t\t\t\t\t<RichTextArea\n\t\t\t\t\t\t// Fudge the ref type because we're using forwardRef and it's not typed correctly.\n\t\t\t\t\t\tref={rInput as any}\n\t\t\t\t\t\trichText={richText}\n\t\t\t\t\t\tisEditing={isEditing}\n\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t{...editableTextRest}\n\t\t\t\t\t\thasCustomTabBehavior={hasCustomTabBehavior}\n\t\t\t\t\t\thandleKeyDown={handleKeyDownCustom ?? editableTextRest.handleKeyDown}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n\n/** @public */\nexport interface RichTextSVGProps {\n\tbounds: Box\n\trichText: TLRichText\n\tfontSize: number\n\tfont: TLDefaultFontStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\tlabelColor: string\n\tpadding: number\n\tshowTextOutline?: boolean\n}\n\n/**\n * Renders a rich text string as SVG given bounds and text properties.\n *\n * @public @react\n */\nexport function RichTextSVG({\n\tbounds,\n\trichText,\n\tfontSize,\n\tfont,\n\talign,\n\tverticalAlign,\n\twrap,\n\tlabelColor,\n\tpadding,\n\tshowTextOutline = true,\n}: RichTextSVGProps) {\n\tconst editor = useEditor()\n\tconst html = renderHtmlFromRichText(editor, richText)\n\tconst textAlign =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('start' as const)\n\t\t\t\t: ('end' as const)\n\tconst justifyContent =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('flex-start' as const)\n\t\t\t\t: ('flex-end' as const)\n\tconst alignItems =\n\t\tverticalAlign === 'middle' ? 'center' : verticalAlign === 'start' ? 'flex-start' : 'flex-end'\n\tconst wrapperStyle = {\n\t\tdisplay: 'flex',\n\t\tfontFamily: DefaultFontFamilies[font],\n\t\theight: `100%`,\n\t\tjustifyContent,\n\t\talignItems,\n\t\tpadding: `${padding}px`,\n\t}\n\tconst style = {\n\t\tfontSize: `${fontSize}px`,\n\t\twrap: wrap ? 'wrap' : 'nowrap',\n\t\tcolor: labelColor,\n\t\tlineHeight: TEXT_PROPS.lineHeight,\n\t\ttextAlign,\n\t\twidth: '100%',\n\t\twordWrap: 'break-word' as const,\n\t\toverflowWrap: 'break-word' as const,\n\t\twhiteSpace: 'pre-wrap',\n\t\ttextShadow: showTextOutline ? 'var(--tl-text-outline)' : 'none',\n\t}\n\n\treturn (\n\t\t<foreignObject\n\t\t\tx={bounds.minX}\n\t\t\ty={bounds.minY}\n\t\t\twidth={bounds.w}\n\t\t\theight={bounds.h}\n\t\t\tclassName=\"tl-export-embed-styles tl-rich-text tl-rich-text-svg\"\n\t\t>\n\t\t\t<div style={wrapperStyle}>\n\t\t\t\t<div dangerouslySetInnerHTML={{ __html: html }} style={style} />\n\t\t\t</div>\n\t\t</foreignObject>\n\t)\n}\n"],
5
+ "mappings": "AAiJG,SAcG,KAdH;AAjJH;AAAA,EAEC;AAAA,EAQA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,OAAO,SAAS,eAAe;AAC/B,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AAiC7B,MAAM,gBAAgB,MAAM,KAAK,SAASA,eAAc;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAuB;AACtB,QAAM,SAAS,UAAU;AACzB,QAAM,aAAa,MAAM,OAAO,KAAK;AACrC,QAAM,EAAE,QAAQ,SAAS,WAAW,mBAAmB,GAAG,iBAAiB,IAC1E,oBAAoB,SAAS,MAAM,QAAQ;AAE5C,QAAM,OAAO,QAAQ,MAAM;AAC1B,QAAI,UAAU;AACb,aAAO,uBAAuB,QAAQ,QAAQ;AAAA,IAC/C;AAAA,EACD,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAErB,QAAM,mBAAmB;AAAA,IACxB;AAAA,IACA,MAAM,OAAO,iBAAiB,MAAM;AAAA,IACpC,CAAC,MAAM;AAAA,EACR;AAEA;AAAA,IACC;AAAA,IACA,MAAM;AACL,aAAO,iBAAiB;AACxB,iBAAW,UAAU,OAAO,OAAO;AAAA,IACpC;AAAA,IACA,CAAC,MAAM;AAAA,EACR;AAEA,QAAM,cAAc,cAAc,KAAK;AAEvC,QAAM,oBAAoB,CAAC,MAAwC;AAClE,QAAI,EAAE,kBAAkB,gBAAgB,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,QAAQ,GAAG,IAAI;AAE3F,qBAAe,CAAC;AAEhB,UAAI,CAAC,iBAAkB;AACvB,YAAM,OAAO,EAAE,OAAO,QAAQ,GAAG,GAAG,aAAa,MAAM,KAAK;AAG5D,YAAM,kBAAkB,CAACC,OAAmB;AAC3C,YAAIA,GAAE,SAAS,gBAAgB,CAAC,KAAM;AAEtC,YAAI,CAAC,WAAW,SAAS;AACxB,qBAAW,MAAM,UAAU,KAAK;AAAA,QACjC;AACA,eAAO,IAAI,SAAS,eAAe;AAAA,MACpC;AACA,aAAO,GAAG,SAAS,eAAe;AAAA,IACnC;AAAA,EACD;AAGA,MAAI,CAAC,aAAa,QAAS,QAAO;AAGlC,QAAM,YAAY,mBAAmB;AACrC,SACC;AAAA,IAAC;AAAA;AAAA,MACA,WAAW,GAAG,SAAS;AAAA,MACvB,eAAa,CAAC;AAAA,MACd,aAAW;AAAA,MACX,cAAY;AAAA,MACZ,gBAAc,CAAC;AAAA,MACf,kBAAgB;AAAA,MAChB,iBAAe,CAAC,CAAC;AAAA,MACjB,mBAAiB;AAAA,MACjB,OAAO;AAAA,QACN,gBAAgB,UAAU,YAAY,cAAc,WAAW;AAAA,QAC/D,YAAY,kBAAkB,WAAW,WAAW;AAAA,QACpD;AAAA,QACA,GAAG;AAAA,MACJ;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACA,WAAW,GAAG,SAAS;AAAA,UACvB,OAAO;AAAA,YACN;AAAA,YACA,YAAY,WAAW,SAAS;AAAA,YAChC,WAAW,KAAK,MAAM,WAAW,UAAU,IAAI;AAAA,YAC/C,UAAU,KAAK,KAAK,aAAa,CAAC;AAAA,YAClC,OAAO;AAAA,YACP,OAAO,YAAY,KAAK,KAAK,SAAS,IAAI;AAAA,YAC1C,QAAQ,aAAa,KAAK,KAAK,UAAU,IAAI;AAAA,UAC9C;AAAA,UAEA;AAAA,gCAAC,SAAI,WAAW,GAAG,SAAS,4BAA4B,KAAI,QAC1D,sBACA;AAAA,cAAC;AAAA;AAAA,gBACA,WAAU;AAAA,gBACV,8BAA4B;AAAA,gBAE5B,yBAAyB,EAAE,QAAQ,QAAQ,GAAG;AAAA,gBAC9C,eAAe;AAAA,gBACf,6BAA2B;AAAA;AAAA,YAC5B,GAEF;AAAA,aACE,qBAAqB,eACtB;AAAA,cAAC;AAAA;AAAA,gBAEA,KAAK;AAAA,gBACL;AAAA,gBACA;AAAA,gBACA;AAAA,gBACC,GAAG;AAAA,gBACJ;AAAA,gBACA,eAAe,uBAAuB,iBAAiB;AAAA;AAAA,YACxD;AAAA;AAAA;AAAA,MAEF;AAAA;AAAA,EACD;AAEF,CAAC;AAqBM,SAAS,YAAY;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACnB,GAAqB;AACpB,QAAM,SAAS,UAAU;AACzB,QAAM,OAAO,uBAAuB,QAAQ,QAAQ;AACpD,QAAM,YACL,UAAU,WACN,WACD,UAAU,UACR,UACA;AACN,QAAM,iBACL,UAAU,WACN,WACD,UAAU,UACR,eACA;AACN,QAAM,aACL,kBAAkB,WAAW,WAAW,kBAAkB,UAAU,eAAe;AACpF,QAAM,eAAe;AAAA,IACpB,SAAS;AAAA,IACT,YAAY,oBAAoB,IAAI;AAAA,IACpC,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,SAAS,GAAG,OAAO;AAAA,EACpB;AACA,QAAM,QAAQ;AAAA,IACb,UAAU,GAAG,QAAQ;AAAA,IACrB,MAAM,OAAO,SAAS;AAAA,IACtB,OAAO;AAAA,IACP,YAAY,WAAW;AAAA,IACvB;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,IACV,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,YAAY,kBAAkB,2BAA2B;AAAA,EAC1D;AAEA,SACC;AAAA,IAAC;AAAA;AAAA,MACA,GAAG,OAAO;AAAA,MACV,GAAG,OAAO;AAAA,MACV,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,WAAU;AAAA,MAEV,8BAAC,SAAI,OAAO,cACX,8BAAC,SAAI,yBAAyB,EAAE,QAAQ,KAAK,GAAG,OAAc,GAC/D;AAAA;AAAA,EACD;AAEF;",
6
6
  "names": ["RichTextLabel", "e"]
7
7
  }
@@ -52,21 +52,29 @@ class DraggingHandle extends StateNode {
52
52
  const handles = this.editor.getShapeHandles(shape).sort(sortByIndex);
53
53
  const index = handles.findIndex((h) => h.id === info.handle.id);
54
54
  this.initialAdjacentHandle = null;
55
- for (let i = index + 1; i < handles.length; i++) {
56
- const handle2 = handles[i];
57
- if (handle2.type === "vertex" && handle2.id !== "middle" && handle2.id !== info.handle.id) {
58
- this.initialAdjacentHandle = handle2;
59
- break;
55
+ if (info.handle.snapReferenceHandleId) {
56
+ const customHandle = handles.find((h) => h.id === info.handle.snapReferenceHandleId);
57
+ if (customHandle) {
58
+ this.initialAdjacentHandle = customHandle;
60
59
  }
61
60
  }
62
61
  if (!this.initialAdjacentHandle) {
63
- for (let i = handles.length - 1; i >= 0; i--) {
62
+ for (let i = index + 1; i < handles.length; i++) {
64
63
  const handle2 = handles[i];
65
64
  if (handle2.type === "vertex" && handle2.id !== "middle" && handle2.id !== info.handle.id) {
66
65
  this.initialAdjacentHandle = handle2;
67
66
  break;
68
67
  }
69
68
  }
69
+ if (!this.initialAdjacentHandle) {
70
+ for (let i = handles.length - 1; i >= 0; i--) {
71
+ const handle2 = handles[i];
72
+ if (handle2.type === "vertex" && handle2.id !== "middle" && handle2.id !== info.handle.id) {
73
+ this.initialAdjacentHandle = handle2;
74
+ break;
75
+ }
76
+ }
77
+ }
70
78
  }
71
79
  if (this.editor.isShapeOfType(shape, "arrow")) {
72
80
  const initialBinding = getArrowBindings(this.editor, shape)[info.handle.id];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/tools/SelectTool/childStates/DraggingHandle.tsx"],
4
- "sourcesContent": ["import {\n\tMat,\n\tStateNode,\n\tTLArrowShape,\n\tTLHandle,\n\tTLLineShape,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\tTLShapePartial,\n\tVec,\n\tkickoutOccludedShapes,\n\tsnapAngle,\n\tsortByIndex,\n\tstructuredClone,\n\twarnOnce,\n} from '@tldraw/editor'\nimport { ArrowShapeUtil } from '../../../shapes/arrow/ArrowShapeUtil'\nimport { clearArrowTargetState } from '../../../shapes/arrow/arrowTargetState'\nimport { getArrowBindings } from '../../../shapes/arrow/shared'\n\nexport type DraggingHandleInfo = TLPointerEventInfo & {\n\tshape: TLArrowShape | TLLineShape\n\ttarget: 'handle'\n\tonInteractionEnd?: string | (() => void)\n\tisCreating?: boolean\n\tcreatingMarkId?: string\n}\n\nexport class DraggingHandle extends StateNode {\n\tstatic override id = 'dragging_handle'\n\n\tshapeId!: TLShapeId\n\tinitialHandle!: TLHandle\n\tinitialAdjacentHandle!: TLHandle | null\n\tinitialPagePoint!: Vec\n\n\tmarkId!: string\n\tinitialPageTransform!: Mat\n\tinitialPageRotation!: number\n\n\tinfo!: DraggingHandleInfo\n\n\tisPrecise = false\n\tisPreciseId: TLShapeId | null = null\n\tpointingId: TLShapeId | null = null\n\n\toverride onEnter(info: DraggingHandleInfo) {\n\t\tconst { shape, isCreating, creatingMarkId, handle } = info\n\t\tthis.info = info\n\t\tif (typeof info.onInteractionEnd === 'string') {\n\t\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\t\t}\n\t\tthis.shapeId = shape.id\n\t\tthis.markId = ''\n\n\t\tif (isCreating) {\n\t\t\tif (creatingMarkId) {\n\t\t\t\tthis.markId = creatingMarkId\n\t\t\t} else {\n\t\t\t\t// handle legacy implicit `creating:{shapeId}` marks\n\t\t\t\tconst markId = this.editor.getMarkIdMatching(\n\t\t\t\t\t`creating:${this.editor.getOnlySelectedShapeId()}`\n\t\t\t\t)\n\t\t\t\tif (markId) {\n\t\t\t\t\tthis.markId = markId\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.markId = this.editor.markHistoryStoppingPoint('dragging handle')\n\t\t}\n\n\t\tthis.initialHandle = structuredClone(handle)\n\n\t\tthis.initialPageTransform = this.editor.getShapePageTransform(shape)!\n\t\tthis.initialPageRotation = this.initialPageTransform.rotation()\n\t\tthis.initialPagePoint = this.editor.inputs.originPagePoint.clone()\n\n\t\tthis.editor.setCursor({ type: isCreating ? 'cross' : 'grabbing', rotation: 0 })\n\n\t\tconst handles = this.editor.getShapeHandles(shape)!.sort(sortByIndex)\n\t\tconst index = handles.findIndex((h) => h.id === info.handle.id)\n\n\t\t// Find the adjacent handle\n\t\tthis.initialAdjacentHandle = null\n\n\t\t// Start from the handle and work forward\n\t\tfor (let i = index + 1; i < handles.length; i++) {\n\t\t\tconst handle = handles[i]\n\t\t\tif (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {\n\t\t\t\tthis.initialAdjacentHandle = handle\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// If still no handle, start from the end and work backward\n\t\tif (!this.initialAdjacentHandle) {\n\t\t\tfor (let i = handles.length - 1; i >= 0; i--) {\n\t\t\t\tconst handle = handles[i]\n\t\t\t\tif (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {\n\t\t\t\t\tthis.initialAdjacentHandle = handle\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// <!-- Only relevant to arrows\n\t\tif (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {\n\t\t\tconst initialBinding = getArrowBindings(this.editor, shape)[info.handle.id as 'start' | 'end']\n\n\t\t\tthis.isPrecise = false\n\n\t\t\tif (initialBinding) {\n\t\t\t\tthis.isPrecise = initialBinding.props.isPrecise\n\t\t\t\tif (this.isPrecise) {\n\t\t\t\t\tthis.isPreciseId = initialBinding.toId\n\t\t\t\t} else {\n\t\t\t\t\tthis.resetExactTimeout()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// -->\n\n\t\t// Call onHandleDragStart callback\n\t\tconst handleDragInfo = {\n\t\t\thandle: this.initialHandle,\n\t\t\tisPrecise: this.isPrecise,\n\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\tinitial: shape,\n\t\t}\n\t\tconst util = this.editor.getShapeUtil(shape)\n\t\tconst startChanges = util.onHandleDragStart?.(shape, handleDragInfo)\n\t\tif (startChanges) {\n\t\t\tthis.editor.updateShapes([{ ...startChanges, id: shape.id, type: shape.type }])\n\t\t}\n\n\t\tthis.update()\n\n\t\tthis.editor.select(this.shapeId)\n\t}\n\n\t// Only relevant to arrows\n\tprivate exactTimeout = -1\n\n\t// Only relevant to arrows\n\tprivate resetExactTimeout() {\n\t\tconst arrowUtil = this.editor.getShapeUtil<ArrowShapeUtil>('arrow')\n\t\tconst timeoutValue = arrowUtil.options.pointingPreciseTimeout\n\n\t\tif (this.exactTimeout !== -1) {\n\t\t\tthis.clearExactTimeout()\n\t\t}\n\n\t\tthis.exactTimeout = this.editor.timers.setTimeout(() => {\n\t\t\tif (this.getIsActive() && !this.isPrecise) {\n\t\t\t\tthis.isPrecise = true\n\t\t\t\tthis.isPreciseId = this.pointingId\n\t\t\t\tthis.update()\n\t\t\t}\n\t\t\tthis.exactTimeout = -1\n\t\t}, timeoutValue)\n\t}\n\n\t// Only relevant to arrows\n\tprivate clearExactTimeout() {\n\t\tif (this.exactTimeout !== -1) {\n\t\t\tclearTimeout(this.exactTimeout)\n\t\t\tthis.exactTimeout = -1\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyUp() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.update()\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onExit() {\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\tclearArrowTargetState(this.editor)\n\t\tthis.editor.snaps.clearIndicators()\n\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t}\n\n\tprivate complete() {\n\t\tthis.editor.snaps.clearIndicators()\n\t\tkickoutOccludedShapes(this.editor, [this.shapeId])\n\n\t\t// Call onHandleDragEnd callback before state transitions\n\t\tconst shape = this.editor.getShape(this.shapeId)\n\t\tif (shape) {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst handleDragInfo = {\n\t\t\t\thandle: this.initialHandle,\n\t\t\t\tisPrecise: this.isPrecise,\n\t\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\t\tinitial: this.info.shape,\n\t\t\t}\n\t\t\tconst endChanges = util.onHandleDragEnd?.(shape, handleDragInfo)\n\t\t\tif (endChanges) {\n\t\t\t\tthis.editor.updateShapes([{ ...endChanges, id: shape.id, type: shape.type }])\n\t\t\t}\n\t\t}\n\n\t\tconst { onInteractionEnd } = this.info\n\t\tif (onInteractionEnd) {\n\t\t\tif (typeof onInteractionEnd === 'string') {\n\t\t\t\tif (this.editor.getInstanceState().isToolLocked && onInteractionEnd) {\n\t\t\t\t\t// Return to the tool that was active before this one but only if tool lock is turned on!\n\t\t\t\t\tthis.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tonInteractionEnd?.()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t}\n\n\tprivate cancel() {\n\t\t// Call onHandleDragCancel callback before bailing to mark\n\t\tconst shape = this.editor.getShape(this.shapeId)\n\t\tif (shape) {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst handleDragInfo = {\n\t\t\t\thandle: this.initialHandle,\n\t\t\t\tisPrecise: this.isPrecise,\n\t\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\t\tinitial: this.info.shape,\n\t\t\t}\n\t\t\tutil.onHandleDragCancel?.(shape, handleDragInfo)\n\t\t}\n\n\t\tthis.editor.bailToMark(this.markId)\n\t\tthis.editor.snaps.clearIndicators()\n\n\t\tconst { onInteractionEnd } = this.info\n\t\tif (onInteractionEnd) {\n\t\t\tif (typeof onInteractionEnd === 'string') {\n\t\t\t\t// Return to the tool that was active before this one, whether tool lock is turned on or not!\n\t\t\t\tthis.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })\n\t\t\t} else {\n\t\t\t\tonInteractionEnd?.()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t}\n\n\tprivate update() {\n\t\tconst { editor, shapeId, initialPagePoint } = this\n\t\tconst { initialHandle, initialPageRotation, initialAdjacentHandle } = this\n\t\tconst isSnapMode = this.editor.user.getIsSnapMode()\n\t\tconst {\n\t\t\tsnaps,\n\t\t\tinputs: { currentPagePoint, shiftKey, ctrlKey, altKey, pointerVelocity },\n\t\t} = editor\n\n\t\tconst initial = this.info.shape\n\n\t\tconst shape = editor.getShape(shapeId)\n\t\tif (!shape) return\n\t\tconst util = editor.getShapeUtil(shape)\n\n\t\tconst initialBinding = editor.isShapeOfType<TLArrowShape>(shape, 'arrow')\n\t\t\t? getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']\n\t\t\t: undefined\n\n\t\tlet point = currentPagePoint\n\t\t\t.clone()\n\t\t\t.sub(initialPagePoint)\n\t\t\t.rot(-initialPageRotation)\n\t\t\t.add(initialHandle)\n\n\t\tif (shiftKey && initialAdjacentHandle && initialHandle.id !== 'middle') {\n\t\t\tconst angle = Vec.Angle(initialAdjacentHandle, point)\n\t\t\tconst snappedAngle = snapAngle(angle, 24)\n\t\t\tconst angleDifference = snappedAngle - angle\n\t\t\tpoint = Vec.RotWith(point, initialAdjacentHandle, angleDifference)\n\t\t}\n\n\t\t// Clear any existing snaps\n\t\teditor.snaps.clearIndicators()\n\n\t\tlet nextHandle = { ...initialHandle, x: point.x, y: point.y }\n\n\t\tlet canSnap = false\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tif (initialHandle.canSnap && initialHandle.snapType) {\n\t\t\twarnOnce(\n\t\t\t\t'canSnap is deprecated. Cannot use both canSnap and snapType together - snapping disabled. Please use only snapType.'\n\t\t\t)\n\t\t} else {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\tcanSnap = initialHandle.canSnap || initialHandle.snapType !== undefined\n\t\t}\n\n\t\tif (canSnap && (isSnapMode ? !ctrlKey : ctrlKey)) {\n\t\t\t// We're snapping\n\t\t\tconst pageTransform = editor.getShapePageTransform(shape.id)\n\t\t\tif (!pageTransform) throw Error('Expected a page transform')\n\n\t\t\tconst snap = snaps.handles.snapHandle({ currentShapeId: shapeId, handle: nextHandle })\n\n\t\t\tif (snap) {\n\t\t\t\tsnap.nudge.rot(-editor.getShapeParentTransform(shape)!.rotation())\n\t\t\t\tpoint.add(snap.nudge)\n\t\t\t\tnextHandle = { ...initialHandle, x: point.x, y: point.y }\n\t\t\t}\n\t\t}\n\n\t\tconst changes = util.onHandleDrag?.(shape, {\n\t\t\thandle: nextHandle,\n\t\t\tisPrecise: this.isPrecise || altKey,\n\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\tinitial: initial,\n\t\t})\n\n\t\tconst next: TLShapePartial<any> = { id: shape.id, type: shape.type, ...changes }\n\n\t\t// Arrows\n\t\tif (\n\t\t\tinitialHandle.type === 'vertex' &&\n\t\t\tthis.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')\n\t\t) {\n\t\t\tconst bindingAfter = getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']\n\n\t\t\tif (bindingAfter) {\n\t\t\t\tif (initialBinding?.toId !== bindingAfter.toId) {\n\t\t\t\t\tthis.pointingId = bindingAfter.toId\n\t\t\t\t\tthis.isPrecise = pointerVelocity.len() < 0.5 || altKey\n\t\t\t\t\tthis.isPreciseId = this.isPrecise ? bindingAfter.toId : null\n\t\t\t\t\tthis.resetExactTimeout()\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (initialBinding) {\n\t\t\t\t\tthis.pointingId = null\n\t\t\t\t\tthis.isPrecise = false\n\t\t\t\t\tthis.isPreciseId = null\n\t\t\t\t\tthis.resetExactTimeout()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (changes) {\n\t\t\teditor.updateShapes([next])\n\t\t}\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tMat,\n\tStateNode,\n\tTLArrowShape,\n\tTLHandle,\n\tTLLineShape,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\tTLShapePartial,\n\tVec,\n\tkickoutOccludedShapes,\n\tsnapAngle,\n\tsortByIndex,\n\tstructuredClone,\n\twarnOnce,\n} from '@tldraw/editor'\nimport { ArrowShapeUtil } from '../../../shapes/arrow/ArrowShapeUtil'\nimport { clearArrowTargetState } from '../../../shapes/arrow/arrowTargetState'\nimport { getArrowBindings } from '../../../shapes/arrow/shared'\n\nexport type DraggingHandleInfo = TLPointerEventInfo & {\n\tshape: TLArrowShape | TLLineShape\n\ttarget: 'handle'\n\tonInteractionEnd?: string | (() => void)\n\tisCreating?: boolean\n\tcreatingMarkId?: string\n}\n\nexport class DraggingHandle extends StateNode {\n\tstatic override id = 'dragging_handle'\n\n\tshapeId!: TLShapeId\n\tinitialHandle!: TLHandle\n\tinitialAdjacentHandle!: TLHandle | null\n\tinitialPagePoint!: Vec\n\n\tmarkId!: string\n\tinitialPageTransform!: Mat\n\tinitialPageRotation!: number\n\n\tinfo!: DraggingHandleInfo\n\n\tisPrecise = false\n\tisPreciseId: TLShapeId | null = null\n\tpointingId: TLShapeId | null = null\n\n\toverride onEnter(info: DraggingHandleInfo) {\n\t\tconst { shape, isCreating, creatingMarkId, handle } = info\n\t\tthis.info = info\n\t\tif (typeof info.onInteractionEnd === 'string') {\n\t\t\tthis.parent.setCurrentToolIdMask(info.onInteractionEnd)\n\t\t}\n\t\tthis.shapeId = shape.id\n\t\tthis.markId = ''\n\n\t\tif (isCreating) {\n\t\t\tif (creatingMarkId) {\n\t\t\t\tthis.markId = creatingMarkId\n\t\t\t} else {\n\t\t\t\t// handle legacy implicit `creating:{shapeId}` marks\n\t\t\t\tconst markId = this.editor.getMarkIdMatching(\n\t\t\t\t\t`creating:${this.editor.getOnlySelectedShapeId()}`\n\t\t\t\t)\n\t\t\t\tif (markId) {\n\t\t\t\t\tthis.markId = markId\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.markId = this.editor.markHistoryStoppingPoint('dragging handle')\n\t\t}\n\n\t\tthis.initialHandle = structuredClone(handle)\n\n\t\tthis.initialPageTransform = this.editor.getShapePageTransform(shape)!\n\t\tthis.initialPageRotation = this.initialPageTransform.rotation()\n\t\tthis.initialPagePoint = this.editor.inputs.originPagePoint.clone()\n\n\t\tthis.editor.setCursor({ type: isCreating ? 'cross' : 'grabbing', rotation: 0 })\n\n\t\tconst handles = this.editor.getShapeHandles(shape)!.sort(sortByIndex)\n\t\tconst index = handles.findIndex((h) => h.id === info.handle.id)\n\n\t\t// Find the adjacent handle\n\t\tthis.initialAdjacentHandle = null\n\n\t\t// First, check if the handle specifies a custom reference handle\n\t\tif (info.handle.snapReferenceHandleId) {\n\t\t\tconst customHandle = handles.find((h) => h.id === info.handle.snapReferenceHandleId)\n\t\t\tif (customHandle) {\n\t\t\t\tthis.initialAdjacentHandle = customHandle\n\t\t\t}\n\t\t}\n\n\t\t// If no custom reference handle, use default behavior\n\t\tif (!this.initialAdjacentHandle) {\n\t\t\t// Start from the handle and work forward\n\t\t\tfor (let i = index + 1; i < handles.length; i++) {\n\t\t\t\tconst handle = handles[i]\n\t\t\t\tif (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {\n\t\t\t\t\tthis.initialAdjacentHandle = handle\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If still no handle, start from the end and work backward\n\t\t\tif (!this.initialAdjacentHandle) {\n\t\t\t\tfor (let i = handles.length - 1; i >= 0; i--) {\n\t\t\t\t\tconst handle = handles[i]\n\t\t\t\t\tif (handle.type === 'vertex' && handle.id !== 'middle' && handle.id !== info.handle.id) {\n\t\t\t\t\t\tthis.initialAdjacentHandle = handle\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// <!-- Only relevant to arrows\n\t\tif (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {\n\t\t\tconst initialBinding = getArrowBindings(this.editor, shape)[info.handle.id as 'start' | 'end']\n\n\t\t\tthis.isPrecise = false\n\n\t\t\tif (initialBinding) {\n\t\t\t\tthis.isPrecise = initialBinding.props.isPrecise\n\t\t\t\tif (this.isPrecise) {\n\t\t\t\t\tthis.isPreciseId = initialBinding.toId\n\t\t\t\t} else {\n\t\t\t\t\tthis.resetExactTimeout()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// -->\n\n\t\t// Call onHandleDragStart callback\n\t\tconst handleDragInfo = {\n\t\t\thandle: this.initialHandle,\n\t\t\tisPrecise: this.isPrecise,\n\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\tinitial: shape,\n\t\t}\n\t\tconst util = this.editor.getShapeUtil(shape)\n\t\tconst startChanges = util.onHandleDragStart?.(shape, handleDragInfo)\n\t\tif (startChanges) {\n\t\t\tthis.editor.updateShapes([{ ...startChanges, id: shape.id, type: shape.type }])\n\t\t}\n\n\t\tthis.update()\n\n\t\tthis.editor.select(this.shapeId)\n\t}\n\n\t// Only relevant to arrows\n\tprivate exactTimeout = -1\n\n\t// Only relevant to arrows\n\tprivate resetExactTimeout() {\n\t\tconst arrowUtil = this.editor.getShapeUtil<ArrowShapeUtil>('arrow')\n\t\tconst timeoutValue = arrowUtil.options.pointingPreciseTimeout\n\n\t\tif (this.exactTimeout !== -1) {\n\t\t\tthis.clearExactTimeout()\n\t\t}\n\n\t\tthis.exactTimeout = this.editor.timers.setTimeout(() => {\n\t\t\tif (this.getIsActive() && !this.isPrecise) {\n\t\t\t\tthis.isPrecise = true\n\t\t\t\tthis.isPreciseId = this.pointingId\n\t\t\t\tthis.update()\n\t\t\t}\n\t\t\tthis.exactTimeout = -1\n\t\t}, timeoutValue)\n\t}\n\n\t// Only relevant to arrows\n\tprivate clearExactTimeout() {\n\t\tif (this.exactTimeout !== -1) {\n\t\t\tclearTimeout(this.exactTimeout)\n\t\t\tthis.exactTimeout = -1\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyDown() {\n\t\tthis.update()\n\t}\n\n\toverride onKeyUp() {\n\t\tthis.update()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.update()\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onExit() {\n\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\tclearArrowTargetState(this.editor)\n\t\tthis.editor.snaps.clearIndicators()\n\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t}\n\n\tprivate complete() {\n\t\tthis.editor.snaps.clearIndicators()\n\t\tkickoutOccludedShapes(this.editor, [this.shapeId])\n\n\t\t// Call onHandleDragEnd callback before state transitions\n\t\tconst shape = this.editor.getShape(this.shapeId)\n\t\tif (shape) {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst handleDragInfo = {\n\t\t\t\thandle: this.initialHandle,\n\t\t\t\tisPrecise: this.isPrecise,\n\t\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\t\tinitial: this.info.shape,\n\t\t\t}\n\t\t\tconst endChanges = util.onHandleDragEnd?.(shape, handleDragInfo)\n\t\t\tif (endChanges) {\n\t\t\t\tthis.editor.updateShapes([{ ...endChanges, id: shape.id, type: shape.type }])\n\t\t\t}\n\t\t}\n\n\t\tconst { onInteractionEnd } = this.info\n\t\tif (onInteractionEnd) {\n\t\t\tif (typeof onInteractionEnd === 'string') {\n\t\t\t\tif (this.editor.getInstanceState().isToolLocked && onInteractionEnd) {\n\t\t\t\t\t// Return to the tool that was active before this one but only if tool lock is turned on!\n\t\t\t\t\tthis.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tonInteractionEnd?.()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t}\n\n\tprivate cancel() {\n\t\t// Call onHandleDragCancel callback before bailing to mark\n\t\tconst shape = this.editor.getShape(this.shapeId)\n\t\tif (shape) {\n\t\t\tconst util = this.editor.getShapeUtil(shape)\n\t\t\tconst handleDragInfo = {\n\t\t\t\thandle: this.initialHandle,\n\t\t\t\tisPrecise: this.isPrecise,\n\t\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\t\tinitial: this.info.shape,\n\t\t\t}\n\t\t\tutil.onHandleDragCancel?.(shape, handleDragInfo)\n\t\t}\n\n\t\tthis.editor.bailToMark(this.markId)\n\t\tthis.editor.snaps.clearIndicators()\n\n\t\tconst { onInteractionEnd } = this.info\n\t\tif (onInteractionEnd) {\n\t\t\tif (typeof onInteractionEnd === 'string') {\n\t\t\t\t// Return to the tool that was active before this one, whether tool lock is turned on or not!\n\t\t\t\tthis.editor.setCurrentTool(onInteractionEnd, { shapeId: this.shapeId })\n\t\t\t} else {\n\t\t\t\tonInteractionEnd?.()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t}\n\n\tprivate update() {\n\t\tconst { editor, shapeId, initialPagePoint } = this\n\t\tconst { initialHandle, initialPageRotation, initialAdjacentHandle } = this\n\t\tconst isSnapMode = this.editor.user.getIsSnapMode()\n\t\tconst {\n\t\t\tsnaps,\n\t\t\tinputs: { currentPagePoint, shiftKey, ctrlKey, altKey, pointerVelocity },\n\t\t} = editor\n\n\t\tconst initial = this.info.shape\n\n\t\tconst shape = editor.getShape(shapeId)\n\t\tif (!shape) return\n\t\tconst util = editor.getShapeUtil(shape)\n\n\t\tconst initialBinding = editor.isShapeOfType<TLArrowShape>(shape, 'arrow')\n\t\t\t? getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']\n\t\t\t: undefined\n\n\t\tlet point = currentPagePoint\n\t\t\t.clone()\n\t\t\t.sub(initialPagePoint)\n\t\t\t.rot(-initialPageRotation)\n\t\t\t.add(initialHandle)\n\n\t\tif (shiftKey && initialAdjacentHandle && initialHandle.id !== 'middle') {\n\t\t\tconst angle = Vec.Angle(initialAdjacentHandle, point)\n\t\t\tconst snappedAngle = snapAngle(angle, 24)\n\t\t\tconst angleDifference = snappedAngle - angle\n\t\t\tpoint = Vec.RotWith(point, initialAdjacentHandle, angleDifference)\n\t\t}\n\n\t\t// Clear any existing snaps\n\t\teditor.snaps.clearIndicators()\n\n\t\tlet nextHandle = { ...initialHandle, x: point.x, y: point.y }\n\n\t\tlet canSnap = false\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tif (initialHandle.canSnap && initialHandle.snapType) {\n\t\t\twarnOnce(\n\t\t\t\t'canSnap is deprecated. Cannot use both canSnap and snapType together - snapping disabled. Please use only snapType.'\n\t\t\t)\n\t\t} else {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\tcanSnap = initialHandle.canSnap || initialHandle.snapType !== undefined\n\t\t}\n\n\t\tif (canSnap && (isSnapMode ? !ctrlKey : ctrlKey)) {\n\t\t\t// We're snapping\n\t\t\tconst pageTransform = editor.getShapePageTransform(shape.id)\n\t\t\tif (!pageTransform) throw Error('Expected a page transform')\n\n\t\t\tconst snap = snaps.handles.snapHandle({ currentShapeId: shapeId, handle: nextHandle })\n\n\t\t\tif (snap) {\n\t\t\t\tsnap.nudge.rot(-editor.getShapeParentTransform(shape)!.rotation())\n\t\t\t\tpoint.add(snap.nudge)\n\t\t\t\tnextHandle = { ...initialHandle, x: point.x, y: point.y }\n\t\t\t}\n\t\t}\n\n\t\tconst changes = util.onHandleDrag?.(shape, {\n\t\t\thandle: nextHandle,\n\t\t\tisPrecise: this.isPrecise || altKey,\n\t\t\tisCreatingShape: !!this.info.isCreating,\n\t\t\tinitial: initial,\n\t\t})\n\n\t\tconst next: TLShapePartial<any> = { id: shape.id, type: shape.type, ...changes }\n\n\t\t// Arrows\n\t\tif (\n\t\t\tinitialHandle.type === 'vertex' &&\n\t\t\tthis.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')\n\t\t) {\n\t\t\tconst bindingAfter = getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']\n\n\t\t\tif (bindingAfter) {\n\t\t\t\tif (initialBinding?.toId !== bindingAfter.toId) {\n\t\t\t\t\tthis.pointingId = bindingAfter.toId\n\t\t\t\t\tthis.isPrecise = pointerVelocity.len() < 0.5 || altKey\n\t\t\t\t\tthis.isPreciseId = this.isPrecise ? bindingAfter.toId : null\n\t\t\t\t\tthis.resetExactTimeout()\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (initialBinding) {\n\t\t\t\t\tthis.pointingId = null\n\t\t\t\t\tthis.isPrecise = false\n\t\t\t\t\tthis.isPreciseId = null\n\t\t\t\t\tthis.resetExactTimeout()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (changes) {\n\t\t\teditor.updateShapes([next])\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["handle"]
7
7
  }
@@ -389,7 +389,7 @@ class Idle extends StateNode {
389
389
  }
390
390
  case "Tab": {
391
391
  const selectedShapes = this.editor.getSelectedShapes();
392
- if (selectedShapes.length) {
392
+ if (selectedShapes.length && !info.altKey) {
393
393
  this.editor.selectAdjacentShape(info.shiftKey ? "prev" : "next");
394
394
  }
395
395
  break;
@@ -428,7 +428,7 @@ class Idle extends StateNode {
428
428
  }
429
429
  case "Tab": {
430
430
  const selectedShapes = this.editor.getSelectedShapes();
431
- if (selectedShapes.length) {
431
+ if (selectedShapes.length && !info.altKey) {
432
432
  this.editor.selectAdjacentShape(info.shiftKey ? "prev" : "next");
433
433
  }
434
434
  break;