Como adiciono suporte a editor de rich text à minha extensão de markdown?

Criei uma extensão que adiciona algumas tags bbcode para renderizar snapblocks. Quando o editor de rich text foi introduzido, isso quebrou minha extensão, ou pelo menos a tornou inutilizável com o editor de rich text. Quero adicionar suporte para o editor de rich text, então dei uma olhada na extensão spoiled para ver como ela faz isso, e atualmente tenho isso funcionando parcialmente. Eu consigo inserir os snapblocks na postagem no editor de rich text, mas não consigo fazer com que as tags snapblocks existentes sejam convertidas para o editor de rich text.

Aqui está o que tenho até agora.

No momento, estou focado especificamente na conversão real. Tenho uma ideia básica de como funciona, mas até agora não estou obtendo nenhum resultado. Então, como faço o parsing funcionar?

1 curtida

Depois de muita dor de cabeça e de adicionar muitos console.log() ao discourse, descobri por que não estava funcionando e como fazê-lo funcionar.

O parsing não é feito com o HTML renderizado, mas sim com os tokens do markdownit. Eu estava usando state.push(html_raw) na função replace() para renderizar minhas tags 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>`
      },
    });
}

O problema era que o tipo de token html_raw é ignorado pelo conversor do editor de rich text. O que significa que você não pode usar isso se quiser suporte ao editor de rich text.

Descobri que, usando bbcode_open, text e bbcode_close, consigo fazer 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']] // necessário para verificações posteriores
      },
    });
}

Uma vez feito isso, isso não será mais ignorado.

Movendo para rich-text-editor-extension.js, você então faz isso.

// 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*", // Isso é necessário
      createGapCursor: true,
      parseDOM:[{ tag: "pre.snapblocks-blocks" }],
      toDOM: () => ["pre", { class: "snapblocks-blocks" }, 0],
    },
  },
  parse: {
    bbcode_open(state, token) {
      // O token aqui é o mesmo objeto de token `bbcode_open`
      // do 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) {
      // Isso apenas o converte de volta para texto simples markdown
      state.write("[snapblocks]\n");
      state.renderContent(node);
      state.write("\n[/snapblocks]\n\n");
    },
  },
}

As chaves na propriedade parse são parsers para o tipo de token que pode ser usado no parser de markdown.

Definitivamente há mais do que apenas isso, mas pelo menos isso esclarece muito mais do que antes.

1 curtida

Não exatamente, mas já temos a extensão html-block lidando com os tokens html_raw do markdown-it, que é uma maneira genérica de lidar com conteúdo passthrough.

Você pode verificar todos os tipos de extensões permitidas via registerRichEditorExtension, e existem muitas extensões de plugins assim como as que são registradas por padrão que podem ser usadas como inspiração.

Por favor, nos informe se tiver alguma dúvida.

3 curtidas

Obrigado pelo esclarecimento.


Estou agora a deparar-me com um problema em que alternar a versão de bloco não está a funcionar. Consigo alternar blocos em linha, mas não o nó de nível de bloco.

A certa altura, gostaria que ele renderizasse os blocos quando o cursor não estivesse sobre eles, mas sinto que isso pode vir depois de apenas fazê-lo funcionar.

Desculpe, não entendi. O que você quer dizer com “alternar a versão de bloco”?

Minha tag bbcode tem [snapblocks] e [sb], onde [sb] é inline e [snapblocks] é de nível de bloco. Ao pressionar o botão da barra de ferramentas, ele está alternando a tag inline corretamente, mas não está alternando a tag de nível de bloco quando seleciono um bloco de texto.