Oddities when posting images via a clipboard

I slightly modified the original image node by adding the image ID in the database and replacing the output in the DOM so that the caption under the image is visible.

const nodes = baseSchema.spec.nodes.remove('image').append({
    image: {
        attrs: {
            fid: { default: null, validate: "string|null|number" },
            src: { default: null, validate: "string|null" },
            alt: { default: null, validate: "string|null" },
            title: { default: null, validate: "string|null" },
        },
        group: "block",
        draggable: true,
        parseDOM: [{tag: "img[src]", getAttrs(dom: HTMLElement) {
            return {
                fid: dom.getAttribute("fid"),
                src: dom.getAttribute("src"),
                alt: dom.getAttribute("alt"),
                title: dom.getAttribute("title"),
            }
        }}],
        toDOM(node) {
            let { fid, src, alt, title } = node.attrs;
            if (fid) src = `${API_URL}/files/${fid}`;
            if (!src) src = ERROR_IMAGE_DATA;
            return ["div", { title, class: "image" }, [ "img", { fid, src, alt, title }]];
        }
    } as NodeSpec,
});

After that, I noticed a strange behavior when pasting images via the clipboard: When pasting text containing images, images in webp format are not inserted (other formats are inserted correctly), but replaced with a paragraph. However, if you insert a separate image into the editor, it will be inserted correctly.

What is the reason for this?

After some research, I realized that the problem is certainly not in the image format. But rather in the fact that the layout of the site from which I made cat and paste had images in paragraphs and for some reason this construction throws the image out of the document when copying.

<p>
    <img src="https://cdn.devreality.ru/itnews/2025_4/30bb7069e84a4f5c8e2488a073997776.webp">
    <br>
    <em>Battlestate Games</em>
</p>

Why is this happening?

How are images represented in your schema? Pasting something like that into the basic schema seems to work fine.

I gave the definition in the first post

image: {
        atom: true,
        attrs: {
            fid: { default: null, validate: "string|null|number" },
            src: { default: null, validate: "string|null" },
            alt: { default: null, validate: "string|null" },
            title: { default: null, validate: "string|null" },
        },
        group: "block",
        draggable: true,
        parseDOM: [{tag: "img[src]", getAttrs(dom: HTMLElement) {
            return {
                fid: dom.getAttribute("fid"),
                src: dom.getAttribute("src"),
                alt: dom.getAttribute("alt"),
                title: dom.getAttribute("title"),
            }
        }}],
        toDOM(node) {
            let { fid, src, alt, title } = node.attrs;
            if (fid) src = `${API_URL}/files/${fid}`;
            if (!src) src = ERROR_IMAGE_DATA;
            return ["div", { title, class: "image" }, [ "img", { fid, src, alt, title }]];
        }
    } as NodeSpec

I think the problem is that in my scheme the images should be blocks. And when the parser sees them inside a paragraph, it simply throws\out of it. But I would like the paragraph to be split into two sections in this case.

So you did. Sorry about that.

I can reproduce this now. The existing parser code assumes that nodes whose ProseMirror representation cannot be placed inside the representation of one of their parent nodes (in the DOM) must be discarded. So if your clipboard data includes the paragraph, which cannot be a parent of your image node, it would drop it.

Attached patch relaxes this requirements. I’m a bit worried about the kind of effects it will have on messy parses, but I agree that dropping the image in this situation is not ideal.

Thank you so much!