Content Editor

The Content Editor is a UI component that provides a WYSIWYG editing experience for GitLab Flavored Markdown (GFM) in the GitLab application. It also serves as the foundation for implementing Markdown-focused editors that target other engines, like static site generators.

We use tiptap 2.0 and ProseMirror to build the Content Editor. These frameworks provide a level of abstraction on top of the native contenteditable web technology.

Architecture remarks

At a high level, the Content Editor:

  • Imports arbitrary Markdown.
  • Renders it in a HTML editing area.
  • Exports it back to Markdown with changes introduced by the user.

The Content Editor relies on the Markdown API endpoint to transform Markdown into HTML. It sends the Markdown input to the REST API and displays the API’s HTML output in the editing area. The editor exports the content back to Markdown using a client-side library that serializes editable documents into Markdown.

Content Editor high level diagram

Check the Content Editor technical design document for more information about the design decisions that drive the development of the editor.

NOTE: We also designed the Content Editor to be extensible. We intend to provide more information about extension development for supporting new types of content in upcoming milestones.

GitLab Flavored Markdown support

The GitLab Flavored Markdown extends the CommonMark specification with support for a variety of content types like diagrams, math expressions, and tables. Supporting all GitLab Flavored Markdown content types in the Content Editor is a work in progress. For the status of the ongoing development for CommonMark and GitLab Flavored Markdown support, read:

Usage

To include the Content Editor in your feature, import the createContentEditor factory function and the ContentEditor Vue component. createContentEditor sets up an instance of tiptap’s Editor class with all the necessary extensions to support editing GitLab Flavored Markdown content. It also creates a Markdown serializer that allows exporting tiptap’s document format to Markdown.

createContentEditor requires a renderMarkdown parameter invoked by the editor every time it needs to convert Markdown to HTML. The Content Editor does not provide a default value for this function yet.

NOTE: The Content Editor is in an early development stage. Usage and development guidelines are subject to breaking changes in the upcoming months.

<script>
import { GlButton } from '@gitlab/ui';
import { createContentEditor, ContentEditor } from '~/content_editor';
import { __ } from '~/locale';
import createFlash from '~/flash';

export default {
  components: {
    ContentEditor,
    GlButton,
  },
  data() {
    return {
      contentEditor: null,
    }
  },
  created() {
    this.contentEditor = createContentEditor({
      renderMarkdown: (markdown) => Api.markdown({ text: markdown }),
    });

    try {
      await this.contentEditor.setSerializedContent(this.content);
    } catch (e) {
      createFlash(__('There was an error loading content in the editor'), e);
    }
  },
  methods: {
    async save() {
      await Api.updateContent({
        content: this.contentEditor.getSerializedContent(),
      });
    },
  },
};
</script>
<template>
  <div>
    <content-editor :content-editor="contentEditor" />
    <gl-button @click="save()">Save</gl-button>
  </div>
</template>

Call setSerializedContent to set initial Markdown in the Editor. This method is asynchronous because it makes an API request to render the Markdown input. getSerializedContent returns a Markdown string that represents the serialized version of the editable document.