多くの試行錯誤と console.log() を追加して調査した結果、動作しなかった理由と、動作させる方法がわかりました。
レンダリングされた HTML ではなく、markdownit トークンで解析が行われています。replace() 関数で state.push(html_raw) を使用して [snapblocks] bbcode タグをレンダリングしていました。
// 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>`
},
});
}
問題は、html_raw トークンタイプがリッチテキストエディタコンバータによって無視されることでした。これは、リッチテキストエディタのサポートが必要な場合はこれを使用できないことを意味します。
bbcode_open、text、bbcode_close を使用することで、動作させることができることがわかりました。
// 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']] // 後続のチェックに必要
},
});
}
それが完了すると、これは無視されなくなります。
rich-text-editor-extension.js に移動して、次のようにします。
// 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*", // これが必要
createGapCursor: true,
parseDOM: [{ tag: "pre.snapblocks-blocks" }],
toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
},
},
parse: {
bbcode_open(state, token) {
// ここでのトークンは、前のコードと同じ `bbcode_open` トークンオブジェクトです
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) {
// これは単にプレーンテキストマークダウンに変換されます
state.write("[snapblocks]\n");
state.renderContent(node);
state.write("\n[/snapblocks]\n\n");
},
},
}
parse プロパティのキーは、マークダウンパーサーで使用できるトークンタイプのパーサーです。
これには間違いなくもっと多くのことがありますが、少なくともこれまでのところよりもはるかに明確になりました。