TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Build your own Google Docs with GoInstant's new OT API

53 点作者 jhchen大约 11 年前

5 条评论

adityab大约 11 年前
This is nice!<p>WebODF[1] developer here. Commenting because the project is doing this sort of thing and I have decent experience writing OT. I&#x27;ll give a short explanation of how Operation Transformation works.<p>&quot;Build your own Google Docs&quot; is a slightly brave claim - &quot;build your own Etherpad&quot; would be more accurate.<p>This is because their operation set is very simple; for rich text documents the operations simply do not contain enough information to transform them against other ops. At the moment, their API includes 3 operations:<p><pre><code> * Insert * Format * Delete </code></pre> What I don&#x27;t see:<p>Something like MoveCursor, for a start. From the source code of the markdown editor, it appears that cursor position synchronization is handled outside of the OT realm, likely by some other signalling. This is bad because the cursor positions do not get transformed at all - you can see this if you try to collaboratively edit on the same line in the demo - your cursor jumps around unwieldily.<p>Why this happens:<p>Imagine two users A and B concurrently editing inside a paragraph (a | denotes a cursor, belonging to B):<p><pre><code> &lt;p&gt;abc|def&lt;&#x2F;p&gt; </code></pre> Now suppose A inserts &quot;123&quot; at position 3 (after c), and B tries to move his&#x2F;her cursor to the right by 1 step. Assume that A&#x27;s operation reaches the server first. Then server&#x27;s and A&#x27;s state is:<p><pre><code> &lt;p&gt;abc123|def&lt;&#x2F;p&gt; </code></pre> because B&#x27;s cursor is pushed to the right by the preceding text.<p>Then B&#x27;s operation &quot;move cursor to position 4 (after the d) reaches the server. But A&#x27;s operation has not reached B yet. The state on B becomes:<p><pre><code> &lt;p&gt;abcd|ef&lt;&#x2F;p&gt; </code></pre> And the state on the server becomes:<p><pre><code> &lt;p&gt;abc1|23def&lt;&#x2F;p&gt; </code></pre> Now B&#x27;s op is relayed to A, and A&#x27;s to B, and A&#x27;s state becomes:<p><pre><code> &lt;p&gt;abc1|23def&lt;&#x2F;p&gt;, </code></pre> and B&#x27;s state becomes:<p><pre><code> &lt;p&gt;abc123d|&lt;&#x2F;p&gt;. </code></pre> because there is no OT for &quot;MoveCursor&quot; against &quot;InsertText&quot; -- in fact &quot;MoveCursor&quot; is not even an op here. This is an inconsistent state, which is basically a broken editing session (should never happen). Fixing this up by passing around diffs is not allowed (in a strict sense); diffing is not controllable OT.<p>WebODF uses stateless client-side OT, so any server is just mostly a dumb operation relayer. See what the OT logic for these two operations does: <a href="https://github.com/kogmbh/WebODF/blob/master/webodf/lib/ops/OperationTransformMatrix.js#L537" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;kogmbh&#x2F;WebODF&#x2F;blob&#x2F;master&#x2F;webodf&#x2F;lib&#x2F;ops&#x2F;...</a><p>If the &quot;InsertText&quot; operation&#x27;s position was lesser than the concurrent &quot;MoveCursor&quot; position, &quot;MoveCursor&quot; is <i>transformed</i> to increment it&#x27;s position by the &quot;InsertText&quot; operation&#x27;s text length. This ensures that the cursor, while moving, will end up at the <i>intended</i> position within the word you were looking at. (you&#x27;ll notice that &quot;MoveCursor&quot; has a &#x27;length&#x27; parameter in WebODF because a cursor also represents a user selection, yes it has colored collaborative selections).<p>OT&#x27;d process:<p><pre><code> &lt;p&gt;abc&lt;cursorB&#x2F;&gt;def&lt;&#x2F;p&gt; </code></pre> A inserts &quot;123&quot;, A&#x27;s state is:<p><pre><code> &lt;p&gt;abc123&lt;cursorB&#x2F;&gt;def&lt;&#x2F;p&gt; </code></pre> B moves cursor to position 4, B&#x27;s state is:<p><pre><code> &lt;p&gt;abcd&lt;cursorB&#x2F;&gt;def&lt;&#x2F;p&gt; </code></pre> A gets B&#x27;s op, <i>transforms</i> it to increment position by &quot;123&quot;.length = 3, state becomes:<p><pre><code> &lt;p&gt;abc123d&lt;cursorB&#x2F;&gt;ef&lt;&#x2F;p&gt; </code></pre> B gets A&#x27;s op, applies it because moving a cursor doesn&#x27;t affect text insertion, state becomes:<p><pre><code> &lt;p&gt;abc123d&lt;cursorB&#x2F;&gt;ef&lt;&#x2F;p&gt; </code></pre> Hooray, Eventual Consistency!<p>Any OT implementation is full of such <i>opinionated</i> enforcements and tradeoffs. It should make some assumptions about the editing UX and make sure that this is preserved when doing realtime concurrent editing; ignoring that and <i>only</i> going for eventual consistency will result in situations where the user cannot look at where they are typing.<p>WebODF has many such operations (~23) for each kind of edit: &quot;SplitParagraph&quot;, &quot;InsertText&quot;, &quot;ApplyHyperlink&quot;, &quot;ApplyDirectStyling&quot;, etc at last count. A large number of them are supported in collaborative mode, i.e. they have OT written. And this is no small task, because <i>each operation needs to have a transform written against every other operation in the set of all operations, including itself</i>. It does help that not all operations interfere with others, so the OT matrix is somewhat sparse. :-)<p>Fact: Any complex rich text editing framework will need to have it&#x27;s own customized implementation of OT. We&#x27;re dealing with the Open Document Format, we have Operations that are significantly tailored to it.<p>I feel that GoInstant&#x27;s API is a nice thing! But it needs: 1. More operations. 2. Purely client-side OT (not strictly required, but very very useful).<p>Shameless plug: WebODF is a FOSS project with ~3 fulltime developers (including me) working on it. We are also working with the ODF change-tracking subcommittee to standardise things. We need more contributors and&#x2F;or donations. :)<p><pre><code> [1] http:&#x2F;&#x2F;webodf.org&#x2F; </code></pre> EDIT 1: Sorry, this post became longer than I had wished. I hope it helps anyone understand how OT works. Also, &#x27;gratulations to GoInstant for launching this. :)<p>EDIT 2: Reformatting post for readability.
评论 #7675731 未加载
评论 #7675442 未加载
habosa大约 11 年前
An Operational Transform API has always been my &quot;maybe when I have more time and knowledge&quot; idea. I&#x27;m glad to see someone else doing it and I hope I have a chance to use it. OT is the magic behind Google Docs, and it&#x27;s one of those really elegant protocols that most CS people instantly appreciate when it&#x27;s explained to them.<p><a href="http://en.wikipedia.org/wiki/Operational_transformation" rel="nofollow">http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Operational_transformation</a>
评论 #7674780 未加载
amix大约 11 年前
I can recommend this article that explains OT really well: <a href="http://www.codecommit.com/blog/java/understanding-and-applying-operational-transformation" rel="nofollow">http:&#x2F;&#x2F;www.codecommit.com&#x2F;blog&#x2F;java&#x2F;understanding-and-applyi...</a>
bsoft16384大约 11 年前
Google Drive Realtime API developer here. <a href="https://developers.google.com/drive/realtime/" rel="nofollow">https:&#x2F;&#x2F;developers.google.com&#x2F;drive&#x2F;realtime&#x2F;</a><p>If you&#x27;re trying to build your own Google Docs, you might want to consider using the Drive Realtime API, which is based on the same technology as Google Docs.<p>The Drive Realtime API has a wide range of mutations built in for common data structures: - Maps (last writer wins) - Lists (insert, delete, overwrite) - Strings (insert, delete, overwrite)<p>We also support arbitrary data structures including trees and graphs.<p>For modeling cursors, we have &quot;Index References&quot; which are effectively pointers to a specific element in a string or list. You can move them around on the client side and we have all of the transformation logic worked out so that concurrent edits don&#x27;t make them point at the wrong position.<p>Because the data is stored in Google Drive, you can create, share and organize files using the standard functions in the Google Drive API. There&#x27;s also an easy-to-use REST API for importing and exporting data as JSON.
amix大约 11 年前
Are there any great open-source implementations of OT? Last time I looked there were some, but the quality and popularity of them was questionable.
评论 #7674728 未加载
评论 #7674755 未加载
评论 #7675507 未加载