Markdown拡張機能にリッチテキストエディタサポートを追加するにはどうすればよいですか?

an extensionを作成し、snapblocksをレンダリングするためのbbcodeタグを追加しました。リッチテキストエディタが導入されたとき、この拡張機能は壊れてしまいました。少なくともリッチテキストエディタでは使用できなくなりました。リッチテキストエディタのサポートを追加したいので、壊れた拡張機能を見て、それがどのように機能するかを調べました。現在、ある程度機能しています。スナップブロックをリッチテキストエディタの投稿に挿入することはできますが、既存のスナップブロックタグをリッチテキストエディタに変換することができません。

これまでの成果は以下の通りです。

現在、特に変換に焦点を当てています。基本的な推測はありますが、今のところ結果は得られていません。では、どのようにすれば解析が機能するのでしょうか?

「いいね!」 1

多くの試行錯誤と 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_opentextbbcode_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 プロパティのキーは、マークダウンパーサーで使用できるトークンタイプのパーサーです。

これには間違いなくもっと多くのことがありますが、少なくともこれまでのところよりもはるかに明確になりました。

「いいね!」 1

正確にはそうではありませんが、すでに html-block 拡張機能が html_raw markdown-it トークンを処理しており、これは パススルー コンテンツを処理するための汎用的な方法です。

registerRichEditorExtension を介して許可されるすべての 拡張機能のタイプ を確認できます。また、プラグインからの拡張機能 や、デフォルトで登録されている拡張機能 も多数あり、これらを参考にすることができます。

ご不明な点がございましたら、お知らせください。

「いいね!」 3

明確化ありがとうございます。


ブロックバージョンの切り替えが機能しないという問題が発生しています。インラインブロックは切り替えられますが、ブロックレベルノードは切り替えられません。

いつか、カーソルがブロック上にないときにブロックをレンダリングしたいのですが、それは後で対応できることだと思います。

すみません、よく理解できません。「ブロックバージョンの切り替え」とはどういう意味ですか?

私のbbcodeタグには[snapblocks][sb]があり、[sb]はインライン、[snapblocks]はブロックレベルです。ツールバーボタンを押すと、インラインタグは切り替わりますが、テキストブロックを選択したときにブロックレベルタグが切り替わりません。