 there is a single upstream copy of @doc in the local_hub daemon.
 
 The user edits @nb.  Periodically we check to see if any changes were made (@nb.dirty) and
-if so, we copy the state of @nb to @doc's live.   We then initiate a sync with upstream.
+if so, we copy the state of @nb to @doc's live.
+
+When @doc changes do to some other user changing something, we compute a diff that tranforms
+the live notebook from its current state to the state that matches the new version of @doc.
+See the function set_nb below.  Incidentally, I came up with this approach from scratch after
+trying a lot of ideas, though in hindsite it's exactly the same as what React.js does (though
+I didn't know about React.js at the time).
 ###
 
 async                = require('async')

        obj.nbformat_minor = @nb.nbformat_minor
        #console.log("to_obj: done", misc.mswalltime(t))
        return obj

    ###
    from_obj: (obj) =>
        if not @nb?
            return
        i = @nb.get_selected_index()
        st = @nb.element.scrollTop()
        @nb.fromJSON(obj)
        @nb.dirty = false
        @nb.select(i)
        @nb.element.scrollTop(st)
    ###

    delete_cell: (index) =>
        @dbg("delete_cell", index)

        opts = @opts = defaults opts,
            sync_interval   : 1000
            cursor_interval : 2000
            read_only : false
        window.s = @
        @element = templates.find(".smc-jupyter-notebook").clone()

        if @opts.read_only
            @readonly = true
            @element.find(".smc-jupyter-notebook-buttons").remove()

        @element.data("jupyter_notebook", @)

        # Jupyter is proxied via the following canonical URL (don't have to guess at the port):
