11
11
from jupyter_ydoc import ydocs as YDOCS
12
12
from pycrdt_websocket .websocket_server import YRoom
13
13
from pycrdt_websocket .ystore import BaseYStore , YDocNotFound
14
+ from pycrdt import MapEvent
14
15
15
16
from .loaders import FileLoader
16
17
from .utils import JUPYTER_COLLABORATION_EVENTS_URI , LogLevel , OutOfBandChanges
@@ -157,6 +158,7 @@ async def initialize(self) -> None:
157
158
if self .ystore :
158
159
await self .ystore .encode_state_as_update (self .ydoc )
159
160
161
+ self ._document .dirty = False
160
162
self .ready = True
161
163
self ._emit (LogLevel .INFO , "initialize" , "Room initialized" )
162
164
@@ -206,6 +208,7 @@ async def _on_outofband_change(self) -> None:
206
208
return
207
209
208
210
self ._document .source = model ["content" ]
211
+ self ._document .dirty = False
209
212
210
213
def _on_document_change (self , target : str , event : Any ) -> None :
211
214
"""
@@ -222,6 +225,12 @@ def _on_document_change(self, target: str, event: Any) -> None:
222
225
document. This tasks are debounced (60 seconds by default) so we
223
226
need to cancel previous tasks before creating a new one.
224
227
"""
228
+ if target == "state" and isinstance (event , MapEvent ) and list (event .keys .keys ()) == ["dirty" ]:
229
+ # do not write when we are just setting the `dirty` attribute to
230
+ # `False` for the JupyterLab UI. this prevents a save loop, as this
231
+ # is set when the Ydoc is saved.
232
+ return
233
+
225
234
if self ._maybe_save_task and not self ._maybe_save_task .done ():
226
235
# only one `self._maybe_save_task` needs to be running.
227
236
# if this method is called after the save delay, then we need to set
@@ -266,6 +275,7 @@ async def _maybe_save_document(self) -> None:
266
275
}
267
276
))
268
277
await self ._save_task
278
+ self ._document .dirty = False
269
279
self ._emit (LogLevel .INFO , "save" , "Content saved." )
270
280
271
281
if self ._should_resave :
0 commit comments