Page MenuHomePhabricator

Assemble a visual editor compatibe with MMD
Open, NormalPublic

Description

Affects rMMD

We need a client editor that is capable of providing visual editing for mMD. While the browsers do provide a mechanism for editing HTML, it's more often than not flawed and inconsistent.

See: https://medium.engineering/why-contenteditable-is-terrible-122d8a40e480

My idea for a good visual editor shares many of the concerns that this medium post points out very eloquently and I think we need to provide a simple editor that can manage the behaviors we need, without making it excessively complicated or restrictive. Here's a small list of requirements that this editor has:

  • Inline level markup (most notably italic, bold and urls).
  • Block level markup (most notably paragraphs, titles and block quotes)
  • The editor should ship without support for images, embeds, tables, code, or lists (code and lists may be added at a later point, but are secondary to our mission at this point)
  • Consistent behavior importing and exporting mMD.
    • Importing and exporting a valid file without editing it should lead to the content being identical
    • Precedence should be respected.

There are a few really challenging bits in this project, but most of them are alleviated when reducing the scope of the application. Therefore the editor should ship with the most limited feature set that makes sense for us.

We see a visual editor as a subset of a page editor, it is not intended to manage the media, embeds and interactive content that the user wishes to throw at it. It should just provide a balance between a page editor (the one that manages things like where the elements are on the page, whether the elements are images, text, video or other content) and the fact that a user may just want to add a bit of bold text to highlight something in the paragraph they're currently writing. We don't expect the user to create a "block" whenever they wish to add a bit of highlighted text. That would drive the most patient people mad.

But on the other hand, we do not want our editor to manage content that does not "fit" in a string. It shouldn't accept anything but plain text.

Challenges

This are some of the most notable challenges that a visual editor comes across and that need to be properly managed:

  1. Selection. What text the user has currently selected. This gets especially tricky when it goes across multiple lines.
  2. Line breaks. As the medium post highlights, breaking a line is not as trivial as it may seem at first.
  3. Clipboard. Accepting data from the clipboard and sanitizing it is not that easy either.
  4. Performance. In our case this may be secondary, since our editor is intended only for small tidbits to be edited, but the data may become heavy and therefore the system would have to carefully decide when to update the models.

Event Timeline

cesar triaged this task as Normal priority.Mar 31 2020, 12:00 PM
cesar created this task.
cesar created this object with visibility "Public (No Login Required)".
cesar added a parent task: Restricted Maniphest Task.Apr 2 2020, 10:42 AM
cesar added a comment.Apr 16 2020, 2:33 PM

Made some progress here. Assembled a line feeding demo, that seems to be rather decent at doing just that.
Also, have been reading on what the deal with clipboards is: https://stackoverflow.com/questions/2176861/javascript-get-clipboard-data-on-paste-event-cross-browser/6804718#6804718

My latest big progress was that I started maintaining a document as an array of lines, and a line is a type plus an array of inlines. Every inline can have multiple properties, like bold, italics, or something similar.
This allows us to construct a document with minimal effort while maintaining a good level of compatibility.

The benefits of this approach are:

  • Nesting is gone. This may seem like a minor change, but it really means a lot. Without nesting, the document become much simpler to navigate and health-check. There's no longer need to walk several layers deep into the document nor does the selection jump out of three nested elements when moving from an italicized, small, and bold section of text to the next word that does not have any of these properties.
  • Nesting is still gone This also means the precedence of elements is virtually irrelevant. When we select a span that is italicized and bolded and remove the italics from a part of the span, we break the span into three elements, and remove the italics property from just one of the elements. In an environment with nested tags, we would also need to 'un-nest' an element and that would be a mess.
  • Translating documents is simple Since we only have spans with properties, we can always start a span when we hit a new inline modifier. Just removing or adding a property depending on whether the modifier was an opening or a closing modifier. This makes translating documents super easy, barely an inconvenience.
  • Creating a schema is cake a schema for an HTML document basically requires a full fledged implementation of a DOM document to maintain a visual editor. When replacing the system with a series of <div> containing a bunch of <span> each, the code becomes more manageable. We just need the appropriate code to handle adding, removing and editing (in the case of inline elements) of lines and inline elements. We can programmatically cause the model to watch the changes inside inline elements, making the functionality to undo or redo changes really simple.

I have been working on some demonstration code that did a pretty good job at managing the spans. I also created a demo of a piece of code that generates a schema and maintains the schema, now the next step would be to merge the two into one, refactor the code and properly test it.

cesar added a comment.Fri, Jun 12, 3:59 PM

The basic model is set up. This allows our system to store all the relevant data, and generate operations that allow forwarding and rewinding a document. I've been able to divide the editor's model into three components:

  • Document
  • Block
  • Inline

I have also found that there's a strange behavior relating to <span>s when making the editable. It seems that by default, the home key on a keyboard would escape the inline <span> and move the selection to the <div> that represents the block element. Block elements should not be edited directly and only provide structure to the document. Weirdly enough, this behavior seems to be cancelled out when the <span> are styled using CSS to set them as inline-block elements which causes them to function normally.

Here's the gist of the CSS needed to make <span> behave as expected:


span {
	display: inline-block;
	white-space: pre-wrap;
	min-height: 1.2rem;
	font-size: 1.1rem;
	margin: 0;
}