After a lot of head banging and adding many console.log()s to discourse, I have discovered why it wasn’t working, and how to get it to work.
The parsing is not done with the rendered html, but instead with the markdownit tokens. I had been using state.push(html_raw) in the replace() function to render my [snapblocks] bbcode tags.
// 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>`
},
});
}
The issue was that the html_raw token type is ignored by the rich text editor convertor. Which means that you can’t use this if you want rich text editor support.
I found out that by using bbcode_open, text and bbcode_close, I can get it to work.
// 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']] // needed for later checks
},
});
}
Once that is done, this will not be ignored anymore.
Moving to rich-text-editor-extension.js, you then do this.
// 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*", // This is needed
createGapCursor: true,
parseDOM: [{ tag: "pre.snapblocks-blocks" }],
toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
},
},
parse: {
bbcode_open(state, token) {
// The token here is the same `bbcode_open` token object
// from the previous 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) {
// This just converts it back to plain text markdown
state.write("[snapblocks]\n");
state.renderContent(node);
state.write("\n[/snapblocks]\n\n");
},
},
}
The keys in the parse property are parsers for the token type that can be used in the markdown parser.
There’s definitely more to it than just this, but at least this should clear things up a lot more, and lower the amount of head banging I need to do.