¿Cómo agrego soporte para editor de texto enriquecido a mi extensión de markdown?

He creado una extensión que añade algunas etiquetas bbcode para renderizar snapblocks. Cuando se introdujo el editor de texto enriquecido, esto rompió mi extensión, o al menos la hizo inutilizable con el editor de texto enriquecido. Quiero añadir soporte para el editor de texto enriquecido, así que miré la extensión spoiled para ver cómo lo hace, y actualmente lo tengo funcionando hasta cierto punto. De hecho, puedo insertar los snapblocks en la publicación en el editor de texto enriquecido, pero no parece que las etiquetas snapblocks existentes se conviertan al editor de texto enriquecido.

Esto es lo que tengo hasta ahora.

Ahora mismo me estoy centrando específicamente en la conversión real. Tengo una idea básica de cómo funciona, pero hasta ahora no estoy obteniendo ningún resultado. Entonces, ¿cómo consigo que funcione el análisis?

1 me gusta

Después de mucho esfuerzo y de añadir muchos console.log()s a discourse, he descubierto por qué no funcionaba y cómo hacerlo funcionar.

El análisis no se realiza con el HTML renderizado, sino con los tokens de markdownit. Había estado usando state.push(html_raw) en la función replace() para renderizar mis etiquetas 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>`
      },
    });
}

El problema era que el tipo de token html_raw es ignorado por el convertidor del editor de texto enriquecido. Lo que significa que no puedes usar esto si quieres compatibilidad con el editor de texto enriquecido.

Descubrí que usando bbcode_open, text y bbcode_close, puedo hacerlo funcionar.

// 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']] // necesario para comprobaciones posteriores
      },
    });
}

Una vez hecho esto, esto ya no será ignorado.

Pasando a rich-text-editor-extension.js, entonces haces esto.

// 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*", // Esto es necesario
      createGapCursor: true,
      parseDOM:[{ tag: "pre.snapblocks-blocks" }],
      toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
    },
  },
  parse: {
    bbcode_open(state, token) {
      // El token aquí es el mismo objeto token `bbcode_open`
      // del código anterior
      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) {
      // Esto simplemente lo convierte de nuevo a texto plano markdown
      state.write("[snapblocks]\n");
      state.renderContent(node);
      state.write("\n[/snapblocks]\n\n");
    },
  },
}

Las claves en la propiedad parse son analizadores para el tipo de token que se puede usar en el analizador de markdown.

Definitivamente hay más que esto, pero al menos esto aclara mucho más las cosas que antes.

1 me gusta

No exactamente, pero ya tenemos la extensión html-block que maneja los tokens html_raw de markdown-it, que es una forma genérica de manejar contenido de passthrough.

Puedes consultar todos los tipos de extensiones permitidas a través de registerRichEditorExtension, y hay muchas extensiones de plugins así como las que se registran por defecto que pueden servir de inspiración.

Por favor, háznos saber si tienes alguna pregunta.

3 Me gusta

Gracias por la aclaración.


Ahora estoy teniendo un problema donde alternar la versión de bloque no funciona. Puedo alternar bloques en línea, pero no el nodo de nivel de bloque.

En algún momento, me gustaría que renderizara los bloques cuando el cursor no está sobre ellos, pero siento que eso puede venir después de que simplemente funcione.

Lo siento, no entiendo. ¿Qué quieres decir con “alternar la versión de bloque”?

Mi etiqueta bbcode tiene [snapblocks] y [sb], donde [sb] es en línea y [snapblocks] es de nivel de bloque. Al presionar el botón de la barra de herramientas, está alternando la etiqueta en línea correctamente, pero no está alternando la etiqueta de nivel de bloque cuando selecciono un bloque de texto.