{"version":3,"sources":["../src/index.ts","../src/image.ts"],"sourcesContent":["import { Image } from './image.js'\n\nexport * from './image.js'\n\nexport default Image\n","import type { ResizableNodeViewDirection } from '@tiptap/core'\nimport { mergeAttributes, Node, nodeInputRule, ResizableNodeView } from '@tiptap/core'\n\nexport interface ImageOptions {\n /**\n * Controls if the image node should be inline or not.\n * @default false\n * @example true\n */\n inline: boolean\n\n /**\n * Controls if base64 images are allowed. Enable this if you want to allow\n * base64 image urls in the `src` attribute.\n * @default false\n * @example true\n */\n allowBase64: boolean\n\n /**\n * HTML attributes to add to the image element.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record\n\n /**\n * Controls if the image should be resizable and how the resize is configured.\n * @default false\n * @example { directions: { top: true, right: true, bottom: true, left: true, topLeft: true, topRight: true, bottomLeft: true, bottomRight: true }, minWidth: 100, minHeight: 100 }\n */\n resize:\n | {\n enabled: boolean\n directions?: ResizableNodeViewDirection[]\n minWidth?: number\n minHeight?: number\n alwaysPreserveAspectRatio?: boolean\n }\n | false\n}\n\nexport interface SetImageOptions {\n src: string\n alt?: string\n title?: string\n width?: number\n height?: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands {\n image: {\n /**\n * Add an image\n * @param options The image attributes\n * @example\n * editor\n * .commands\n * .setImage({ src: 'https://tiptap.dev/logo.png', alt: 'tiptap', title: 'tiptap logo' })\n */\n setImage: (options: SetImageOptions) => ReturnType\n }\n }\n}\n\n/**\n * Matches an image to a ![image](src \"title\") on input.\n */\nexport const inputRegex = /(?:^|\\s)(!\\[(.+|:?)]\\((\\S+)(?:(?:\\s+)[\"'](\\S+)[\"'])?\\))$/\n\n/**\n * This extension allows you to insert images.\n * @see https://www.tiptap.dev/api/nodes/image\n */\nexport const Image = Node.create({\n name: 'image',\n\n addOptions() {\n return {\n inline: false,\n allowBase64: false,\n HTMLAttributes: {},\n resize: false,\n }\n },\n\n inline() {\n return this.options.inline\n },\n\n group() {\n return this.options.inline ? 'inline' : 'block'\n },\n\n draggable: true,\n\n addAttributes() {\n return {\n src: {\n default: null,\n },\n alt: {\n default: null,\n },\n title: {\n default: null,\n },\n width: {\n default: null,\n },\n height: {\n default: null,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: this.options.allowBase64 ? 'img[src]' : 'img[src]:not([src^=\"data:\"])',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['img', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]\n },\n\n parseMarkdown: (token, helpers) => {\n return helpers.createNode('image', {\n src: token.href,\n title: token.title,\n alt: token.text,\n })\n },\n\n renderMarkdown: node => {\n const src = node.attrs?.src ?? ''\n const alt = node.attrs?.alt ?? ''\n const title = node.attrs?.title ?? ''\n\n return title ? `![${alt}](${src} \"${title}\")` : `![${alt}](${src})`\n },\n\n addNodeView() {\n if (!this.options.resize || !this.options.resize.enabled || typeof document === 'undefined') {\n return null\n }\n\n const { directions, minWidth, minHeight, alwaysPreserveAspectRatio } = this.options.resize\n\n return ({ node, getPos, HTMLAttributes, editor }) => {\n const el = document.createElement('img')\n\n Object.entries(HTMLAttributes).forEach(([key, value]) => {\n if (value != null) {\n switch (key) {\n case 'width':\n case 'height':\n break\n default:\n el.setAttribute(key, value)\n break\n }\n }\n })\n\n el.src = HTMLAttributes.src\n\n const nodeView = new ResizableNodeView({\n element: el,\n editor,\n node,\n getPos,\n onResize: (width, height) => {\n el.style.width = `${width}px`\n el.style.height = `${height}px`\n },\n onCommit: (width, height) => {\n const pos = getPos()\n if (pos === undefined) {\n return\n }\n\n this.editor\n .chain()\n .setNodeSelection(pos)\n .updateAttributes(this.name, {\n width,\n height,\n })\n .run()\n },\n onUpdate: (updatedNode, _decorations, _innerDecorations) => {\n if (updatedNode.type !== node.type) {\n return false\n }\n\n return true\n },\n options: {\n directions,\n min: {\n width: minWidth,\n height: minHeight,\n },\n preserveAspectRatio: alwaysPreserveAspectRatio === true,\n },\n })\n\n const dom = nodeView.dom as HTMLElement\n\n // when image is loaded, show the node view to get the correct dimensions\n dom.style.visibility = 'hidden'\n dom.style.pointerEvents = 'none'\n el.onload = () => {\n dom.style.visibility = ''\n dom.style.pointerEvents = ''\n }\n\n return nodeView\n }\n },\n\n addCommands() {\n return {\n setImage:\n options =>\n ({ commands }) => {\n return commands.insertContent({\n type: this.name,\n attrs: options,\n })\n },\n }\n },\n\n addInputRules() {\n return [\n nodeInputRule({\n find: inputRegex,\n type: this.type,\n getAttributes: match => {\n const [, , alt, src, title] = match\n\n return { src, alt, title }\n },\n }),\n ]\n },\n})\n"],"mappings":"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","names":[]}