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.