1212from .yroom_file_api import YRoomFileAPI
1313
1414if TYPE_CHECKING :
15- from typing import Literal , Tuple
15+ from typing import Literal , Tuple , Any
1616 from jupyter_server_fileid .manager import BaseFileIdManager
1717 from jupyter_server .services .contents .manager import AsyncContentsManager , ContentsManager
1818
@@ -22,7 +22,11 @@ class YRoom:
2222 log : Logger
2323 """Log object"""
2424 room_id : str
25- """Room Id"""
25+ """
26+ The ID of the room. This is a composite ID following the format:
27+
28+ room_id := "{file_type}:{file_format}:{file_id}"
29+ """
2630
2731 _jupyter_ydoc : YBaseDoc
2832 """JupyterYDoc"""
@@ -60,9 +64,10 @@ def __init__(
6064 self ._client_group = YjsClientGroup (room_id = room_id , log = self .log , loop = self ._loop )
6165 self ._ydoc = pycrdt .Doc ()
6266 self ._awareness = pycrdt .Awareness (ydoc = self ._ydoc )
67+ _ , file_type , _ = self .room_id .split (":" )
6368 JupyterYDocClass = cast (
6469 type [YBaseDoc ],
65- jupyter_ydoc_classes .get (self . file_type , jupyter_ydoc_classes ["file" ])
70+ jupyter_ydoc_classes .get (file_type , jupyter_ydoc_classes ["file" ])
6671 )
6772 self .jupyter_ydoc = JupyterYDocClass (ydoc = self ._ydoc , awareness = self ._awareness )
6873
@@ -90,6 +95,9 @@ def __init__(
9095 # messages in the message queue to the appropriate handler method.
9196 self ._message_queue = asyncio .Queue ()
9297 self ._loop .create_task (self ._on_new_message ())
98+
99+ # Log notification that room is ready
100+ self .log .info (f"Room '{ self .room_id } ' initialized." )
93101
94102
95103 @property
@@ -157,20 +165,28 @@ async def _on_new_message(self) -> None:
157165 # Handle Awareness messages
158166 message_type = message [0 ]
159167 if message_type == YMessageType .AWARENESS :
160- self .handle_awareness_update (client_id , message [1 :])
168+ self .log .debug (f"Received AwarenessUpdate from '{ client_id } '." )
169+ self .handle_awareness_update (client_id , message )
170+ self .log .debug (f"Handled AwarenessUpdate from '{ client_id } '." )
161171 continue
162172
163173 # Handle Sync messages
164174 assert message_type == YMessageType .SYNC
165175 message_subtype = message [1 ] if len (message ) >= 2 else None
166176 if message_subtype == YSyncMessageSubtype .SYNC_STEP1 :
177+ self .log .info (f"Received SS1 from '{ client_id } '." )
167178 self .handle_sync_step1 (client_id , message )
179+ self .log .info (f"Handled SS1 from '{ client_id } '." )
168180 continue
169181 elif message_subtype == YSyncMessageSubtype .SYNC_STEP2 :
182+ self .log .info (f"Received SS2 from '{ client_id } '." )
170183 self .handle_sync_step2 (client_id , message )
184+ self .log .info (f"Handled SS2 from '{ client_id } '." )
171185 continue
172186 elif message_subtype == YSyncMessageSubtype .SYNC_UPDATE :
187+ self .log .info (f"Received SyncUpdate from '{ client_id } '." )
173188 self .handle_sync_update (client_id , message )
189+ self .log .info (f"Handled SyncUpdate from '{ client_id } '." )
174190 continue
175191 else :
176192 self .log .warning (
@@ -213,7 +229,8 @@ def handle_sync_step1(self, client_id: str, message: bytes) -> None:
213229 try :
214230 # TODO: remove the assert once websocket is made required
215231 assert isinstance (new_client .websocket , WebSocketHandler )
216- new_client .websocket .write_message (sync_step2_message )
232+ new_client .websocket .write_message (sync_step2_message , binary = True )
233+ self .log .info (f"Sent SS2 reply to client '{ client_id } '." )
217234 except Exception as e :
218235 self .log .error (
219236 "An exception occurred when writing the SyncStep2 reply "
@@ -228,7 +245,8 @@ def handle_sync_step1(self, client_id: str, message: bytes) -> None:
228245 try :
229246 assert isinstance (new_client .websocket , WebSocketHandler )
230247 sync_step1_message = pycrdt .create_sync_message (self ._ydoc )
231- new_client .websocket .write_message (sync_step1_message )
248+ new_client .websocket .write_message (sync_step1_message , binary = True )
249+ self .log .info (f"Sent SS1 message to client '{ client_id } '." )
232250 except Exception as e :
233251 self .log .error (
234252 "An exception occurred when writing a SyncStep1 message "
@@ -295,23 +313,22 @@ def write_sync_update(self, message_payload: bytes, client_id: str | None = None
295313
296314 This method can also be called manually.
297315 """
298- # Broadcast the message:
316+ # Broadcast the message
299317 message = pycrdt .create_update_message (message_payload )
300318 self ._broadcast_message (message , message_type = "SyncUpdate" )
301319
302- # Save the file to disk.
303- # TODO: requires YRoomLoader implementation
304- return
320+ # Save the file to disk
321+ self .file_api .schedule_save ()
305322
306323
307324 def handle_awareness_update (self , client_id : str , message : bytes ) -> None :
308325 # Apply the AwarenessUpdate message
309326 try :
310- message_payload = message [1 :]
327+ message_payload = pycrdt . read_message ( message [1 :])
311328 self ._awareness .apply_awareness_update (message_payload , origin = self )
312329 except Exception as e :
313330 self .log .error (
314- "An exception occurred when applying an AwarenessUpdate"
331+ "An exception occurred when applying an AwarenessUpdate "
315332 f"message from client '{ client_id } ':"
316333 )
317334 self .log .exception (e )
@@ -351,7 +368,7 @@ def _broadcast_message(self, message: bytes, message_type: Literal['AwarenessUpd
351368 try :
352369 # TODO: remove this assertion once websocket is made required
353370 assert isinstance (client .websocket , WebSocketHandler )
354- client .websocket .write_message (message )
371+ client .websocket .write_message (message , binary = True )
355372 except Exception as e :
356373 self .log .warning (
357374 f"An exception occurred when broadcasting a "
0 commit comments