Hello everyone,
As I'm building out Quill 1.0, I'm running into some challenges that is making me consider an eventual update to
rich-text. This is not a proposal--just wanted to share some thoughts and solicit feedback.
The main issue is that rich-text is a 1-dimensional data format. Without any depth, it's always been awkward to represent content presented by a tree structure like the DOM. The current workaround is the use the newline character as a way to format lines. For example:
[
{ insert: 'Grocery List\nEggs' },
{ insert: '\n', { list: true } },
{ insert: 'Milk' },
{ insert: '\n', { list: true } },
{ insert: 'Note: Get quarters\n' }
]
A couple of side effects from this dependency on newlines:
- Every line needs to end with a newline
- The empty document is a newline
- Lookahead is required when building the DOM representation
This actually isn't as bad as all the problem it solves but there is content that I would eventually like to add to Quill that currently cannot be represented and maintain OT properties out of the box, such as tables.
One thought to solve the newline annoyance is a two dimensional. The type would have a linear array of "sections", and each section's contents is essentially an old rich-text. For example:
[
{ section: 'paragraph', insert: [{ insert: 'Grocery List' }] },
{ section: 'list', insert: [{ insert: 'Eggs\nMilk' }] },
{ section: 'paragraph', insert: [{ insert: 'Note: get quarters' }] }
]
Deleting or retaining (without formatting) an entire section would look similar to the current rich-text delete and retain instructions. Modifying text within an element could look something like this:
[
{ retain: 1 },
{
retain: [ // A standard rich-text delta as the retain value would indicate subsection modification
{ retain: 4, { bold: true } }
]
},
{ retain: 1, { section: 'footnote' } }
]
The above instruction would keep the first paragraph, bold "Eggs" and change the last line "Note: Get quarters" into a list. Note this instruction will not cause a merger with the previous list. Also we can simultaneously change subsection content as well as the section type itself.
Most content in a rich text document is either block level or inline level so the two levels would map this nicely. There does not appear to be much added complexity (one area where I'd definitely like some feedback) since the subsections behave the same way as sections themselves. The most problematic conflict I could think of is modifying a section that a remote user deletes, but this the same case as formatting a letter that a remote user deletes, which rich-text already handles (ignores the format for user 1 and deletes for formatted character for user 2).
But this format actually still does not solve the problem of representing tables since tables have 3 levels (tables, rows, cells).
The obvious next step then is the make this "sections" idea recursive. Now if the assumption that going from 1 level to 2 is not much more complexity holds, it should also hold from 2 to N with just a one difference I can currently think of:
The depth level is now variable--it's now neither always 1 level deep like rich-text is currently nor always 2 levels deep like the non-recursive proposal above. So in theory a user could get an instruction that modifies the 3rd level of a section but he/she currently only sees two levels for that particular section. I'm not sure this is actually possible though given ShareJS's central server architecture (if someone could come up with a counterexample that would put this question to rest). One solution or just to limit the complexity could be that a given section could also declare an immutable integer depth. This would mean we could no longer change section types of unequal depths so no turning a list into a table or a table into a paragraph (which may also mean declaring the section types and depths ahead of time?).
I only briefly looked at the json0 ottype and am not sure it could solve this multilevel rich text format problem (one issue is a json0 document is an object and not an array but I see there was some discussion about representing as an array but not sure where that landed). Also not sure if this use case is cutting across the complexity boundaries that the new ShareJS wants to avoid.
If my optimism on the complexity of adding multiple levels to rich-text holds then this begs the question: can sections be analogous to DOM nodes and use this to OT a DOM tree? Has anyone used the json0 ottype to do this? Is one of the goals of the new json ottype to synchronize DOM trees?
Thanks in advance for any feedback or thoughts.