Is It Feasible to Insert Content from One Schema into Another?

Hello,

I’m working with two ProseMirror instances that have different schemas. Specifically, the schema of Instance editorViewOutput is a subset of Instance editorView’s schema. Here’s the code snippet for context:

export function insertAfterSelectedContent(
  editorViewOutput: EditorView,
  editorView: EditorView
) {
  const outputState = editorViewOutput.state;
  const targetState = editorView.state;

  let outputContent = outputState.doc.content;

  const insertPos = targetState.selection.empty
    ? targetState.selection.from
    : targetState.selection.to;

  try {
    let transaction = targetState.tr.insert(insertPos, outputContent);
    const endPos = insertPos + outputContent.size;

    transaction = transaction.setSelection(
      TextSelection.create(transaction.doc, insertPos, endPos)
    );

    editorView.dispatch(transaction);
  } catch (error) {
    console.log("insertAfterSelectedContent error", error);
  }
}

editorViewOutput’s Schema:

import { Schema } from "prosemirror-model";
import { addListNodes } from "prosemirror-schema-list";

import { tableNodes } from "prosemirror-tables";

export const createTableNodes = () => {
  return tableNodes({
    tableGroup: "block",
    cellContent: "block+",
    cellAttributes: {
      background: {
        default: null,
        getFromDOM(dom) {
          return dom.style.backgroundColor || null;
        },
        setDOMAttr(value, attrs) {
          if (value)
            attrs.style = (attrs.style || "") + `background-color: ${value};`;
        },
      },
    },
  });
};

const brDOM = ["br"];

const calcYchangeDomAttrs = (attrs: any, domAttrs = {}) => {
  domAttrs = Object.assign({}, domAttrs);
  if (attrs?.ychange !== null) {
    (domAttrs as any).ychange_user = attrs?.ychange.user;
    (domAttrs as any).ychange_state = attrs?.ychange.state;
  }
  return domAttrs;
};

// :: Object
// [Specs](#model.NodeSpec) for the nodes defined in this schema.
export const nodes: any = {
  // :: NodeSpec The top level document node.
  doc: {
    content: "block+",
  },

  // :: NodeSpec A plain paragraph textblock. Represented in the DOM
  // as a `<p>` element.
  paragraph: {
    attrs: {
      ychange: { default: null },
      align: { default: null },
    },
    content: "inline*",
    group: "block",
    parseDOM: [
      {
        tag: "p",
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
    ],
    toDOM(node: any) {
      const ychangeAttrs = calcYchangeDomAttrs(node.attrs.ychange);
      const alignStyle = node.attrs.align
        ? { style: `text-align: ${node.attrs.align}` }
        : {};

      return ["p", { ...ychangeAttrs, ...alignStyle }, 0];
    },
  },

  // :: NodeSpec A heading textblock, with a `level` attribute that
  // should hold the number 1 to 6. Parsed and serialized as `<h1>` to
  // `<h6>` elements.
  heading: {
    attrs: {
      level: { default: 1 },
      ychange: { default: null },
      id: { default: null },
      "data-toc-id": { default: null },
    },
    content: "inline*",
    group: "block",
    defining: true,
    parseDOM: [
      {
        tag: "h1",
        attrs: { level: 1 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h2",
        attrs: { level: 2 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h3",
        attrs: { level: 3 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h4",
        attrs: { level: 4 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h5",
        attrs: { level: 5 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h6",
        attrs: { level: 6 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
    ],
    toDOM(node: any) {
      const ychangeAttrs = calcYchangeDomAttrs(node.attrs);

      const domAttrs = {
        ...ychangeAttrs,
        ...(node.attrs.id ? { id: node.attrs.id } : {}),
        ...(node.attrs["data-toc-id"]
          ? { "data-toc-id": node.attrs["data-toc-id"] }
          : {}),
        ...(node.attrs.align
          ? { style: `text-align: ${node.attrs.align}` }
          : {}),
      };

      return ["h" + node.attrs.level, domAttrs, 0];
    },
  },

  // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
  blockquote: {
    attrs: { ychange: { default: null } },
    content: "block+",
    group: "block",
    defining: true,
    parseDOM: [{ tag: "blockquote" }],
    toDOM(node: any) {
      return ["blockquote", calcYchangeDomAttrs(node.attrs), 0];
    },
  },

  // :: NodeSpec A horizontal rule (`<hr>`).
  horizontal_rule: {
    attrs: { ychange: { default: null } },
    group: "block",
    parseDOM: [{ tag: "hr" }],
    toDOM(node: any) {
      return ["hr", calcYchangeDomAttrs(node.attrs)];
    },
  },

  // :: NodeSpec A code listing. Disallows marks or non-text inline
  // nodes by default. Represented as a `<pre>` element with a
  // `<code>` element inside of it.
  code_block: {
    attrs: { ychange: { default: null } },
    content: "text*",
    marks: "",
    group: "block",
    code: true,
    defining: true,
    parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
    toDOM(node: any) {
      return ["pre", calcYchangeDomAttrs(node.attrs), ["code", 0]];
    },
  },

  input_node: {
    attrs: {
      "data-type": { default: "input" },
      "data-options": { default: [] },
      "data-placeholder": {},
    },
    inline: true,
    content: "inline*",
    group: "inline",
    selectable: true,
    parseDOM: [
      {
        tag: 'span[data-type="input"]',
        getAttrs: (dom: any) => ({
          "data-type": dom.getAttribute("data-type") || "input",
          "data-placeholder": dom.getAttribute("data-placeholder") || "",
          "data-options": JSON.parse(dom.getAttribute("data-options") || "[]"),
        }),
      },
    ],
    toDOM(node: any) {
      return [
        "span",
        {
          "data-type": node.attrs["data-type"] || "",
          "data-placeholder": node.attrs["data-placeholder"] || "",
          "data-options": JSON.stringify(node.attrs["data-options"]) || "[]",
        },
        0,
      ];
    },
  },

  // :: NodeSpec The text node.
  text: {
    group: "inline",
  },

  // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
  // `alt`, and `href` attributes. The latter two default to the empty
  // string.
  image: {
    inline: true,
    attrs: {
      ychange: { default: null },
      src: {},
      alt: { default: null },
      title: { default: null },
      class: {
        default:
          "inline-image qmwd-rounded-lg qmwd-border qmwd-border-stone-200",
      },
      width: {
        default: 300,
      },
      height: {
        default: null,
      },
      "data-mediafile-type": { default: null },
      "data-mediafile-hash": { default: null },
      "data-mediafile-size": { default: null },
    },
    group: "inline",
    draggable: true,
    parseDOM: [
      {
        tag: "img[src]",
        getAttrs(dom: any) {
          return {
            src: dom.getAttribute("src"),
            title: dom.getAttribute("title"),
            alt: dom.getAttribute("alt"),
            class: dom.getAttribute("class"),
            width: dom.getAttribute("width"),
            height: dom.getAttribute("height"),
            "data-mediafile-type": dom.getAttribute("data-mediafile-type"),
            "data-mediafile-hash": dom.getAttribute("data-mediafile-hash"),
            "data-mediafile-size": dom.getAttribute("data-mediafile-size"),
          };
        },
      },
    ],
    toDOM(node: any) {
      const domAttrs = {
        src: node.attrs.src,
        title: node.attrs.title,
        alt: node.attrs.alt,
        class: node.attrs.class,
        width: node.attrs.width,
        height: node.attrs.height,
        "data-mediafile-type": node.attrs["data-mediafile-type"],
        "data-mediafile-hash": node.attrs["data-mediafile-hash"],
        "data-mediafile-size": node.attrs["data-mediafile-size"],
      };
      return ["img", calcYchangeDomAttrs(node.attrs, domAttrs)];
    },
  },

  // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
  hard_break: {
    inline: true,
    group: "inline",
    selectable: false,
    parseDOM: [{ tag: "br" }],
    toDOM() {
      return brDOM;
    },
  },
};

const emDOM = ["em", 0];
const strongDOM = ["strong", 0];
const codeDOM = ["code", 0];

// :: Object [Specs](#model.MarkSpec) for the marks in the schema.
export const marks: any = {
  // :: MarkSpec A link. Has `href` and `title` attributes. `title`
  // defaults to the empty string. Rendered and parsed as an `<a>`
  // element.
  link: {
    attrs: {
      href: {},
      title: { default: null },
    },
    inclusive: false,
    parseDOM: [
      {
        tag: "a[href]",
        getAttrs(dom: any) {
          return {
            href: dom.getAttribute("href"),
            title: dom.getAttribute("title"),
          };
        },
      },
    ],
    toDOM(node: any) {
      return ["a", node.attrs, 0];
    },
  },

  // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
  // Has parse rules that also match `<i>` and `font-style: italic`.
  em: {
    parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
    toDOM() {
      return emDOM;
    },
  },

  // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
  // also match `<b>` and `font-weight: bold`.
  strong: {
    parseDOM: [
      { tag: "strong" },
      // This works around a Google Docs misbehavior where
      // pasted content will be inexplicably wrapped in `<b>`
      // tags with a font-weight normal.
      {
        tag: "b",
        getAttrs: (node: any) => node.style.fontWeight !== "normal" && null,
      },
      {
        style: "font-weight",
        getAttrs: (value: any) =>
          /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null,
      },
    ],
    toDOM() {
      return strongDOM;
    },
  },

  // :: MarkSpec Code font mark. Represented as a `<code>` element.
  code: {
    parseDOM: [{ tag: "code" }],
    toDOM() {
      return codeDOM;
    },
  },
  ychange: {
    attrs: {
      user: { default: null },
      state: { default: null },
    },
    inclusive: false,
    parseDOM: [{ tag: "ychange" }],
    toDOM(node: any) {
      return [
        "ychange",
        { ychange_user: node.attrs.user, ychange_state: node.attrs.state },
        0,
      ];
    },
  },

  // :: MarkSpec 删除线
  strikethrough: {
    parseDOM: [
      { tag: "del" },
      { tag: "s" },
      { style: "text-decoration=line-through" },
    ],
    toDOM() {
      return ["del", 0];
    },
  },

  // :: MarkSpec 下划线
  underline: {
    parseDOM: [{ tag: "u" }, { style: "text-decoration=underline" }],
    toDOM() {
      return ["u", 0];
    },
  },
};

// :: Schema
// This schema rougly corresponds to the document schema used by
// [CommonMark](http://commonmark.org/), minus the list elements,
// which are defined in the [`prosemirror-schema-list`](#schema-list)
// module.
//
// To reuse elements from this schema, extend or read from its
// `spec.nodes` and `spec.marks` [properties](#model.Schema.spec).
const schemaWithoutList = new Schema({ nodes, marks });

// export const schemaBasic = new Schema({
//   nodes: addListNodes(
//     schemaWithoutList.spec.nodes,
//     "paragraph block*",
//     "block"
//   ),
//   marks: schemaWithoutList.spec.marks,
// });

import {
  createListPlugins,
  createListSpec,
  listInputRules,
  listKeymap,
} from "../plugin/prosemirror-flat-list-master/packages/core/src/index";
import { keymap } from "prosemirror-keymap";
import { inputRules } from "prosemirror-inputrules";

export const schemaBasic = new Schema({
  nodes: schemaWithoutList.spec.nodes
    .append({ list: createListSpec() })
    .append(createTableNodes()),
  marks: schemaWithoutList.spec.marks,
});

export const listKeymapPlugin = keymap(listKeymap);
export const listInputRulePlugin = inputRules({ rules: listInputRules });
export const listPlugins = createListPlugins({ schema: schemaBasic });

editorView’s Schema

import { Schema } from "prosemirror-model";
import { addListNodes } from "prosemirror-schema-list";

import { tableNodes } from "prosemirror-tables";

export const createTableNodes = () => {
  return tableNodes({
    tableGroup: "block",
    cellContent: "block+",
    cellAttributes: {
      background: {
        default: null,
        getFromDOM(dom) {
          return dom.style.backgroundColor || null;
        },
        setDOMAttr(value, attrs) {
          if (value)
            attrs.style = (attrs.style || "") + `background-color: ${value};`;
        },
      },
    },
  });
};

const brDOM = ["br"];

const calcYchangeDomAttrs = (attrs: any, domAttrs = {}) => {
  domAttrs = Object.assign({}, domAttrs);
  if (attrs?.ychange !== null) {
    (domAttrs as any).ychange_user = attrs?.ychange.user;
    (domAttrs as any).ychange_state = attrs?.ychange.state;
  }
  return domAttrs;
};

// :: Object
// [Specs](#model.NodeSpec) for the nodes defined in this schema.
export const nodes: any = {
  // :: NodeSpec The top level document node.
  doc: {
    content: "customTitle block+",
  },

  // :: NodeSpec A title textblock, with a `level` attribute that
  // should hold the number 1. Parsed and serialized as `<div>` elements.
  customTitle: {
    attrs: {
      level: { default: 1 },
      ychange: { default: null },
      id: { default: null },
      "data-toc-id": { default: null },
      class: { default: "custom-title" },
      align: { default: "left" },
    },
    content: "inline*",
    group: "customTitleBlock",
    defining: true,
    parseDOM: [
      {
        tag: "div", // 使用div,原因:https://discuss.prosemirror.net/t/custom-title-element-in-prosemirror-unexpected-content-shift-on-deletion/6017
        getAttrs: (node: any) => ({
          level: node.getAttribute("level") || 1,
          id: node.getAttribute("id"),
          "data-toc-id": node.getAttribute("data-toc-id"),
          align: node.style.textAlign || null,
        }),
      },
    ],
    toDOM(node: any) {
      const ychangeAttrs = calcYchangeDomAttrs(node.attrs);
      const domAttrs: any = {
        ...ychangeAttrs,
        class: node.attrs.class || "custom-title", // 如果 node.attrs.class 不存在,则默认为 "custom-title",
        ...(node.attrs.id ? { id: node.attrs.id } : {}),
        ...(node.attrs["data-toc-id"]
          ? { "data-toc-id": node.attrs["data-toc-id"] }
          : {}),
        ...(node.attrs.align
          ? { style: `text-align: ${node.attrs.align}` }
          : {}),
      };

      return ["div", domAttrs, 0];
    },
  },

  // :: NodeSpec A plain paragraph textblock. Represented in the DOM
  // as a `<p>` element.
  paragraph: {
    attrs: {
      ychange: { default: null },
      align: { default: null },
    },
    content: "inline*",
    group: "block",
    parseDOM: [
      {
        tag: "p",
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
    ],
    toDOM(node: any) {
      const ychangeAttrs = calcYchangeDomAttrs(node.attrs.ychange);
      const alignStyle = node.attrs.align
        ? { style: `text-align: ${node.attrs.align}` }
        : {};

      return ["p", { ...ychangeAttrs, ...alignStyle }, 0];
    },
  },

  // :: NodeSpec A heading textblock, with a `level` attribute that
  // should hold the number 1 to 6. Parsed and serialized as `<h1>` to
  // `<h6>` elements.
  heading: {
    attrs: {
      level: { default: 1 },
      ychange: { default: null },
      id: { default: null },
      "data-toc-id": { default: null },
      align: { default: "left" },
    },
    content: "inline*",
    group: "block",
    defining: true,
    parseDOM: [
      {
        tag: "h1",
        attrs: { level: 1 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h2",
        attrs: { level: 2 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h3",
        attrs: { level: 3 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h4",
        attrs: { level: 4 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h5",
        attrs: { level: 5 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
      {
        tag: "h6",
        attrs: { level: 6 },
        getAttrs: (node: any) => ({
          align: node.style.textAlign || null,
        }),
      },
    ],
    toDOM(node: any) {
      const ychangeAttrs = calcYchangeDomAttrs(node.attrs);

      const domAttrs = {
        ...ychangeAttrs,
        ...(node.attrs.id ? { id: node.attrs.id } : {}),
        ...(node.attrs["data-toc-id"]
          ? { "data-toc-id": node.attrs["data-toc-id"] }
          : {}),
        ...(node.attrs.align
          ? { style: `text-align: ${node.attrs.align}` }
          : {}),
      };

      return ["h" + node.attrs.level, domAttrs, 0];
    },
  },

  // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
  blockquote: {
    attrs: { ychange: { default: null } },
    content: "block+",
    group: "block",
    defining: true,
    parseDOM: [{ tag: "blockquote" }],
    toDOM(node: any) {
      return ["blockquote", calcYchangeDomAttrs(node.attrs), 0];
    },
  },

  // :: NodeSpec A horizontal rule (`<hr>`).
  horizontal_rule: {
    attrs: { ychange: { default: null } },
    group: "block",
    parseDOM: [{ tag: "hr" }],
    toDOM(node: any) {
      return ["hr", calcYchangeDomAttrs(node.attrs)];
    },
  },

  // :: NodeSpec A code listing. Disallows marks or non-text inline
  // nodes by default. Represented as a `<pre>` element with a
  // `<code>` element inside of it.
  code_block: {
    attrs: { ychange: { default: null } },
    content: "text*",
    marks: "",
    group: "block",
    code: true,
    defining: true,
    parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
    toDOM(node: any) {
      return ["pre", calcYchangeDomAttrs(node.attrs), ["code", 0]];
    },
  },

  // :: NodeSpec The text node.
  text: {
    group: "inline",
  },

  // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
  // `alt`, and `href` attributes. The latter two default to the empty
  // string.
  image: {
    inline: true,
    attrs: {
      ychange: { default: null },
      src: {},
      alt: { default: null },
      title: { default: null },
      class: {
        default:
          "inline-image qmwd-rounded-lg qmwd-border qmwd-border-stone-200",
      },
      width: {
        default: 300,
      },
      height: {
        default: null,
      },
      "data-mediafile-type": { default: null },
      "data-mediafile-hash": { default: null },
      "data-mediafile-size": { default: null },
    },
    group: "inline",
    draggable: true,
    parseDOM: [
      {
        tag: "img[src]",
        getAttrs(dom: any) {
          return {
            src: dom.getAttribute("src"),
            title: dom.getAttribute("title"),
            alt: dom.getAttribute("alt"),
            class: dom.getAttribute("class"),
            width: dom.getAttribute("width"),
            height: dom.getAttribute("height"),
            "data-mediafile-type": dom.getAttribute("data-mediafile-type"),
            "data-mediafile-hash": dom.getAttribute("data-mediafile-hash"),
            "data-mediafile-size": dom.getAttribute("data-mediafile-size"),
          };
        },
      },
    ],
    toDOM(node: any) {
      const domAttrs = {
        src: node.attrs.src,
        title: node.attrs.title,
        alt: node.attrs.alt,
        class: node.attrs.class,
        width: node.attrs.width,
        height: node.attrs.height,
        "data-mediafile-type": node.attrs["data-mediafile-type"],
        "data-mediafile-hash": node.attrs["data-mediafile-hash"],
        "data-mediafile-size": node.attrs["data-mediafile-size"],
      };
      return ["img", calcYchangeDomAttrs(node.attrs, domAttrs)];
    },
  },

  // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
  hard_break: {
    inline: true,
    group: "inline",
    selectable: false,
    parseDOM: [{ tag: "br" }],
    toDOM() {
      return brDOM;
    },
  },
};

const emDOM = ["em", 0];
const strongDOM = ["strong", 0];
const codeDOM = ["code", 0];

// :: Object [Specs](#model.MarkSpec) for the marks in the schema.
export const marks: any = {
  // :: MarkSpec A link. Has `href` and `title` attributes. `title`
  // defaults to the empty string. Rendered and parsed as an `<a>`
  // element.
  link: {
    attrs: {
      href: {},
      title: { default: null },
    },
    inclusive: false,
    parseDOM: [
      {
        tag: "a[href]",
        getAttrs(dom: any) {
          return {
            href: dom.getAttribute("href"),
            title: dom.getAttribute("title"),
          };
        },
      },
    ],
    toDOM(node: any) {
      return ["a", node.attrs, 0];
    },
  },

  // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
  // Has parse rules that also match `<i>` and `font-style: italic`.
  em: {
    parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
    toDOM() {
      return emDOM;
    },
  },

  // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
  // also match `<b>` and `font-weight: bold`.
  strong: {
    parseDOM: [
      { tag: "strong" },
      // This works around a Google Docs misbehavior where
      // pasted content will be inexplicably wrapped in `<b>`
      // tags with a font-weight normal.
      {
        tag: "b",
        getAttrs: (node: any) => node.style.fontWeight !== "normal" && null,
      },
      {
        style: "font-weight",
        getAttrs: (value: any) =>
          /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null,
      },
    ],
    toDOM() {
      return strongDOM;
    },
  },

  // :: MarkSpec Code font mark. Represented as a `<code>` element.
  code: {
    parseDOM: [{ tag: "code" }],
    toDOM() {
      return codeDOM;
    },
  },
  ychange: {
    attrs: {
      user: { default: null },
      state: { default: null },
    },
    inclusive: false,
    parseDOM: [{ tag: "ychange" }],
    toDOM(node: any) {
      return [
        "ychange",
        { ychange_user: node.attrs.user, ychange_state: node.attrs.state },
        0,
      ];
    },
  },

  // :: MarkSpec 删除线
  strikethrough: {
    parseDOM: [
      { tag: "del" },
      { tag: "s" },
      { style: "text-decoration=line-through" },
    ],
    toDOM() {
      return ["del", 0];
    },
  },

  // :: MarkSpec 下划线
  underline: {
    parseDOM: [{ tag: "u" }, { style: "text-decoration=underline" }],
    toDOM() {
      return ["u", 0];
    },
  },
};

// :: Schema
// This schema rougly corresponds to the document schema used by
// [CommonMark](http://commonmark.org/), minus the list elements,
// which are defined in the [`prosemirror-schema-list`](#schema-list)
// module.
//
// To reuse elements from this schema, extend or read from its
// `spec.nodes` and `spec.marks` [properties](#model.Schema.spec).
const schemaWithoutList = new Schema({ nodes, marks });

// export const schema = new Schema({
//   nodes: addListNodes(
//     schemaWithoutList.spec.nodes,
//     "paragraph block*",
//     "block"
//   ),
//   marks: schemaWithoutList.spec.marks,
// });

// import {
//   createListPlugins,
//   createListSpec,
//   listInputRules,
//   listKeymap,
// } from "prosemirror-flat-list";
import {
  createListPlugins,
  createListSpec,
  listInputRules,
  listKeymap,
} from "../plugin/prosemirror-flat-list-master/packages/core/src/index";
import { keymap } from "prosemirror-keymap";
import { inputRules } from "prosemirror-inputrules";

export const schema = new Schema({
  nodes: schemaWithoutList.spec.nodes
    .append({ list: createListSpec() })
    .append(createTableNodes()),
  marks: schemaWithoutList.spec.marks,
});

export const listKeymapPlugin = keymap(listKeymap);
export const listInputRulePlugin = inputRules({ rules: listInputRules });
export const listPlugins = createListPlugins({ schema });

My question is, based on my understanding, since Instance editorViewOutput’s schema is contained within Instance editorView’s schema, is it theoretically possible to insert the content from Instance editorViewOutput(outputContent) into Instance editorView using the tr.insert(0, outputContent) method?

I’m not entirely sure if this approach is feasible.If my understanding is incorrect, what would be the correct approach to achieve this insertion? Any insights or guidance on this matter would be greatly appreciated. Thanks in advance!

If schema A is a strict subset of B, you should be able to serialize content from A, and deserialize it with B in order to have it with the correct node types that allow you to insert it into a B document.

1 Like