Comment ajouter la prise en charge d'un éditeur de texte enrichi à mon extension markdown ?

Après beaucoup de frustration et l’ajout de nombreux console.log() dans discourse, j’ai découvert pourquoi cela ne fonctionnait pas et comment y remédier.

L’analyse n’est pas effectuée sur le HTML rendu, mais sur les tokens markdownit. J’utilisais state.push(html_raw) dans la fonction replace() pour rendre mes tags bbcode [snapblocks].

// 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>`
      },
    });
}

Le problème était que le type de token html_raw est ignoré par le convertisseur d’éditeur de texte riche. Cela signifie que vous ne pouvez pas l’utiliser si vous souhaitez une prise en charge de l’éditeur de texte riche.

J’ai découvert qu’en utilisant bbcode_open, text et bbcode_close, je pouvais y parvenir.

// 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']] // nécessaire pour les vérifications ultérieures
      },
    });
}

Une fois cela fait, cela ne sera plus ignoré.

En passant à rich-text-editor-extension.js, vous faites ensuite ceci.

// 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*", // Ceci est nécessaire
      createGapCursor: true,
      parseDOM: [{ tag: "pre.snapblocks-blocks" }],
      toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
    },
  },
  parse: {
    bbcode_open(state, token) {
      // Le token ici est le même objet token `bbcode_open`
      // que dans le code précédent
      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) {
      // Ceci convertit simplement en texte brut markdown
      state.write("[snapblocks]\n");
      state.renderContent(node);
      state.write("\n[/snapblocks]\n\n");
    },
  },
}

Les clés de la propriété parse sont des analyseurs pour le type de token qui peuvent être utilisés dans l’analyseur markdown.

Il y a certainement plus que cela, mais au moins cela clarifie beaucoup plus les choses qu’auparavant.

1 « J'aime »