Editor Markdown: un editor WYSIWYG y de marcado construido sobre Gravity UI

TL;DR

¡Hola! Me llamo Сергей Махнаткин, trabajo como desarrollador en el equipo de User Experience en Yandex Cloud. El año pasado escribimos sobre nuestro sistema de diseño y la biblioteca de componentes Gravity UI. Desde entonces, el sistema se ha actualizado en varias ocasiones y ha incorporado nuevas funciones, y hoy quiero hablar de una herramienta nueva: Markdown Editor, que simplifica notablemente el trabajo con la documentación.

Hablaremos de la historia de creación de la interfaz de usuario, de particularidades de la arquitectura y de detalles técnicos de integración y del desarrollo de extensiones propias, y después: por qué todo esto está disponible como código abierto.

Por cierto, puedes probar la herramienta aquí:

Por qué necesitamos nuestro propio Markdown Editor

Para que sea cómodo almacenar y estructurar información corporativa, desarrollamos la plataforma Wiki, que permite crear bases de conocimiento. Además de la base de conocimiento, fuimos desarrollando enfoques de documentación como Docs as Code, donde la documentación y el código conviven lado a lado en un almacenamiento de archivos (.md). Así surgió la plataforma Diplodoс.

Lo que une a Wiki y a Diplodoc es que ambas plataformas trabajan con un dialecto de markdown: Yandex Flavored Markdown (YFM), que se usa en Nebius, Bitrix, DoubleCloud, Mappable, Meteum.

Con el tiempo vimos que hay dos grupos de usuarios que se imaginan de manera distinta el proceso de crear y editar texto. Unos prefieren ver de inmediato el resultado final mientras trabajan, como en MS Word, Confluence o Notion. Otros solo confían en el marcado y prefieren dar formato a las páginas con markdown. No encontramos bibliotecas conocidas que trabajen simultáneamente en modo WYSIWYG/markdown. Por ejemplo, Notion es solo WYSIWYG, y en los editores de código suele haber solo markdown y un modo de vista previa.

Desarrollamos un editor markdown que puede trabajar simultáneamente en dos modos: visual (WYSIWYG) y modo de marcado (markdown). En el primer modo, los iconos de la barra ayudan a aplicar el formato, y en el segundo los usuarios pueden editar manualmente el código markdown. Además, nuestra solución guarda el documento como un archivo md, independientemente de qué modo se haya usado para crearlo.

Así se ve el editor visual, donde el texto se puede formatear con botones:

Full screen image

Y así, el modo de marcado, en el que los elementos de formato se обозначаются mediante símbolos especiales:

Full screen image

Funciones de Markdown Editor en Gravity UI

El editor cumple el estándar CommonMark, soporta el lenguaje markdown estándar y el lenguaje YFM. También añadimos la posibilidad de ampliar la sintaxis con otros dialectos de markdown, por ejemplo GitHub Flavored Markdown. A la vez, el editor permite cambiar del modo de marcado al modo WYSIWYG, y el propio documento se almacenará como marcado md o como md extendido (por ejemplo, en el caso de YFM).

Extensiones

De entrada, el editor viene con muchas extensiones y ajustes integrados. Por ejemplo, diagramas Mermaid y bloques HTML:

Full screen image
Full screen image

Intentamos que el núcleo del editor fuera fácil de ampliar. Los desarrolladores pueden crear su propia extensión o funcionalidad adicional para:

  • añadir nuevas entidades: bloques o modificadores de texto;
  • configurar adicionalmente el parser de markdown;
  • añadir actions que permitan trabajar con el editor desde fuera;
  • enriquecer la funcionalidad de la interfaz, por ejemplo mostrar un menú de comandos disponibles al introducir una barra (/);
  • modificar el comportamiento actual, por ejemplo insertar imágenes y archivos y subirlos al almacenamiento.

Aquí tienes una serie de ejemplos de extensiones de este tipo que desarrollamos para nuestro Wiki:

  • modo de edición colaborativa;
Full screen image
Full screen image
Full screen image
Full screen image
  • inclusiones (includes);
Full screen image
  • estructura de la sección;
Full screen image
  • secciones para crear una cuadrícula cómoda;
Full screen image
  • modo markdown con vista previa;
Full screen image
  • y muchos otros.
Full screen image

El marcado puede transformarse automáticamente. Si prefieres trabajar sin ratón, en el modo de editor visual hay símbolos especiales que permiten aplicar marcado прямо en el texto. Por ejemplo, ** ponen el texto en negrita en modo WYSIWYG. Con estos símbolos se puede formatear texto, crear código inline y en bloque.

También se puede abrir el menú de extensiones introduciendo el símbolo /.

Full screen image

Presets

El editor permite configurar la barra de herramientas para cada proyecto por separado, pero se entrega con una serie de configuraciones ya preparadas: presets.

Editor sin presets:

Full screen image

El preset CommonMark ofrece soporte para los elementos estándar de markdown: negrita, cursiva, encabezados, listas, enlaces, citas y bloques de código.

Full screen image

En el preset por defecto también aparece texto tachado, y además una tabla cuyas celdas solo pueden contener texto. Este preset corresponde al markdown-it estándar.

Full screen image

Como se dijo выше, el editor también soporta YFM, por lo que se integra perfectamente con Diplodoc. En el preset YFM aparecen elementos adicionales: tablas avanzadas, inserción de archivos e imágenes, checkboxes, cut, pestañas, fuente monoespaciada.

Full screen image

El preset completo contiene aún más elementos.

Full screen image

Arquitectura

La base del editor en modo WYSIWYG es la conocida biblioteca ProseMirror, y para el marcado se usa CodeMirror. ProseMirror soporta edición con formato, mientras que CodeMirror es adecuado para situaciones en las que es necesario trabajar con texto sin marcar.

Elegimos estas bibliotecas porque están desarrolladas por el mismo autor, son coherentes en arquitectura y en enfoques de implementación, cuentan con el respaldo de una gran comunidad, se usan en muchos editores y están bien optimizadas para trabajar con texto. Por ejemplo: el sistema de transacciones para aplicar cambios al documento, decoraciones para la vista, virtualización del DOM o soporte de sintaxis para muchos lenguajes de programación.

Integración

Nuestro editor se conecta fácilmente como un hook de 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 de la biblioteca uikit de GravityUI. Esto garantiza que toda la interfaz sea consistente y cumpla con guías de estilo unificadas. El uso de estos componentes también asegura un alto grado de coherencia y familiaridad para los usuarios, lo que hace que trabajar con el editor sea aún más cómodo.

Tenemos instrucciones detalladas aquí sobre cómo integrar el editor en una app React, y también sobre cómo conectar distintos tipos de extensiones: por ejemplo, YandexGPT, MermaidLaTeX.

Integración de extensiones personalizadas

En el editor ya está integrada una serie de extensiones adicionales. Pero si esto no es suficiente, los desarrolladores pueden añadir sus propias extensiones al modo WYSIWYG del editor. Ya hablamos arriba de lo que pueden aportar las extensiones.

Si quieres añadir un bloque nuevo o un modificador de texto, primero debes configurar la instancia interna de markdown-it con el método configureMd. Después hay que añadir conocimiento sobre la nueva entidad con los métodos addNode o addMark, pasando el nombre de la entidad y una función callback que devuelve un objeto con tres campos obligatorios:

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 — especificación para ProseMirror;
  • fromMd — configuración de parseo del marcado markdown a la representación interna de ProseMirror;
  • toMd — configuración para serializar la entidad a marcado markdown.

Por ejemplo, abajo se muestra la configuración de la extensión para texto subrayado. Se puede ampliar añadiendo una acción (action) con el 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)),
      })
  )

Esa acción se puede llamar en el código de la siguiente manera:

// editor: instancia del editor obtenida como resultado de llamar a useMarkdownEditor
editor.actions.underline.run(),

En la documentación puedes ver la guía completa para crear una nueva extensión.


Estamos ampliando constantemente los horizontes de uso de nuestro editor: ahora trabajamos en un plugin para VS Code, que permitirá trabajar con archivos md en un cómodo modo WYSIWYG directamente desde el editor. También planeamos añadir un modo móvil completo. Esto permitirá que cualquier usuario trabaje en nuestro editor teniendo a mano solo un teléfono móvil.

Nuestro editor no apareció de un día para otro: es el resultado de acumular experiencia y conocimientos. Nos enorgullece que el editor se base полностью en productos de código abierto, incluidos los instrumentos fiables y probados ProseMirror, CodeMirror, markdown-it, así como nuestros propios desarrollos: Diplodoc y Gravity UI.

Siempre puedes contribuir al desarrollo del editor: crear un pull request o ayudar a resolver los problemas actuales listados en la sección Issues. Tu apoyo y una mirada fresca nos ayudarán a hacer el editor mejor. Y si consideras útil nuestro proyecto, ponle una estrella a nuestro repositorio en GitHub, es valioso :)

Editor Markdown: un editor WYSIWYG y de marcado construido sobre Gravity UI

Sign in to save this post