Markdown Editor: editor WYSIWYG e markup baseado no Gravity UI

TL;DR

Olá! Meu nome é Sergey Makhnatkin, trabalho como desenvolvedor no departamento de User Experience no Yandex Cloud. No ano passado, escrevemos sobre a nossa design system e biblioteca de componentes Gravity UI. Desde então, o sistema foi atualizado várias vezes e ganhou novas funcionalidades, e hoje quero falar sobre uma nova ferramenta — o Markdown Editor, que simplifica significativamente o processo de trabalho com documentação.

Vamos falar sobre a história de criação da interface de usuário, características de arquitetura e detalhes técnicos de integração e desenvolvimento de extensões próprias — e depois, por que tudo isso está disponível em open source.

Aliás, você pode experimentar a ferramenta aqui:

Por que precisamos do nosso próprio Markdown Editor

Para armazenar e estruturar informações corporativas de forma conveniente, desenvolvemos a plataforma Wiki, que permite criar bases de conhecimento. Além da base de conhecimento, desenvolvemos abordagens para documentação, como Docs as Code, onde documentação e código vivem lado a lado no armazenamento de arquivos (arquivos .md). Assim surgiu a plataforma Diplodoс.

O que une a Wiki e o Diplodoc é que ambas as plataformas trabalham com o dialeto markdown — Yandex Flavored Markdown (YFM), usado em Nebius, Bitrix, DoubleCloud, Mappable, Meteum.

Com o tempo, percebemos que existem dois grupos de usuários que enxergam de forma diferente o processo de criação e edição de texto. Alguns preferem ver imediatamente o resultado final, trabalhando com texto como no MS Word, Confluence ou Notion. Outros confiam apenas na marcação e preferem formatar páginas usando markdown. Não encontramos bibliotecas conhecidas que funcionem simultaneamente em modo WYSIWYG/markdown. Por exemplo, o Notion é apenas WYSIWYG, e em editores de código há apenas markdown e modo de preview.

Desenvolvemos um editor de markdown que pode funcionar simultaneamente em dois modos: visual (WYSIWYG) e modo de marcação (markdown). No primeiro modo, ícones na barra ajudam a aplicar a marcação; no segundo, os usuários podem editar manualmente o código markdown. Além disso, nossa solução salva o documento como arquivo md, independentemente de qual modo foi usado para criá-lo.

Assim é o editor visual, no qual o texto pode ser formatado usando botões:

Full screen image

E assim é o modo de marcação, no qual os elementos de formatação são indicados com símbolos especiais:

Full screen image

Funcionalidades do Markdown Editor no Gravity UI

O editor está em conformidade com o padrão CommonMark, suporta a linguagem markdown padrão e o idioma YFM. Também adicionamos a possibilidade de estender a sintaxe com outros dialetos de markdown, por exemplo Github Flavored Markdown. Ao mesmo tempo, o editor permite alternar do modo de markup para o modo WYSIWYG, e o próprio documento será armazenado como marcação md ou md estendido (por exemplo, no caso de YFM).

Extensões

O editor já vem com muitas extensões e configurações embutidas. Por exemplo, diagramas Mermaid e blocos HTML:

Full screen image
Full screen image

Tentamos tornar o núcleo do editor facilmente extensível. Desenvolvedores podem criar uma extensão própria ou funcionalidades adicionais que ajudem a:

  • adicionar novas entidades — blocos ou modificadores de texto;
  • configurar adicionalmente o parser de markdown;
  • adicionar actions que permitem trabalhar com o editor externamente;
  • enriquecer a funcionalidade da interface, por exemplo, mostrar um menu de comandos disponíveis ao digitar uma barra (/);
  • modificar o comportamento atual, por exemplo, inserir imagens e arquivos e enviá-los para um storage.

Aqui estão alguns exemplos dessas extensões que desenvolvemos para a nossa Wiki:

  • modo colaborativo de edição;
Full screen image
Full screen image
Full screen image
Full screen image
  • includes;
Full screen image
  • estrutura de seção;
Full screen image
  • seções para criar uma grid conveniente;
Full screen image
  • modo markdown com preview;
Full screen image
  • e muitos outros.
Full screen image

A marcação pode ser transformada automaticamente. Se você prefere trabalhar sem mouse, no modo do editor visual há símbolos especiais que permitem aplicar marcação diretamente no texto. Por exemplo, ** tornam o texto em negrito no modo WYSIWYG. Com esses símbolos, é possível formatar texto, criar código inline e em bloco.

Também dá para abrir o menu de extensões digitando o símbolo /.

Full screen image

Presets

O editor permite configurar a barra de ferramentas para cada projeto separadamente, mas ele vem com várias configurações prontas — presets.

Editor sem presets:

Full screen image

preset CommonMark garante suporte aos elementos padrão de markdown: negrito, itálico, cabeçalhos, listas, links, citações, blocos de código.

Full screen image

No preset padrão também aparece texto tachado, além de uma tabela em que nas células pode haver apenas texto. Esse preset corresponde ao markdown‑it padrão.

Full screen image

Como mencionado acima, o editor também suporta YFM, portanto ele se integra perfeitamente com o Diplodoc. No preset YFM aparecem elementos adicionais: tabelas avançadas, inserção de arquivos e imagens, checkboxes, cut, tabs e fonte monoespaçada.

Full screen image

preset completo contém ainda mais elementos.

Full screen image

Arquitetura

No modo WYSIWYG, o editor é baseado na conhecida biblioteca ProseMirror, e para a marcação é usado CodeMirror. O ProseMirror suporta edição com formatação, enquanto o CodeMirror é adequado para situações em que é necessário trabalhar com texto sem marcação.

Escolhemos essas bibliotecas porque foram desenvolvidas por um mesmo autor, são unificadas em arquitetura e abordagem de implementação, são mantidas por uma grande comunidade, são usadas em muitos editores e são bem otimizadas para trabalhar com texto. Por exemplo: sistema de transações para aplicar alterações no documento, decorações para a view, virtualização do DOM e suporte à sintaxe de muitas linguagens de programação.

Integração

Nosso editor é facilmente conectado como um hook React:


import React from 'react';
import {useMarkdownEditor, MarkdownEditorView} from '@gravity-ui/markdown-editor';
import {toaster} from '@gravity-ui/uikit/toaster-singleton-react-18';

function Editor({onSubmit}) {
  const editor = useMarkdownEditor({allowHTML: false});

  React.useEffect(() => {
    function submitHandler() {
      // Serialize current content to markdown markup
      const value = editor.getValue();
      onSubmit(value);
    }

    editor.on('submit', submitHandler);
    return () => {
      editor.off('submit', submitHandler);
    };
  }, [onSubmit]);

  return <MarkdownEditorView stickyToolbar autofocus toaster={toaster} editor={editor} />;
}

Usamos componentes da biblioteca uikit do GravityUI. Isso garante que toda a interface seja consistente e esteja de acordo com guias de estilo unificados. O uso desses componentes também proporciona um alto grau de consistência e reconhecimento para os usuários, tornando o trabalho com o editor ainda mais conveniente.

Temos instruções detalhadas sobre como conectar o editor em uma aplicação React, assim como sobre como conectar diferentes tipos de extensões: por exemplo, YandexGPT, Mermaid ou LaTeX.

Integração de extensões customizadas

O editor já integra várias extensões adicionais. Mas, se isso não for suficiente, desenvolvedores podem adicionar suas próprias extensões no modo WYSIWYG do editor. Sobre o que as extensões podem oferecer, falamos acima.

Se você quiser adicionar um novo bloco ou um modificador de texto, primeiro é preciso configurar a instância interna do markdown‑it com o método configureMd. Em seguida, é necessário adicionar conhecimento sobre a nova entidade usando os métodos addNode ou addMark, passando o nome da entidade e uma função callback que retorna um objeto com três campos obrigatórios:

import insPlugin from 'markdown-it-ins';
export const underlineMarkName = 'ins';

export const UnderlineSpecs: ExtensionAuto = (builder) => {
    builder
        .configureMd((md) => md.use(insPlugin))
        .addMark(underlineMarkName, () => ({
            spec: {
                parseDOM: [{tag: 'ins'}, {tag: 'u'}],
                toDOM() {
                    return ['ins'];
                },
            },
            toMd: {open: '++', close: '++', mixable: true, expelEnclosingWhitespace: true},
            fromMd: {tokenSpec: {name: underlineMarkName, type: 'mark'}},
        }));
};
  • spec — especificação para o ProseMirror;
  • fromMd — configuração de parsing da marcação markdown para a representação dentro do ProseMirror;
  • toMd — configuração para serializar a entidade em marcação markdown.

Por exemplo, abaixo está a configuração da extensão para texto sublinhado. Ela pode ser estendida adicionando uma action com o método addAction:

import {toggleMark} from 'prosemirror-commands';

const undAction = 'underline';

builder
    .addAction(undAction, ({schema}) => ({
        isActive: (state) => Boolean(isMarkActive(state, markType)),
        isEnable: toggleMark(underlineType(schema)),
        run: toggleMark(underlineType(schema)),
      })
  )

Essa action pode ser chamada no código da seguinte forma:

// editor – instância do editor obtida ao chamar useMarkdownEditor
editor.actions.underline.run(),

Na documentação você pode ver a instrução completa para criar uma nova extensão.


Estamos constantemente ampliando os horizontes de uso do nosso editor: agora estamos trabalhando em um plugin para VS Code, que permitirá trabalhar com arquivos md em um modo WYSIWYG conveniente прямо do editor. Também planejamos adicionar um modo móvel completo. Isso permitirá que qualquer usuário trabalhe no nosso editor tendo apenas um celular em mãos.

Nosso editor não surgiu de repente: ele é o resultado do acúmulo de experiência e conhecimento. Temos orgulho de que o editor se baseia totalmente em produtos open source, incluindo ferramentas confiáveis e comprovadas como ProseMirror, CodeMirror, markdown‑it, além dos nossos próprios desenvolvimentos — Diplodoc e Gravity UI.

Você sempre pode contribuir para o desenvolvimento do editor: criar um pull request ou ajudar a resolver os problemas atuais listados na seção Issues. Seu apoio e um olhar novo vão nos ajudar a tornar o editor melhor. E se você considera nosso projeto útil — dê uma estrela no nosso repositório no GitHub; isso é valioso :)

Markdown Editor: editor WYSIWYG e markup baseado no Gravity UI

Sign in to save this post