Wie füge ich Rich-Text-Editor-Unterstützung zu meiner Markdown-Erweiterung hinzu?

Nach viel Kopfzerbrechen und dem Hinzufügen vieler console.log()s zu Discourse habe ich entdeckt, warum es nicht funktionierte und wie es zum Laufen gebracht werden kann.

Das Parsen erfolgt nicht mit dem gerenderten HTML, sondern mit den Markdown-Tokens. Ich hatte state.push(html_raw) in der replace()-Funktion verwendet, um meine [snapblocks] BBCode-Tags zu rendern.

// assets/javascripts/lib/discourse-markdown/snapblocks-discourse.js

export function(helper) {
  md.block.bbcode.ruler.push("snapblocks", {
      tag: "snapblocks",
      replace(state, tagInfo, content) {
        let token = state.push('html_raw', '', 0)
        token.content = `<pre class="snapblocks-blocks">${content}</pre>`
      },
    });
}

Das Problem war, dass der Token-Typ html_raw vom Rich-Text-Editor-Konverter ignoriert wird. Das bedeutet, dass Sie dies nicht verwenden können, wenn Sie Unterstützung für den Rich-Text-Editor wünschen.

Ich habe herausgefunden, dass es durch die Verwendung von bbcode_open, text und bbcode_close funktioniert.

// assets/javascripts/lib/discourse-markdown/snapblocks-discourse.js

export function(helper) {
  md.block.bbcode.ruler.push("snapblocks", {
      tag: "snapblocks",
      replace(state, tagInfo, content) {
        let token = state.push('bbcode_open', 'pre', 1)
        token.attrs = [['class', 'snapblocks-discourse']]

        token = state.push('text', '', 0)
        token.content = content

        token = token.push('bbcode_close', 'pre', -1)
        token.attrs = [['class', 'snapblocks-discourse']] // für spätere Prüfungen benötigt
      },
    });
}

Sobald dies erledigt ist, wird es nicht mehr ignoriert.

Wenn Sie zu rich-text-editor-extension.js wechseln, machen Sie Folgendes.

// assets/javascripts/lib/rich-text-editor-extension.js

import { i18n } from "discourse-i18n";

const SNAPBLOCKS_NODES = ["inline_snapblocks", "snapblocks"];

/** @type {RichEditorExtension} */
const extension = {
    snapblocks: {
      attrs: { rendered: { default: true } },
      code: true,
      group: "block",
      content: "text*", // Dies ist erforderlich
      createGapCursor: true,
      parseDOM:[{ tag: "pre.snapblocks-blocks" }],
      toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
    },
  },
  parse: {
    bbcode_open(state, token) {
      // Der Token hier ist dasselbe `bbcode_open`-Token-Objekt
      // aus dem vorherigen Code
      if (token.attrGet('class') === 'snapblocks-blocks') {
        state.openNode(state.schema.nodes.snapblocks, {
          open: token.attrGet("open") !== null,
        });
        return true;
      }
    },
    bbcode_close(state, token) {
      if (token.attrGet('class') === 'snapblocks-blocks') {
        state.closeNode();
        return true;
      }
    },
  },
  serializeNode: {
    snapblocks(state, node) {
      // Dies konvertiert es einfach zurück in einfachen Text-Markdown
      state.write("[snapblocks]\n");
      state.renderContent(node);
      state.write("\n[/snapblocks]\n\n");
    },
  },
}

Die Schlüssel in der parse-Eigenschaft sind Parser für den Token-Typ, der im Markdown-Parser verwendet werden kann.

Es steckt definitiv mehr dahinter, aber zumindest klärt dies die Dinge viel mehr als zuvor.

1 „Gefällt mir“