Hacker News new | past | comments | ask | show | jobs | submit login
Ritzy – A collaborative web-based rich text editor (github.com/ritzyed)
100 points by kylemathews on Aug 23, 2015 | hide | past | favorite | 41 comments



I am the author a recently-launched similar product [1], which also tries to contain the impact of contentEditable by capturing user actions and applying them to the document model in its own code. The main difference is that my system does leave selection (mostly) to the browser, by enabling contentEditable and then capturing events that actually perform changes. Another project of mine [2] does take the approach taken by Ritzy, but for plain-text editing. I consciously didn't go that way this time. Here's why:

- Accessibility. Screen readers don't know what you are doing if the browser doesn't know. They won't communicate your tag soup to the user as an editor interface.

- Cursor motion and selection management hard. Consider right-to-left text and mixed bidirectional text. Getting this right is a big project on its own. You can get the basics working easily, but there's a long tail of hard behavior that you will at some point need.

- Touch interfaces don't interface well with your fake selection. So you'll have to implement your own selection-management interface. Which might not even be possible for some things (like access to the built-in spell checker).

- It isn't needed. Though it also requires some gymnastics, working with the browser's native selection on modern browsers isn't all that hard.

[1]: http://prosemirror.net

[2]: http://codemirror.net


I wrote some collaborative editors and I fully agree on those points. In my opinion, contentEditable is a codebase way too big to replicate in JavaScript (unless you're Google).


Pretty interesting, although reimplementing every single standard text editor behavior can be difficult.

Missing standard text editor behavior that I know of:

- right-clicking on selected text

- dragging-and-dropping selected text

- triple-click to select paragraph

- OS X emacs keybindings: ctrl+n/p/f/b as arrow keys, ctrl+a/e to go to beginning/end of line, ctrl+k/y to cut/paste, etc


i hit the same things on mac, like ctrl-a/e. couldn't jump to the beginning and the end of the file either. i tried various shift key combinations until i finally inserted some special character which i couldn't delete. all this happened within ~20seconds of trying it. not sure what to conclude from it; im still in shock. somehow the language of announcement is not in line with the capabilities of the tool. it suggested something more mature.


undo/redo


I sympathize with anybody who attempts to write an editor that runs in a browser.

If you're going to even entertain the thought of writing an editor I recommend reading Piotrek Koszuliński's article on Medium [1]. There are many pitfalls when writing an editor and the sooner you know of them the better.

On the plus side a new spec for a more low-level version of contentEditable is currently being worked on.[2] It's a complicated undertaking but the task force is making progress. There is participation from all major browser vendors so we have reasons to be optimistic: we may make it out of the Dark Ages of web-based rich editing one day.

[1] https://medium.com/content-uneditable/contenteditable-the-go...

[2] https://medium.com/content-uneditable/fixing-contenteditable...


I've yet to see a good Wysiwyg table editor on the web, especially open source and embeddable. So if you can deliver on that at some point, that would be a big reason for me on why start using. I like minimalistic approaches, but so far the minimalistic approach is usually to just forget about tables, which I don't find acceptable. Emacs Orgmode so far has the cleanest simple approach to this and I'd love to see that replicated.


A nice contribution to the browser-based editor world. I enjoyed perusing the source, which looks pretty clear. There's not much UI, which is perfect for me because I've been looking for a browser-based editor to study.

Tried it on a 2,000 line text file and it caused Chrome 44 on Yosemite to time out close the tab.


Trivial CRDT implementations tend to be memory-heavy and to scale badly, especially after a long editing session. Decreasing the cost of tree traversal requires smart garbage collection, balancing and merging of nodes.


CRDT is heavy on metadata and thus requires some compression and garbage collection techniques. In this implementation, every letter is a JavaScript object. Performance was not an objective, I guess.


Another interesting library built for collaborative editing is Prosemirror. It's also in its infancy stages.

https://github.com/ProseMirror/prosemirror

It's by the guy who created CodeMirror and TernJS.


Which is seeking funding to go open source https://www.indiegogo.com/projects/prosemirror/#/story


Here's a direct link to ProseMirror's collaborative editing demo http://prosemirror.net/demo_collab.html#edit-Example (which is up, and fast)


I'm definitely excited for ProseMirror. If it's as quality as CodeMirror I'll be happily integrating it into everything I build!


What is the file format? WebODF has all the mentioned features, includeing collaborative editing, and in addition it uses OpenDocument Format as the file format.

http://www.webodf.org/demo/


The primary data structure underneath is a "causal tree" CRDT. However, import/export to a JSON structure representing rich text is the "native" format. Example is the source used to create the text in demo instances of the editor:

https://github.com/ritzyed/ritzy-demo/blob/master/src/create...


Since it uses React internally, I wonder how large these documents can grow until the experience gets sluggish. I ask this because React basically requires all of the internal content to be "visited" upon a redraw. But regardless of React, it is still interesting to know the constraints in terms of performance.

Also, instead of just pointing to the code on github, I'd like to get at least a little insight into the architecture of the project, because that is the interesting part, in my opinion. There is a brief description in the readme on github, but it doesn't really paint a clear picture.


The techniques for addressing this are essentially the same for any display technology: Avoid rendering stuff that is not on the screen. This works just fine in React.


But if at every update (every keystroke of the user), the algorithm needs to go through millions of lines of text to determine which ones to show, it is going to be slow, even if eventually only e.g. 50 lines are shown.


You can say the same thing about any text editor built with any technology. This problem is fundamental and the tools to solve it are foundational. It's also no different than a game that doesn't render objects behind the player or obscured by walls. You need to choose data structures that enable efficient visibility queries.

For a game, there's some sophisticated math involved. But for a text editor? You only need to perform basic arithmetic on the scroll position and window height to index in to a vector of lines.

In React, you could render exactly 50 <Line> components and choose which 50 by exactly the same means vim chooses which 50 ncurses lines to show.


This is an interesting project, and I totally understand the desire to eschew the use of contentEditable. But, from what I've seen, handling bidi text and accessibility essentially require using contentEditable as the input mechanism, even if you manage the document yourself.

Does this handle RTL text input and accessibility issues?


It does not. See the comments from Marijn above. I don't know anything about RTL/bidi and accessibility, so I'm not sure if these are intractable problems for Ritzy or not.


It would be nicer if this wasn't tied in to a server implementation, so it could be a standard react component that specifies a couple of handlers as PropTypes. Would make it much more easier for people to reuse it!



The demo is down, but it doesn't even have bullets?

If you're looking for a good collaborative rich text editor on the web, look up Quill or ProseMirror.


Quill is good but not great. I looked at Quill extensively before embarking on writing Ritzy. Its collaboration features are minimal -- as far as I know there is no way to do collaboration across two browsers i.e. there is no working and/or available server-side component to coordinate the OT deltas produced by Quill (both editors have to be on the same page, which is somewhat pointless). I believe the authors were moving towards doing this via compatibility with ShareJS, but never quite got there. Offline support will be a pain to add with OT.

ProseMirror looks cool. I wish @Marijn the best with it! Not sure about ProseMirror's offline capabilities. I hope he adds multiple cursors and selections like Google Docs (and Ritzy).


Awesome project, but I misread the title and expected this to be a collaborative real-time editor built for React...


Looks very promising. Been looking for Google Doc alternative for sometime now.

I realize that there is a commercial service aspect that is being worked on to provide server side hosting, however, how easy would it be to self host this on sandstorm.io?


Pretty easy! Check out the demo source code: https://github.com/ritzyed/ritzy-demo


Very well done.

On mac you use cmd-C, cmd-V instead of control-C, control-V.

There is a bug that causes new line to function incorrectly when the cursor is on the first line of the document. New line on other lines work fine. (Safari on Mac)


Yup, I noticed that bug! It's fixed now (https://github.com/ritzyed/ritzy/commit/c42769d27730a11fe0d4...) (demo is not updated yet). It should be easy to add the Mac key bindings -- I need to find a Mac to test on.


Demo is updated now to fix the newline bug.


You guys should publish a content-editable abstraction that fixes regular cases first and then build stuff like collaborative editor on top.


This looks interesting, sadly I'm getting an internal server error from the demo.


On iPhone I can't get focus to edit the demo. That is slightly concerning.


Demo is crashing :(


Author here. Thanks for posting this. I had posted it earlier as a ShowHN (https://news.ycombinator.com/item?id=10087162), but without much notice, so I'm ecstatic to see lots of comments here. Great feedback.

Some responses to the various comments:

1) This is a very early release. I'm aware of all the missing functionality, and the README is pretty clear about all the missing stuff, including undo/redo, bullets, numbered lists, tables, and more. I don't have access to a Mac, so I wouldn't expect any of the Mac keybindings to work. That should be pretty easy to solve though.

2) Piotrek Koszuliński's article was really eye opening. Being completely new to the field of editors, and even new to Javascript (this is my first major Javascript project -- my background is mostly enterprise Java and some Scala), I defer to experts in this field like Piotrek and Marijn, the author of Codemirror and Prosemirror. I'm honored that Marijn even took the time to comment :-) It's possible that Ritzy could still serve as a useful tool within some narrower contexts that do not require handling the long tail of hard behavior mentioned (accessibility and RTL/bidi). OR, with the appropriate expertise, that some of that functionality could be added. Re. touch interfaces, fair point -- my thought was that could be worked around (e.g. by an alternate native component on touch interfaces that talked the same CRDT language -- I believe that's the approach that Google Docs took with editing Docs on mobile). Currently on mobile, basic cursor navigation by text, keyboard / text input, and viewing others' changes in real-time appears to work pretty well (better than I expected actually!). Selections don't work at all.

@marijn: how difficult would it be to add multiple cursors and selections to Prosemirror? I notice that currently the Prosemirror collaboration demo does not show cursors or selections, which is pretty key to a great user experience.

3) @gritzo (the author of Swarm.js, a key library used in Ritzy) mentions that the implementation uses a JS object for each char. That is true and it's very heavy. My initial goal was correctness and as a POC, with something easy to understand and debug. Optimization can come later, if indeed there is interest in taking this general approach further.

4) Recent changes to Ritzy (not yet pushed to the demo page) make each cursor, line, and selection a separate React component within a hierarchy. This makes navigation, rendering and selections pretty quick. However, the editor has never been tested against documents larger than a few hundred lines. My gut feeling is that the limiting factor right now is not React, but the "fat" CRDT data structure I used. It gets particularly bad when a lot of characters are deleted. There is no tombstone clearing logic yet.

5) It should be pretty easy to strip out the server-side stuff and make it client-side only. You lose the "killer feature" of Docs-style collaborative editing though. The server-side is pretty simple to self-host -- check out the demo source code @ https://github.com/ritzyed/ritzy-demo for an example.

I'll try to get the demo back up and running. Its hosted on the smallest (free) tier of Openshift and I've done zero server-side optimization. I'm sure it didn't take much traffic to kill it. Any other suggestions for (free) hosting for NodeJS projects?

I'll also try to write up a design doc over the next few days for those who requested that / are more interested in those aspects.

Thanks again for all the comments / feedback!


> how difficult would it be to add multiple cursors and selections to Prosemirror? I notice that currently the Prosemirror collaboration demo does not show cursors or selections, which is pretty key to a great user experience.

If you mean Sublime Text-style multiple cursors, that'd be very hard. CodeMirror's contentEdtiable-base mobile backend does support them, but that's only possible because it already contains a full implementation of cursor/selection for the non-mobile version.

If, on the other hand, you mean showing the cursors from other people working on a collaborative document, that isn't very hard, it just involves inserting widgets and marked ranges at the right places.


> If, on the other hand, you mean showing the cursors from other people working on a collaborative document

This is what I meant. Good that its not very hard. Though I'm curious: how do you know what the "right place" actually is i.e. don't the coordinates of the cursor position depend on the rendering of the text which is under the browser's control?

What about showing multiple selections from other authors like Google Docs (and Ritzy) does?


> don't the coordinates of the cursor position depend on the rendering of the text which is under the browser's control?

Yes, but you can ask the browser (using `getBoundingClientRect` and `getClientRects`, which also exist for text range object on all halfway modern browsers.) Or you can insert the caret element inline at the correct place.

What would be hard about showing a selection from another person?


Not "hard" as such. Just requires a completely separate code path than the local cursor and selection machinery. That isn't too much of a barrier though... I look forward to its implementation in Prosemirror!




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: