How to force plain test paste

I’m working on a plain text editor, functionally very similar to a textarea.

I’d like to force the plain text pasting behaviour in the clipboard handler. As I see there are many handlers I can use, but the main behaviour is hard-coded in input.ts.

I come up with a few solutions. 1 goes into an infinite loop. 2 simply doesn’t paste anything. 3 seems to work well, the moment I press a key, it removes all line breaks and makes it into a single line string.

How would you recommend me properly implementing this functionality?

export const forcePlainTextPaste = new Plugin({
  props: {
    handlePaste(view, event) {
      const text = event.clipboardData?.getData('text/plain')
      if (text) {
        view.pasteText(text, event)
        return true
      }
      return false
    },
  },
})

export const forcePlainTextPaste2 = new Plugin({
  props: {
    transformPastedHTML() {
      // Return empty string to force plain text parsing
      return ''
    },
  },
})

export const forcePlainTextPaste3 = new Plugin({
  props: {
    handlePaste(view, event) {
      const text = event.clipboardData?.getData('text/plain')
      if (text) {
        const tr = view.state.tr.insertText(text)
        view.dispatch(tr)
        return true
      }
      return false
    },
  },
})



export const forcePlainTextPaste = new Plugin({
  props: {
    handlePaste(view, event) {
      const text = event.clipboardData?.getData('text/plain')
      if (text) {
        view.pasteText(text, event)
        return true
      }
      return false
    },
  },
})

export const forcePlainTextPaste2 = new Plugin({
  props: {
    transformPastedHTML() {
      // Return empty string to force plain text parsing
      return ''
    },
  },
})

export const forcePlainTextPaste3 = new Plugin({
  props: {
    handlePaste(view, event) {
      const text = event.clipboardData?.getData('text/plain')
      if (text) {
        const tr = view.state.tr.insertText(text)
        view.dispatch(tr)
        return true
      }
      return false
    },
  },
})

This is my schema:

import { type DOMOutputSpec, type NodeSpec, Schema } from 'prosemirror-model'

const pDOM: DOMOutputSpec = ['p', 0]
const brDOM: DOMOutputSpec = ['br']

export const pmSchema = new Schema({
  nodes: {
    doc: {
      content: 'block+',
    } as NodeSpec,

    paragraph: {
      content: 'inline*',
      group: 'block',
      parseDOM: [
        {
          tag: 'p',
          preserveWhitespace: 'full',
        },
      ],
      toDOM() {
        return pDOM
      },
    } as NodeSpec,

    text: {
      group: 'inline',
    } as NodeSpec,

    hard_break: {
      inline: true,
      group: 'inline',
      selectable: false,
      parseDOM: [{ tag: 'br' }],
      toDOM() {
        return brDOM
      },
    } as NodeSpec,
  },
  marks: {},
})

It sounds like a text editor (such as CodeMirror) might make that easier. But if you configure your ProseMirror schema to only allow the plain constructs you intend to allow, pasting should automatically do the right thing.

Yes, in theory everything works, but I’m running into cases which do not.

For example here is this: plain

  blocks.push({
    type: 'text',
    content: multiLineText,
  })

html:

<meta charset='utf-8'><pre class="CodeToken__styles.base backgroundColor-x1qv17b8 paddingBlock-xp59q4u paddingInline-xaope02 borderRadius-xm5kryp borderColor-x7kbyhx borderWidth-xmkeg23 fontSize-x1s8wshw overflowX-xw2csxc" style="margin: 0px 0px 1rem; padding: 0px; box-sizing: border-box; border-width: 1px; border-style: solid; border-color: rgb(221, 221, 221); font-family: var(--font-family-code); font-feature-settings: normal; font-variation-settings: normal; font-size: 0.875em; border-radius: var(--xlxplcu); padding-block: 10px; padding-inline: 12px; background-color: rgb(249, 249, 249); overflow-x: auto; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><code style="margin: 0px; padding: 0px; box-sizing: border-box; border-width: 0px; border-style: solid; border-color: currentcolor; font-family: var(--font-family-code); font-feature-settings: normal; font-variation-settings: normal; font-size: 1em;">  blocks.<span class="hljs-title function_" style="margin: 0px; padding: 0px; box-sizing: border-box; border-width: 0px; border-style: solid; border-color: currentcolor; color: rgb(111, 66, 193);">push</span>({
    <span class="hljs-attr" style="margin: 0px; padding: 0px; box-sizing: border-box; border-width: 0px; border-style: solid; border-color: currentcolor; color: rgb(0, 92, 197);">type</span>: <span class="hljs-string" style="margin: 0px; padding: 0px; box-sizing: border-box; border-width: 0px; border-style: solid; border-color: currentcolor; color: rgb(3, 47, 98);">'text'</span>,
    <span class="hljs-attr" style="margin: 0px; padding: 0px; box-sizing: border-box; border-width: 0px; border-style: solid; border-color: currentcolor; color: rgb(0, 92, 197);">content</span>: multiLineText,
  })</code></pre>

This can only be pasted if I use the Shift version, otherwise line breaks disappear. It works correctly on https://prosemirror.net/ though. With my schema it removes the newlines.

Why does it happen?