{"version":3,"file":"extension-CDrL5i44.mjs","names":[],"sources":["../src/plugins/image/upload-flow.ts","../src/plugins/image/file-handler.ts","../src/plugins/image/extension.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core';\nimport type { UseEditorImageOptions } from './types';\n\ninterface ExecuteUploadFlowParams {\n  editor: Editor;\n  file: File;\n  uploadImage: UseEditorImageOptions['uploadImage'];\n}\n\nexport async function executeUploadFlow({\n  editor,\n  file,\n  uploadImage,\n}: ExecuteUploadFlowParams): Promise<void> {\n  const blobUrl = URL.createObjectURL(file);\n\n  editor.chain().focus().setImage({ src: blobUrl }).run();\n\n  try {\n    const { url } = await uploadImage(file);\n    swapImageSrc(editor, blobUrl, url);\n  } catch (error) {\n    removeImageBySrc(editor, blobUrl);\n    console.error(\n      `Failed to upload image \"${file.name}\":`,\n      error instanceof Error ? error : new Error(String(error)),\n    );\n  } finally {\n    URL.revokeObjectURL(blobUrl);\n  }\n}\n\nfunction swapImageSrc(editor: Editor, oldSrc: string, newSrc: string): void {\n  const { state } = editor;\n  const { tr } = state;\n  let found = false;\n\n  state.doc.descendants((node, pos) => {\n    if (found) return false;\n    if (node.type.name === 'image' && node.attrs.src === oldSrc) {\n      tr.setNodeMarkup(pos, undefined, { ...node.attrs, src: newSrc });\n      found = true;\n      return false;\n    }\n  });\n\n  if (found) {\n    editor.view.dispatch(tr);\n  }\n}\n\nfunction removeImageBySrc(editor: Editor, src: string): void {\n  const { state } = editor;\n  const { tr } = state;\n  let found = false;\n\n  state.doc.descendants((node, pos) => {\n    if (found) return false;\n    if (node.type.name === 'image' && node.attrs.src === src) {\n      tr.delete(pos, pos + node.nodeSize);\n      found = true;\n      return false;\n    }\n  });\n\n  if (found) {\n    editor.view.dispatch(tr);\n  }\n}\n","import type { Editor } from '@tiptap/core';\nimport { Plugin, PluginKey } from '@tiptap/pm/state';\nimport type { UseEditorImageOptions } from './types';\nimport { executeUploadFlow } from './upload-flow';\n\nexport function createImageFileHandlerPlugin(\n  editor: Editor,\n  uploadImage: UseEditorImageOptions['uploadImage'],\n) {\n  return new Plugin({\n    key: new PluginKey('imageFileHandler'),\n    props: {\n      handlePaste(_view, event) {\n        const file = event.clipboardData?.files?.[0];\n        if (!file?.type.includes('image/')) {\n          return false;\n        }\n\n        event.preventDefault();\n        void executeUploadFlow({ editor, file, uploadImage });\n\n        return true;\n      },\n      handleDrop(_view, event, _slice, moved) {\n        if (moved || !event.dataTransfer?.files?.[0]) {\n          return false;\n        }\n\n        const file = event.dataTransfer.files[0];\n        if (!file.type.includes('image/')) {\n          return false;\n        }\n\n        event.preventDefault();\n        void executeUploadFlow({ editor, file, uploadImage });\n\n        return true;\n      },\n    },\n  });\n}\n","import { Img, Link } from 'react-email';\nimport { EmailNode } from '../../core/serializer/email-node';\nimport { createImageFileHandlerPlugin } from './file-handler';\nimport type { UseEditorImageOptions } from './types';\nimport { executeUploadFlow } from './upload-flow';\n\nexport function createImageExtension(options: UseEditorImageOptions) {\n  return EmailNode.create({\n    name: 'image',\n    group: 'block',\n    atom: true,\n    draggable: true,\n\n    addAttributes() {\n      return {\n        src: { default: '' },\n        alt: { default: '' },\n        width: { default: 'auto' },\n        height: { default: 'auto' },\n        alignment: { default: 'center' },\n        href: { default: null },\n      };\n    },\n\n    parseHTML() {\n      return [{ tag: 'img[src]' }];\n    },\n\n    renderHTML({ HTMLAttributes }) {\n      return ['img', HTMLAttributes];\n    },\n\n    addCommands() {\n      return {\n        setImage:\n          (attrs) =>\n          ({ commands }) => {\n            return commands.insertContent({\n              type: this.name,\n              attrs,\n            });\n          },\n\n        uploadImage:\n          () =>\n          ({ editor }) => {\n            const input = document.createElement('input');\n            input.type = 'file';\n            input.accept = 'image/*';\n            input.onchange = () => {\n              const file = input.files?.[0];\n              if (file) {\n                void executeUploadFlow({\n                  editor,\n                  file,\n                  uploadImage: options.uploadImage,\n                });\n              }\n            };\n            input.click();\n            return true;\n          },\n      };\n    },\n\n    addProseMirrorPlugins() {\n      const { editor } = this;\n\n      return [createImageFileHandlerPlugin(editor, options.uploadImage)];\n    },\n\n    renderToReactEmail: ({ node, style }) => {\n      const img = (\n        <Img\n          alt={node.attrs?.alt ?? ''}\n          height={\n            node.attrs?.height === 'auto' ? undefined : node.attrs?.height\n          }\n          src={node.attrs?.src ?? ''}\n          style={style}\n          width={node.attrs?.width === 'auto' ? undefined : node.attrs?.width}\n        />\n      );\n\n      if (node.attrs?.href) {\n        return <Link href={node.attrs.href}>{img}</Link>;\n      }\n\n      return img;\n    },\n  });\n}\n"],"mappings":"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"}