{
  "version": 3,
  "sources": ["../../../../src/lib/ui/hooks/useClipboardEvents.ts"],
  "sourcesContent": ["import {\n\tEditor,\n\tFileHelpers,\n\tTLExternalContentSource,\n\tVec,\n\tVecLike,\n\tcompact,\n\tisDefined,\n\tpreventDefault,\n\tstopEventPropagation,\n\tuniq,\n\tuseEditor,\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 { pasteExcalidrawContent } from './clipboard/pasteExcalidrawContent'\nimport { pasteFiles } from './clipboard/pasteFiles'\nimport { pasteTldrawContent } from './clipboard/pasteTldrawContent'\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.getAttribute('contenteditable') ||\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: Editor,\n\tclipboardItems: ClipboardItem[],\n\tpoint?: VecLike\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\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// If we've found tldraw content in the html string, use that as JSON\n\t\t\t\t\t\t\t\t\tconst jsonComment = lz.decompressFromBase64(tldrawHtmlComment)\n\t\t\t\t\t\t\t\t\tif (jsonComment === null) {\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: jsonComment,\n\t\t\t\t\t\t\t\t\t\t\treason: `found tldraw data comment but could not parse base64`,\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} else {\n\t\t\t\t\t\t\t\t\t\tconst json = JSON.parse(jsonComment)\n\t\t\t\t\t\t\t\t\t\tif (json.type !== 'application/tldraw') {\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: `found tldraw data comment but JSON was of a different type: ${json.type}`,\n\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t}\n\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\tpasteTldrawContent(editor, 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\tpasteExcalidrawContent(editor, 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\thandleText(editor, stripHtml(result.data), point, results)\n\t\t\t\treturn\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\tconst stringifiedClipboard = lz.compressToBase64(\n\t\tJSON.stringify({\n\t\t\ttype: 'application/tldraw',\n\t\t\tkind: 'content',\n\t\t\tdata: content,\n\t\t})\n\t)\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 = useEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst copy = useCallback(\n\t\tasync function onCopy(source: TLUiEventSource) {\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.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\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, 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\tstopEventPropagation(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// First try to use the clipboard API:\n\t\t\tif (navigator.clipboard?.read) {\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)\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"],
  "mappings": "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",
  "names": ["url"]
}
