diff --git a/jupyter_collaboration/app.py b/jupyter_collaboration/app.py index 2c35982e..e36f513b 100644 --- a/jupyter_collaboration/app.py +++ b/jupyter_collaboration/app.py @@ -8,7 +8,7 @@ from pycrdt_websocket.ystore import BaseYStore from traitlets import Bool, Float, Type -from .handlers import DocSessionHandler, YDocWebSocketHandler +from .handlers import DocForkHandler, DocDeleteHandler, DocMergeHandler, DocSessionHandler, YDocWebSocketHandler from .loaders import FileLoaderMapping from .stores import SQLiteYStore from .utils import AWARENESS_EVENTS_SCHEMA_PATH, EVENTS_SCHEMA_PATH @@ -109,6 +109,27 @@ def initialize_handlers(self): }, ), (r"/api/collaboration/session/(.*)", DocSessionHandler), + ( + r"/api/collaboration/fork_room/(.*)", + DocForkHandler, + { + "ywebsocket_server": self.ywebsocket_server, + } + ), + ( + r"/api/collaboration/merge_room", + DocMergeHandler, + { + "ywebsocket_server": self.ywebsocket_server, + } + ), + ( + r"/api/collaboration/delete_room", + DocDeleteHandler, + { + "ywebsocket_server": self.ywebsocket_server, + } + ), ] ) diff --git a/jupyter_collaboration/handlers.py b/jupyter_collaboration/handlers.py index 81b64b3d..af12972a 100644 --- a/jupyter_collaboration/handlers.py +++ b/jupyter_collaboration/handlers.py @@ -6,13 +6,14 @@ import asyncio import json import time -import uuid +from uuid import uuid4 from typing import Any from jupyter_server.auth import authorized from jupyter_server.base.handlers import APIHandler, JupyterHandler from jupyter_ydoc import ydocs as YDOCS -from pycrdt_websocket.websocket_server import YRoom +from pycrdt import Doc, Map +from pycrdt_websocket.yroom import YRoom from pycrdt_websocket.ystore import BaseYStore from pycrdt_websocket.yutils import YMessageType, write_var_uint from tornado import web @@ -31,7 +32,7 @@ YFILE = YDOCS["file"] -SERVER_SESSION = str(uuid.uuid4()) +SERVER_SESSION = str(uuid4()) class YDocWebSocketHandler(WebSocketHandler, JupyterHandler): @@ -404,3 +405,119 @@ async def put(self, path): ) self.set_status(201) return self.finish(data) + + +class DocForkHandler(APIHandler): + """ + Jupyter Server's handler to fork a document. + """ + + auth_resource = "contents" + + def initialize( + self, + ywebsocket_server: JupyterWebsocketServer, + ) -> None: + self._websocket_server = ywebsocket_server + + @web.authenticated + @authorized + async def put(self, room_id): + """ + Creates a fork of a root document and returns its ID. + """ + idx = uuid4().hex + + root_room = await self._websocket_server.get_room(room_id) + update = root_room.ydoc.get_update() + fork_ydoc = Doc() + fork_ydoc.apply_update(update) + fork_room = YRoom(ydoc=fork_ydoc) + self._websocket_server.add_room(idx, fork_room) + root_room.fork_ydocs.add(fork_ydoc) + data = json.dumps({ + "sessionId": SERVER_SESSION, + "roomId": idx, + }) + self.set_status(201) + return self.finish(data) + + +class DocMergeHandler(APIHandler): + """ + Jupyter Server's handler to merge a document. + """ + + auth_resource = "contents" + + def initialize( + self, + ywebsocket_server: JupyterWebsocketServer, + ) -> None: + self._websocket_server = ywebsocket_server + + @web.authenticated + @authorized + async def put(self): + """ + Merges back a fork into a root document. + """ + model = self.get_json_body() + fork_roomid = model["fork_roomid"] + root_room = await self._websocket_server.get_room(model["root_roomid"]) + root_ydoc = root_room.ydoc + idx = f"fork_{fork_roomid}" + root_state = root_ydoc.get("state", type=Map) + if idx in root_state: + del root_state[idx] + else: + self.set_status(404) + raise RuntimeError(f"Could not find root document fork with ID: {fork_roomid}") + fork_room = await self._websocket_server.get_room(fork_roomid) + fork_ydoc = fork_room.ydoc + fork_update = fork_ydoc.get_update() + root_ydoc.apply_update(fork_update) + root_room.fork_ydocs.remove(fork_ydoc) + fork_state = fork_ydoc.get("state", type=Map) + fork_state["merge"] = fork_roomid + #self._websocket_server.delete_room(name=fork_roomid) + self.set_status(200) + + +class DocDeleteHandler(APIHandler): + """ + Jupyter Server's handler to delete a document. + """ + + auth_resource = "contents" + + def initialize( + self, + ywebsocket_server: JupyterWebsocketServer, + ) -> None: + self._websocket_server = ywebsocket_server + + @web.authenticated + @authorized + async def delete(self): + """ + Deletes a forked document. + """ + model = self.get_json_body() + fork_roomid = model["fork_roomid"] + root_room = await self._websocket_server.get_room(model["root_roomid"]) + root_ydoc = root_room.ydoc + idx = f"fork_{fork_roomid}" + root_state = root_ydoc.get("state", type=Map) + if idx in root_state: + del root_state[idx] + else: + self.set_status(404) + raise RuntimeError(f"Could not find root document fork with ID: {fork_roomid}") + fork_room = await self._websocket_server.get_room(fork_roomid) + fork_ydoc = fork_room.ydoc + root_room.fork_ydocs.remove(fork_ydoc) + fork_state = fork_ydoc.get("state", type=Map) + fork_state["delete"] = fork_roomid + #self._websocket_server.delete_room(name=fork_roomid) + self.set_status(200) diff --git a/package.json b/package.json index 1bad1fa0..6b8906de 100644 --- a/package.json +++ b/package.json @@ -65,5 +65,8 @@ "stylelint-prettier": "^3.0.0", "typedoc": "~0.23.28", "typescript": "~5.0.4" + }, + "resolutions": { + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc" } } diff --git a/packages/collaboration-extension/package.json b/packages/collaboration-extension/package.json index e8812ce1..76f3b77a 100644 --- a/packages/collaboration-extension/package.json +++ b/packages/collaboration-extension/package.json @@ -55,7 +55,7 @@ "dependencies": { "@jupyter/collaboration": "^2.0.11", "@jupyter/docprovider": "^2.0.11", - "@jupyter/ydoc": "^1.1.0-a0", + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc", "@jupyterlab/application": "^4.0.5", "@jupyterlab/apputils": "^4.0.5", "@jupyterlab/codemirror": "^4.0.5", diff --git a/packages/collaboration-extension/src/collaboration.ts b/packages/collaboration-extension/src/collaboration.ts index e2502bca..783684f1 100644 --- a/packages/collaboration-extension/src/collaboration.ts +++ b/packages/collaboration-extension/src/collaboration.ts @@ -5,25 +5,43 @@ * @module collaboration-extension */ +import { + DocumentRegistry +} from '@jupyterlab/docregistry'; + +import { + NotebookPanel, INotebookModel +} from '@jupyterlab/notebook'; + +import { + IDisposable, DisposableDelegate +} from '@lumino/disposable'; + +import { CommandRegistry } from '@lumino/commands'; + import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; -import { IToolbarWidgetRegistry } from '@jupyterlab/apputils'; +import { Dialog, IToolbarWidgetRegistry } from '@jupyterlab/apputils'; import { EditorExtensionRegistry, IEditorExtensionRegistry } from '@jupyterlab/codemirror'; -import { WebSocketAwarenessProvider } from '@jupyter/docprovider'; -import { SidePanel, usersIcon } from '@jupyterlab/ui-components'; +import { requestDocDelete, requestDocMerge, WebSocketAwarenessProvider } from '@jupyter/docprovider'; +import { + SidePanel, + usersIcon, + caretDownIcon +} from '@jupyterlab/ui-components'; import { URLExt } from '@jupyterlab/coreutils'; import { ServerConnection } from '@jupyterlab/services'; import { IStateDB, StateDB } from '@jupyterlab/statedb'; -import { ITranslator, nullTranslator } from '@jupyterlab/translation'; +import { ITranslator, nullTranslator, TranslationBundle } from '@jupyterlab/translation'; import { Menu, MenuBar } from '@lumino/widgets'; -import { IAwareness } from '@jupyter/ydoc'; +import { IAwareness, ISharedNotebook, NotebookChange } from '@jupyter/ydoc'; import { CollaboratorsPanel, @@ -189,3 +207,221 @@ export const userEditorCursors: JupyterFrontEndPlugin = { }); } }; + +/** + * A plugin to add editing mode to the notebook page + */ +export const editingMode: JupyterFrontEndPlugin = { + id: '@jupyter/collaboration-extension:editingMode', + description: 'A plugin to add editing mode to the notebook page.', + autoStart: true, + optional: [ITranslator], + activate: ( + app: JupyterFrontEnd, + translator: ITranslator | null + ) => { + app.docRegistry.addWidgetExtension('Notebook', new EditingModeExtension(translator)); + }, +}; + +export class EditingModeExtension implements DocumentRegistry.IWidgetExtension { + private _trans: TranslationBundle; + + constructor(translator: ITranslator | null) { + this._trans = (translator ?? nullTranslator).load('jupyter_collaboration'); + } + + createNew( + panel: NotebookPanel, + context: DocumentRegistry.IContext + ): IDisposable { + const editingMenubar = new MenuBar(); + const suggestionMenubar = new MenuBar(); + const reviewMenubar = new MenuBar(); + + const editingCommands = new CommandRegistry(); + const suggestionCommands = new CommandRegistry(); + const reviewCommands = new CommandRegistry(); + + const editingMenu = new Menu({ commands: editingCommands }); + const suggestionMenu = new Menu({ commands: suggestionCommands }); + const reviewMenu = new Menu({ commands: reviewCommands }); + + const sharedModel = context.model.sharedModel; + const suggestions: {[key: string]: Menu.IItem} = {}; + var myForkId = ''; // curently allows only one suggestion per user + + editingMenu.title.label = 'Editing'; + editingMenu.title.icon = caretDownIcon; + + suggestionMenu.title.label = 'Root'; + suggestionMenu.title.icon = caretDownIcon; + + reviewMenu.title.label = 'Review'; + reviewMenu.title.icon = caretDownIcon; + + editingCommands.addCommand('editing', { + label: 'Editing', + execute: () => { + editingMenu.title.label = 'Editing'; + suggestionMenu.title.label = 'Root'; + open_dialog('Editing', this._trans); + } + }); + editingCommands.addCommand('suggesting', { + label: 'Suggesting', + execute: () => { + editingMenu.title.label = 'Suggesting'; + reviewMenu.clearItems(); + if (myForkId === '') { + myForkId = 'pending'; + sharedModel.provider.fork().then(newForkId => { + myForkId = newForkId; + sharedModel.provider.connect(newForkId); + suggestionMenu.title.label = newForkId; + }); + } + else { + suggestionMenu.title.label = myForkId; + sharedModel.provider.connect(myForkId); + } + open_dialog('Suggesting', this._trans); + } + }); + + suggestionCommands.addCommand('root', { + label: 'Root', + execute: () => { + // we cannot review the root document + reviewMenu.clearItems(); + suggestionMenu.title.label = 'Root'; + editingMenu.title.label = 'Editing'; + sharedModel.provider.connect(sharedModel.rootRoomId); + open_dialog('Editing', this._trans); + } + }); + + reviewCommands.addCommand('merge', { + label: 'Merge', + execute: () => { + requestDocMerge(sharedModel.currentRoomId, sharedModel.rootRoomId); + } + }); + reviewCommands.addCommand('discard', { + label: 'Discard', + execute: () => { + requestDocDelete(sharedModel.currentRoomId, sharedModel.rootRoomId); + } + }); + + editingMenu.addItem({type: 'command', command: 'editing'}); + editingMenu.addItem({type: 'command', command: 'suggesting'}); + + suggestionMenu.addItem({type: 'command', command: 'root'}); + + const _onStateChanged = (sender: ISharedNotebook, changes: NotebookChange) => { + if (changes.stateChange) { + changes.stateChange.forEach(value => { + const forkPrefix = 'fork_'; + if (value.name === 'merge' || value.name === 'delete') { + // we are on fork + if (sharedModel.currentRoomId === value.newValue) { + reviewMenu.clearItems(); + const merge = value.name === 'merge'; + sharedModel.provider.connect(sharedModel.rootRoomId, merge); + open_dialog('Editing', this._trans); + myForkId = ''; + } + } + else if (value.name.startsWith(forkPrefix)) { + // we are on root + const forkId = value.name.slice(forkPrefix.length); + if (value.newValue === 'new') { + suggestionCommands.addCommand(forkId, { + label: forkId, + execute: () => { + editingMenu.title.label = 'Suggesting'; + reviewMenu.clearItems(); + reviewMenu.addItem({type: 'command', command: 'merge'}); + reviewMenu.addItem({type: 'command', command: 'discard'}); + suggestionMenu.title.label = forkId; + sharedModel.provider.connect(forkId); + open_dialog('Suggesting', this._trans); + } + }); + const item = suggestionMenu.addItem({type: 'command', command: forkId}); + suggestions[forkId] = item; + if (myForkId !== forkId) { + if (myForkId !== 'pending') { + const dialog = new Dialog({ + title: this._trans.__('New suggestion'), + body: this._trans.__('View suggestion?'), + buttons: [ + Dialog.okButton({ label: 'View' }), + Dialog.cancelButton({ label: 'Discard' }), + ], + }); + dialog.launch().then(resp => { + dialog.close(); + if (resp.button.label === 'View') { + sharedModel.provider.connect(forkId); + suggestionMenu.title.label = forkId; + editingMenu.title.label = 'Suggesting'; + reviewMenu.clearItems(); + reviewMenu.addItem({type: 'command', command: 'merge'}); + reviewMenu.addItem({type: 'command', command: 'discard'}); + } + }); + } + else { + reviewMenu.clearItems(); + reviewMenu.addItem({type: 'command', command: 'merge'}); + reviewMenu.addItem({type: 'command', command: 'discard'}); + } + } + } + else if (value.newValue === undefined) { + editingMenu.title.label = 'Editing'; + suggestionMenu.title.label = 'Root'; + const item: Menu.IItem = suggestions[value.oldValue]; + delete suggestions[value.oldValue]; + suggestionMenu.removeItem(item); + } + } + }); + } + }; + + sharedModel.changed.connect(_onStateChanged, this); + + editingMenubar.addMenu(editingMenu); + suggestionMenubar.addMenu(suggestionMenu); + reviewMenubar.addMenu(reviewMenu); + + panel.toolbar.insertItem(997, 'editingMode', editingMenubar); + panel.toolbar.insertItem(998, 'suggestions', suggestionMenubar); + panel.toolbar.insertItem(999, 'review', reviewMenubar); + return new DisposableDelegate(() => { + editingMenubar.dispose(); + suggestionMenubar.dispose(); + reviewMenubar.dispose(); + }); + } +} + + +function open_dialog(title: string, trans: TranslationBundle) { + var body: string; + if (title === 'Editing') { + body = 'You are now directly editing the document.' + } + else { + body = 'Your edits now become suggestions to the document.' + } + const dialog = new Dialog({ + title: trans.__(title), + body: trans.__(body), + buttons: [Dialog.okButton({ label: 'OK' })], + }); + dialog.launch().then(resp => { dialog.close(); }); +} diff --git a/packages/collaboration-extension/src/index.ts b/packages/collaboration-extension/src/index.ts index 513befa1..3e1be19b 100644 --- a/packages/collaboration-extension/src/index.ts +++ b/packages/collaboration-extension/src/index.ts @@ -19,7 +19,8 @@ import { menuBarPlugin, rtcGlobalAwarenessPlugin, rtcPanelPlugin, - userEditorCursors + userEditorCursors, + editingMode } from './collaboration'; import { sharedLink } from './sharedlink'; @@ -37,7 +38,8 @@ const plugins: JupyterFrontEndPlugin[] = [ rtcGlobalAwarenessPlugin, rtcPanelPlugin, sharedLink, - userEditorCursors + userEditorCursors, + editingMode ]; export default plugins; diff --git a/packages/collaboration/package.json b/packages/collaboration/package.json index 1f813402..8cebcff2 100644 --- a/packages/collaboration/package.json +++ b/packages/collaboration/package.json @@ -42,6 +42,7 @@ "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.7.0", "@jupyter/docprovider": "^2.0.11", + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc", "@jupyterlab/apputils": "^4.0.5", "@jupyterlab/coreutils": "^6.0.5", "@jupyterlab/services": "^7.0.5", diff --git a/packages/docprovider/package.json b/packages/docprovider/package.json index fec4a135..47fa53d0 100644 --- a/packages/docprovider/package.json +++ b/packages/docprovider/package.json @@ -41,7 +41,7 @@ "watch": "tsc -b --watch" }, "dependencies": { - "@jupyter/ydoc": "^1.1.0-a0", + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc", "@jupyterlab/coreutils": "^6.0.5", "@jupyterlab/services": "^7.0.5", "@lumino/coreutils": "^2.1.0", @@ -51,6 +51,10 @@ "y-websocket": "^1.3.15", "yjs": "^13.5.40" }, + "resolutions": { + "@jupyterlab/services": "file:.yalc/@jupyterlab/services", + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc" + }, "devDependencies": { "@jupyterlab/testing": "^4.0.5", "@types/jest": "^29.2.0", diff --git a/packages/docprovider/src/index.ts b/packages/docprovider/src/index.ts index 62edebda..abfd4873 100644 --- a/packages/docprovider/src/index.ts +++ b/packages/docprovider/src/index.ts @@ -8,6 +8,7 @@ */ export * from './awareness'; +export * from './requests'; export * from './ydrive'; export * from './yprovider'; export * from './tokens'; diff --git a/packages/docprovider/src/requests.ts b/packages/docprovider/src/requests.ts index 54e7c364..699f2a65 100644 --- a/packages/docprovider/src/requests.ts +++ b/packages/docprovider/src/requests.ts @@ -11,6 +11,9 @@ import { ServerConnection, Contents } from '@jupyterlab/services'; * See https://github.com/jupyterlab/jupyter_collaboration */ const DOC_SESSION_URL = 'api/collaboration/session'; +const DOC_FORK_URL = 'api/collaboration/fork_room'; +const DOC_MERGE_URL = 'api/collaboration/merge_room'; +const DOC_DELETE_URL = 'api/collaboration/delete_room'; /** * Document session model @@ -73,3 +76,117 @@ export async function requestDocSession( return data; } + + +export async function requestDocFork( + roomid: string, +): Promise { + const settings = ServerConnection.makeSettings(); + const url = URLExt.join( + settings.baseUrl, + DOC_FORK_URL, + encodeURIComponent(roomid) + ); + const body = {method: 'PUT'}; + + let response: Response; + try { + response = await ServerConnection.makeRequest(url, body, settings); + } catch (error) { + throw new ServerConnection.NetworkError(error as Error); + } + + let data: any = await response.text(); + + if (data.length > 0) { + try { + data = JSON.parse(data); + } catch (error) { + console.log('Not a JSON response body.', response); + } + } + + if (!response.ok) { + throw new ServerConnection.ResponseError(response, data.message || data); + } + + return data; +} + + +export async function requestDocMerge( + forkRoomid: string, + rootRoomid: string +): Promise { + const settings = ServerConnection.makeSettings(); + const url = URLExt.join( + settings.baseUrl, + DOC_MERGE_URL + ); + const body = { + method: 'PUT', + body: JSON.stringify({ fork_roomid: forkRoomid, root_roomid: rootRoomid }) + }; + + let response: Response; + try { + response = await ServerConnection.makeRequest(url, body, settings); + } catch (error) { + throw new ServerConnection.NetworkError(error as Error); + } + + let data: any = await response.text(); + + if (data.length > 0) { + try { + data = JSON.parse(data); + } catch (error) { + console.log('Not a JSON response body.', response); + } + } + + if (!response.ok) { + throw new ServerConnection.ResponseError(response, data.message || data); + } + + return data; +} + + +export async function requestDocDelete( + forkRoomid: string, + rootRoomid: string, +): Promise { + const settings = ServerConnection.makeSettings(); + const url = URLExt.join( + settings.baseUrl, + DOC_DELETE_URL, + ); + const body = { + method: 'DELETE', + body: JSON.stringify({ fork_roomid: forkRoomid, root_roomid: rootRoomid }) + }; + + let response: Response; + try { + response = await ServerConnection.makeRequest(url, body, settings); + } catch (error) { + throw new ServerConnection.NetworkError(error as Error); + } + + let data: any = await response.text(); + + if (data.length > 0) { + try { + data = JSON.parse(data); + } catch (error) { + console.log('Not a JSON response body.', response); + } + } + + if (!response.ok) { + throw new ServerConnection.ResponseError(response, data.message || data); + } + + return data; +} diff --git a/packages/docprovider/src/ydrive.ts b/packages/docprovider/src/ydrive.ts index b8ec4d60..aab5b43c 100644 --- a/packages/docprovider/src/ydrive.ts +++ b/packages/docprovider/src/ydrive.ts @@ -147,6 +147,7 @@ export class YDrive extends Drive implements ICollaborativeDrive { const key = `${options.format}:${options.contentType}:${options.path}`; this._providers.set(key, provider); + sharedModel.provider = provider; sharedModel.disposed.connect(() => { const provider = this._providers.get(key); if (provider) { diff --git a/packages/docprovider/src/yprovider.ts b/packages/docprovider/src/yprovider.ts index 4061ebeb..a3990adc 100644 --- a/packages/docprovider/src/yprovider.ts +++ b/packages/docprovider/src/yprovider.ts @@ -8,25 +8,15 @@ import { User } from '@jupyterlab/services'; import { TranslationBundle } from '@jupyterlab/translation'; import { PromiseDelegate } from '@lumino/coreutils'; -import { IDisposable } from '@lumino/disposable'; import { Signal } from '@lumino/signaling'; -import { DocumentChange, YDocument } from '@jupyter/ydoc'; +import { DocumentChange, IDocumentProvider, YDocument } from '@jupyter/ydoc'; import { Awareness } from 'y-protocols/awareness'; import { WebsocketProvider as YWebsocketProvider } from 'y-websocket'; -import { requestDocSession } from './requests'; +import { requestDocFork, requestDocSession } from './requests'; -/** - * An interface for a document provider. - */ -export interface IDocumentProvider extends IDisposable { - /** - * Returns a Promise that resolves when the document provider is ready. - */ - readonly ready: Promise; -} /** * A class to provide Yjs synchronization over WebSocket. @@ -42,6 +32,7 @@ export class WebSocketProvider implements IDocumentProvider { */ constructor(options: WebSocketProvider.IOptions) { this._isDisposed = false; + this._sessionId = options.sessionId ?? ''; this._path = options.path; this._contentType = options.contentType; this._format = options.format; @@ -50,15 +41,14 @@ export class WebSocketProvider implements IDocumentProvider { this._awareness = options.model.awareness; this._yWebsocketProvider = null; this._trans = options.translator; + this._user = options.user; - const user = options.user; - - user.ready + this._user.ready .then(() => { - this._onUserChanged(user); + this._onUserChanged(this._user); }) .catch(e => console.error(e)); - user.userChanged.connect(this._onUserChanged, this); + this._user.userChanged.connect(this._onUserChanged, this); this._connect().catch(e => console.warn(e)); } @@ -91,20 +81,71 @@ export class WebSocketProvider implements IDocumentProvider { Signal.clearData(this); } - private async _connect(): Promise { + async fork(): Promise { const session = await requestDocSession( this._format, this._contentType, this._path ); + const response = await requestDocFork(`${session.format}:${session.type}:${session.fileId}`); + const forkId = response.roomId; + this._sharedModel.currentRoomId = forkId; + this._sharedModel.addFork(forkId); + + return forkId; + } + + connect(roomId: string, merge?: boolean) { + this._sharedModel.currentRoomId = roomId; + this._yWebsocketProvider?.disconnect(); + if (roomId === this._sharedModel.rootRoomId) { + // connecting to the root + // don't bring our changes there if not merging + if (merge !== true) { + while (this._sharedModel.undoManager.canUndo()) { + this._sharedModel.undoManager.undo(); + } + } + this._sharedModel.undoManager.clear(); + } + else { + // connecting to a fork + // keep track of changes so that we can undo them when connecting back to root + this._sharedModel.undoManager.clear(); + } + + this._yWebsocketProvider = new YWebsocketProvider( + this._serverUrl, + roomId, + this._sharedModel.ydoc, + { + disableBc: true, + params: { sessionId: this._sessionId }, + awareness: this._awareness + } + ); + } + + private async _connect(): Promise { + if (this._sharedModel.rootRoomId === '') { + const session = await requestDocSession( + this._format, + this._contentType, + this._path + ); + this._sharedModel.rootRoomId = `${session.format}:${session.type}:${session.fileId}`; + this._sharedModel.currentRoomId = this._sharedModel.rootRoomId; + this._sessionId = session.sessionId; + } + this._yWebsocketProvider = new YWebsocketProvider( this._serverUrl, - `${session.format}:${session.type}:${session.fileId}`, + this._sharedModel.rootRoomId, this._sharedModel.ydoc, { disableBc: true, - params: { sessionId: session.sessionId }, + params: { sessionId: this._sessionId! }, awareness: this._awareness } ); @@ -142,12 +183,14 @@ export class WebSocketProvider implements IDocumentProvider { private _contentType: string; private _format: string; private _isDisposed: boolean; + private _sessionId: string; private _path: string; private _ready = new PromiseDelegate(); private _serverUrl: string; private _sharedModel: YDocument; private _yWebsocketProvider: YWebsocketProvider | null; private _trans: TranslationBundle; + private _user: User.IManager; } /** @@ -192,5 +235,10 @@ export namespace WebSocketProvider { * The jupyterlab translator */ translator: TranslationBundle; + + /** + * The document session ID, if the document is a fork + */ + sessionId?: string; } } diff --git a/yarn.lock b/yarn.lock index f52db1a8..2797ea8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2035,7 +2035,7 @@ __metadata: dependencies: "@jupyter/collaboration": ^2.0.11 "@jupyter/docprovider": ^2.0.11 - "@jupyter/ydoc": ^1.1.0-a0 + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc" "@jupyterlab/application": ^4.0.5 "@jupyterlab/apputils": ^4.0.5 "@jupyterlab/builder": ^4.0.5 @@ -2070,6 +2070,7 @@ __metadata: "@codemirror/state": ^6.2.0 "@codemirror/view": ^6.7.0 "@jupyter/docprovider": ^2.0.11 + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc" "@jupyterlab/apputils": ^4.0.5 "@jupyterlab/coreutils": ^6.0.5 "@jupyterlab/services": ^7.0.5 @@ -2090,7 +2091,7 @@ __metadata: version: 0.0.0-use.local resolution: "@jupyter/docprovider@workspace:packages/docprovider" dependencies: - "@jupyter/ydoc": ^1.1.0-a0 + "@jupyter/ydoc": "file:.yalc/@jupyter/ydoc" "@jupyterlab/coreutils": ^6.0.5 "@jupyterlab/services": ^7.0.5 "@jupyterlab/testing": ^4.0.5 @@ -2107,6 +2108,17 @@ __metadata: languageName: unknown linkType: soft +"@jupyter/react-components@npm:^0.15.2": + version: 0.15.2 + resolution: "@jupyter/react-components@npm:0.15.2" + dependencies: + "@jupyter/web-components": ^0.15.2 + "@microsoft/fast-react-wrapper": ^0.3.22 + react: ">=17.0.0 <19.0.0" + checksum: d6d339ff9c2fed1fd5afda612be500d73c4a83eee5470d50e94020dadd1e389a3bf745c7240b0a48edbc6d3fdacec93367b7b5e40588f2df588419caada705be + languageName: node + linkType: hard + "@jupyter/real-time-collaboration@workspace:.": version: 0.0.0-use.local resolution: "@jupyter/real-time-collaboration@workspace:." @@ -2130,31 +2142,58 @@ __metadata: languageName: unknown linkType: soft -"@jupyter/ydoc@npm:^1.0.2": - version: 1.0.2 - resolution: "@jupyter/ydoc@npm:1.0.2" +"@jupyter/web-components@npm:^0.15.2": + version: 0.15.2 + resolution: "@jupyter/web-components@npm:0.15.2" dependencies: - "@jupyterlab/nbformat": ^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0 - "@lumino/coreutils": ^1.11.0 || ^2.0.0 - "@lumino/disposable": ^1.10.0 || ^2.0.0 - "@lumino/signaling": ^1.10.0 || ^2.0.0 - y-protocols: ^1.0.5 - yjs: ^13.5.40 - checksum: 739f9630940466b3cfcd7b742dd06479f81772ca13f863d057af0bbb5e318829506969066ab72977e7c721644982b5c8f88cf44e1ae81955ed1c27e87632d1f2 + "@microsoft/fast-colors": ^5.3.1 + "@microsoft/fast-element": ^1.12.0 + "@microsoft/fast-foundation": ^2.49.4 + "@microsoft/fast-web-utilities": ^5.4.1 + checksum: f272ef91de08e28f9414a26dbd2388e1a8985c90f4ab00231978cee49bd5212f812411397a9038d298c8c0c4b41eb28cc86f1127bc7ace309bda8df60c4a87c8 languageName: node linkType: hard -"@jupyter/ydoc@npm:^1.1.0-a0": - version: 1.1.0-a0 - resolution: "@jupyter/ydoc@npm:1.1.0-a0" +"@jupyter/ydoc@file:.yalc/@jupyter/ydoc::locator=%40jupyter%2Freal-time-collaboration%40workspace%3A.": + version: 2.0.1 + resolution: "@jupyter/ydoc@file:.yalc/@jupyter/ydoc#.yalc/@jupyter/ydoc::hash=045bce&locator=%40jupyter%2Freal-time-collaboration%40workspace%3A." dependencies: + "@jupyterlab/application": ^4.0.0 "@jupyterlab/nbformat": ^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0 "@lumino/coreutils": ^1.11.0 || ^2.0.0 "@lumino/disposable": ^1.10.0 || ^2.0.0 "@lumino/signaling": ^1.10.0 || ^2.0.0 y-protocols: ^1.0.5 yjs: ^13.5.40 - checksum: 0099bacb2884a460867658e7f5a944e4d6eca7c4d3d68a6b31102be77d8fc9b6ba162af55c20f81c70f7fef4e9d9329eee4da32063c0caa0c6e3f10a488f95b5 + checksum: aed2b93d1f9d447e7ad1be8699dc3a31968f4cfd4772a1ed1330308eb08ba8d14973df24bae4e49bd93f416f646b84edb44c567487403e7d0bdb665e6ca0e29f + languageName: node + linkType: hard + +"@jupyterlab/application@npm:^4.0.0": + version: 4.1.2 + resolution: "@jupyterlab/application@npm:4.1.2" + dependencies: + "@fortawesome/fontawesome-free": ^5.12.0 + "@jupyterlab/apputils": ^4.2.2 + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/docregistry": ^4.1.2 + "@jupyterlab/rendermime": ^4.1.2 + "@jupyterlab/rendermime-interfaces": ^3.9.2 + "@jupyterlab/services": ^7.1.2 + "@jupyterlab/statedb": ^4.1.2 + "@jupyterlab/translation": ^4.1.2 + "@jupyterlab/ui-components": ^4.1.2 + "@lumino/algorithm": ^2.0.1 + "@lumino/application": ^2.3.0 + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/messaging": ^2.0.1 + "@lumino/polling": ^2.1.2 + "@lumino/properties": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/widgets": ^2.3.1 + checksum: 10be4cdfd08dfd69786a3cd9753d48246e507e6a1cf15572bc4938be1d53c5c3fe291153ff02f2ffc941f77ab3c648649a6e6e2db6bbbcfcaa1bcc17a525b90b languageName: node linkType: hard @@ -2215,6 +2254,35 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/apputils@npm:^4.2.2": + version: 4.2.2 + resolution: "@jupyterlab/apputils@npm:4.2.2" + dependencies: + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/observables": ^5.1.2 + "@jupyterlab/rendermime-interfaces": ^3.9.2 + "@jupyterlab/services": ^7.1.2 + "@jupyterlab/settingregistry": ^4.1.2 + "@jupyterlab/statedb": ^4.1.2 + "@jupyterlab/statusbar": ^4.1.2 + "@jupyterlab/translation": ^4.1.2 + "@jupyterlab/ui-components": ^4.1.2 + "@lumino/algorithm": ^2.0.1 + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/domutils": ^2.0.1 + "@lumino/messaging": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/virtualdom": ^2.0.1 + "@lumino/widgets": ^2.3.1 + "@types/react": ^18.0.26 + react: ^18.2.0 + sanitize-html: ~2.7.3 + checksum: 6d0811f30ba353d9ce67d515fdfff802f99a628b7403b4b7aa44291d634bd228c0073ddab5ed6d160eb7bdc214b23e540039c1c5fd1f76ba9635b4ca3cca1d30 + languageName: node + linkType: hard + "@jupyterlab/attachments@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/attachments@npm:4.0.5" @@ -2329,6 +2397,30 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/codeeditor@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/codeeditor@npm:4.1.2" + dependencies: + "@codemirror/state": ^6.2.0 + "@jupyter/ydoc": ^1.1.1 + "@jupyterlab/apputils": ^4.2.2 + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/nbformat": ^4.1.2 + "@jupyterlab/observables": ^5.1.2 + "@jupyterlab/statusbar": ^4.1.2 + "@jupyterlab/translation": ^4.1.2 + "@jupyterlab/ui-components": ^4.1.2 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/dragdrop": ^2.1.4 + "@lumino/messaging": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/widgets": ^2.3.1 + react: ^18.2.0 + checksum: b09618bc80a6d62c11fe53ccfe51cae1bdeb7298fab00c71a1119efa8102b86b6aa828d75b00ede80821b7073e3d6c3bb48c93805572eef4563308e4d3e7da1e + languageName: node + linkType: hard + "@jupyterlab/codemirror@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/codemirror@npm:4.0.5" @@ -2385,6 +2477,34 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/coreutils@npm:^6.1.2": + version: 6.1.2 + resolution: "@jupyterlab/coreutils@npm:6.1.2" + dependencies: + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/signaling": ^2.1.2 + minimist: ~1.2.0 + path-browserify: ^1.0.0 + url-parse: ~1.5.4 + checksum: 6dcc812e0ebae28f902ece4acd58aee8103033b23a3bac0935d4d9d8c9c10f8797b422f4e8b0be53fac4781811fb9b82874ce499cd69a6d198986e0cdb4a97ff + languageName: node + linkType: hard + +"@jupyterlab/coreutils@npm:^6.1.5": + version: 6.1.5 + resolution: "@jupyterlab/coreutils@npm:6.1.5" + dependencies: + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/signaling": ^2.1.2 + minimist: ~1.2.0 + path-browserify: ^1.0.0 + url-parse: ~1.5.4 + checksum: b91c5a374f3c97d62e2442bb5f12cb79c6e440b5f6aa4d4ed6e492e8ca38836f7068106bb7029834a4e5de1947a9c44c342d23bedf9a4611aafca33629aed049 + languageName: node + linkType: hard + "@jupyterlab/docmanager@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/docmanager@npm:4.0.5" @@ -2433,6 +2553,32 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/docregistry@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/docregistry@npm:4.1.2" + dependencies: + "@jupyter/ydoc": ^1.1.1 + "@jupyterlab/apputils": ^4.2.2 + "@jupyterlab/codeeditor": ^4.1.2 + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/observables": ^5.1.2 + "@jupyterlab/rendermime": ^4.1.2 + "@jupyterlab/rendermime-interfaces": ^3.9.2 + "@jupyterlab/services": ^7.1.2 + "@jupyterlab/translation": ^4.1.2 + "@jupyterlab/ui-components": ^4.1.2 + "@lumino/algorithm": ^2.0.1 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/messaging": ^2.0.1 + "@lumino/properties": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/widgets": ^2.3.1 + react: ^18.2.0 + checksum: 6dcef927286f66636d8f7f0a128d7c5084f60f1fc9969d33080f3b905b08c63036bb99602d484b52530f6176242b224ed65444fd7cfc2df7d44f0b0dd039ac40 + languageName: node + linkType: hard + "@jupyterlab/documentsearch@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/documentsearch@npm:4.0.5" @@ -2562,6 +2708,24 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/nbformat@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/nbformat@npm:4.1.2" + dependencies: + "@lumino/coreutils": ^2.1.2 + checksum: 6c55337b667dcc5a6282f93972a30d227ba7c3f576fc4b60069408dd114dff1bc9f743bb6f984da088dfda25b7c4f25f13a472cd5c05b24af2e32b6b17172c6b + languageName: node + linkType: hard + +"@jupyterlab/nbformat@npm:^4.1.5": + version: 4.1.5 + resolution: "@jupyterlab/nbformat@npm:4.1.5" + dependencies: + "@lumino/coreutils": ^2.1.2 + checksum: d417d7eade40d389fea8593358b6455158cf3e67fa40c0c4c05c865852520acc466102109723c9cb16eecf95952617d79f7fe6be9da6ca3f601749bdecdfda97 + languageName: node + linkType: hard + "@jupyterlab/notebook@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/notebook@npm:4.0.5" @@ -2611,6 +2775,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/observables@npm:^5.1.2": + version: 5.1.2 + resolution: "@jupyterlab/observables@npm:5.1.2" + dependencies: + "@lumino/algorithm": ^2.0.1 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/messaging": ^2.0.1 + "@lumino/signaling": ^2.1.2 + checksum: 5bf7ec19c02d7d1923f4bf4c048f182957332a4e1f5481af980f976d518fd1590034cd529d7a980c228586b1650a796361a18b38b00bf6465ac0967ba6cdc8c0 + languageName: node + linkType: hard + "@jupyterlab/outputarea@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/outputarea@npm:4.0.5" @@ -2643,6 +2820,16 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/rendermime-interfaces@npm:^3.9.2": + version: 3.9.2 + resolution: "@jupyterlab/rendermime-interfaces@npm:3.9.2" + dependencies: + "@lumino/coreutils": ^1.11.0 || ^2.1.2 + "@lumino/widgets": ^1.37.2 || ^2.3.1 + checksum: 65d6d4fe8c241b9f1267058db43a8fca01ee9fb6a67a267826accfdd0a9e71f2143fcad778b5c6d8b5bf825440ee9b040088253866e8e1a840b7276fba266b88 + languageName: node + linkType: hard + "@jupyterlab/rendermime@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/rendermime@npm:4.0.5" @@ -2663,22 +2850,42 @@ __metadata: languageName: node linkType: hard -"@jupyterlab/services@npm:^7.0.5": - version: 7.0.5 - resolution: "@jupyterlab/services@npm:7.0.5" +"@jupyterlab/rendermime@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/rendermime@npm:4.1.2" + dependencies: + "@jupyterlab/apputils": ^4.2.2 + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/nbformat": ^4.1.2 + "@jupyterlab/observables": ^5.1.2 + "@jupyterlab/rendermime-interfaces": ^3.9.2 + "@jupyterlab/services": ^7.1.2 + "@jupyterlab/translation": ^4.1.2 + "@lumino/coreutils": ^2.1.2 + "@lumino/messaging": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/widgets": ^2.3.1 + lodash.escape: ^4.0.1 + checksum: bcd161b0d78d2fe1051eaf10bafb70ffacc44ae946ee331acbc6112ecf100995e07204fe00b9f5abb5d60b4fd5b6899eaad7a44a921af42c2c4f39abecee7ab7 + languageName: node + linkType: hard + +"@jupyterlab/services@npm:^7.0.5, @jupyterlab/services@npm:^7.1.2": + version: 7.1.5 + resolution: "@jupyterlab/services@npm:7.1.5" dependencies: - "@jupyter/ydoc": ^1.0.2 - "@jupyterlab/coreutils": ^6.0.5 - "@jupyterlab/nbformat": ^4.0.5 - "@jupyterlab/settingregistry": ^4.0.5 - "@jupyterlab/statedb": ^4.0.5 + "@jupyter/ydoc": ^1.1.1 + "@jupyterlab/coreutils": ^6.1.5 + "@jupyterlab/nbformat": ^4.1.5 + "@jupyterlab/settingregistry": ^4.1.5 + "@jupyterlab/statedb": ^4.1.5 "@lumino/coreutils": ^2.1.2 "@lumino/disposable": ^2.1.2 "@lumino/polling": ^2.1.2 "@lumino/properties": ^2.0.1 "@lumino/signaling": ^2.1.2 ws: ^8.11.0 - checksum: cf4176dbb73c08e777b5e6ca26cba6ad7a142fc76ae6b46ef17ac7d8c8021f62d66e95e2ee0dbce5c33a0b2380750d440783d0398d787b8e8028920e04dd1d0b + checksum: f4b20ee62e5c3c7e0fa5942d3deb95329beb5a9ea6295403eefc0d5a723665379a09c58b21bc6a9fed7a69990570e5cfb66bc314e037a452b678fc4ec237dc55 languageName: node linkType: hard @@ -2701,6 +2908,44 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/settingregistry@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/settingregistry@npm:4.1.2" + dependencies: + "@jupyterlab/nbformat": ^4.1.2 + "@jupyterlab/statedb": ^4.1.2 + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/signaling": ^2.1.2 + "@rjsf/utils": ^5.13.4 + ajv: ^8.12.0 + json5: ^2.2.3 + peerDependencies: + react: ">=16" + checksum: a0a1f9d3747caa3ac6e523df64b4023f9d3bc1c1c9a2982cdf8113a5ba3f191e10cd8a897e9bff111b9faa834b48c0666a6b03ce3749c9f9e5ffb43b9331c207 + languageName: node + linkType: hard + +"@jupyterlab/settingregistry@npm:^4.1.5": + version: 4.1.5 + resolution: "@jupyterlab/settingregistry@npm:4.1.5" + dependencies: + "@jupyterlab/nbformat": ^4.1.5 + "@jupyterlab/statedb": ^4.1.5 + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/signaling": ^2.1.2 + "@rjsf/utils": ^5.13.4 + ajv: ^8.12.0 + json5: ^2.2.3 + peerDependencies: + react: ">=16" + checksum: 576d49cbbb4a18ba5f55230938b67c6dbc6819dfafb75ece2d9d030913e69768ddcb2616de4f7dbd3bcd8aa35e292aee90fe98b91e7dccdaae2610c64ec07f94 + languageName: node + linkType: hard + "@jupyterlab/statedb@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/statedb@npm:4.0.5" @@ -2714,6 +2959,32 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/statedb@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/statedb@npm:4.1.2" + dependencies: + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/properties": ^2.0.1 + "@lumino/signaling": ^2.1.2 + checksum: 30314f4f93441aac6d62068c264c94c0e694829c64ce0dc59e867ef4d188d396edc9c6868dd92ca514f6e7b15dc2568ff3f2de078a20283f60cc5ae70723bacc + languageName: node + linkType: hard + +"@jupyterlab/statedb@npm:^4.1.5": + version: 4.1.5 + resolution: "@jupyterlab/statedb@npm:4.1.5" + dependencies: + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/properties": ^2.0.1 + "@lumino/signaling": ^2.1.2 + checksum: e7f3ea9a5ebb04a602d93d1ddc9175a5b24a0f3814e99410ec3dba2dd3a86572ea61917d8a65e1b4b8c4ed25c8eaa814646a817a3b5d39b8a74a7b6cbb0071c1 + languageName: node + linkType: hard + "@jupyterlab/statusbar@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/statusbar@npm:4.0.5" @@ -2730,6 +3001,22 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/statusbar@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/statusbar@npm:4.1.2" + dependencies: + "@jupyterlab/ui-components": ^4.1.2 + "@lumino/algorithm": ^2.0.1 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/messaging": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/widgets": ^2.3.1 + react: ^18.2.0 + checksum: 4d3e23149cbb6ded2741af5c0cd60f26c37ab36f4bae1c43e847e16559b2a779de85c0474ccd81f0f3decd2d4e6019a202681989a06a095762ad85105f6c1458 + languageName: node + linkType: hard + "@jupyterlab/testing@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/testing@npm:4.0.5" @@ -2789,6 +3076,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/translation@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/translation@npm:4.1.2" + dependencies: + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/rendermime-interfaces": ^3.9.2 + "@jupyterlab/services": ^7.1.2 + "@jupyterlab/statedb": ^4.1.2 + "@lumino/coreutils": ^2.1.2 + checksum: e8261be05ff642434b8c1b439305e464f6c38eea2d1cfbdb38d1ac4922d6df88f157dd1593674c0a3ed90082763bd313610187b1a5007027aa275ed8ed5301e1 + languageName: node + linkType: hard + "@jupyterlab/ui-components@npm:^4.0.5": version: 4.0.5 resolution: "@jupyterlab/ui-components@npm:4.0.5" @@ -2818,6 +3118,37 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/ui-components@npm:^4.1.2": + version: 4.1.2 + resolution: "@jupyterlab/ui-components@npm:4.1.2" + dependencies: + "@jupyter/react-components": ^0.15.2 + "@jupyter/web-components": ^0.15.2 + "@jupyterlab/coreutils": ^6.1.2 + "@jupyterlab/observables": ^5.1.2 + "@jupyterlab/rendermime-interfaces": ^3.9.2 + "@jupyterlab/translation": ^4.1.2 + "@lumino/algorithm": ^2.0.1 + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/messaging": ^2.0.1 + "@lumino/polling": ^2.1.2 + "@lumino/properties": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/virtualdom": ^2.0.1 + "@lumino/widgets": ^2.3.1 + "@rjsf/core": ^5.13.4 + "@rjsf/utils": ^5.13.4 + react: ^18.2.0 + react-dom: ^18.2.0 + typestyle: ^2.0.4 + peerDependencies: + react: ^18.2.0 + checksum: d4c0141802dc62bc9eb7f79b01c83b795798d131ff224653a0f8c63881e45e28c8de565303db2be34ba09ba42f5503c5979897ae5b46e8fe923e0fedc21cc8eb + languageName: node + linkType: hard + "@lerna/child-process@npm:6.6.1": version: 6.6.1 resolution: "@lerna/child-process@npm:6.6.1" @@ -3086,6 +3417,17 @@ __metadata: languageName: node linkType: hard +"@lumino/application@npm:^2.3.0": + version: 2.3.0 + resolution: "@lumino/application@npm:2.3.0" + dependencies: + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/widgets": ^2.3.1 + checksum: 9d1eb5bc972ed158bf219604a53bbac1262059bc5b0123d3e041974486b9cbb8288abeeec916f3b62f62d7c32e716cccf8b73e4832ae927e4f9dd4e4b0cd37ed + languageName: node + linkType: hard + "@lumino/collections@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/collections@npm:2.0.1" @@ -3110,6 +3452,21 @@ __metadata: languageName: node linkType: hard +"@lumino/commands@npm:^2.2.0": + version: 2.2.0 + resolution: "@lumino/commands@npm:2.2.0" + dependencies: + "@lumino/algorithm": ^2.0.1 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/domutils": ^2.0.1 + "@lumino/keyboard": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/virtualdom": ^2.0.1 + checksum: 093e9715491e5cef24bc80665d64841417b400f2fa595f9b60832a3b6340c405c94a6aa276911944a2c46d79a6229f3cc087b73f50852bba25ece805abd0fae9 + languageName: node + linkType: hard + "@lumino/coreutils@npm:^1.11.0 || ^2.0.0, @lumino/coreutils@npm:^1.11.0 || ^2.1.2, @lumino/coreutils@npm:^2.1.0, @lumino/coreutils@npm:^2.1.1, @lumino/coreutils@npm:^2.1.2": version: 2.1.2 resolution: "@lumino/coreutils@npm:2.1.2" @@ -3143,6 +3500,16 @@ __metadata: languageName: node linkType: hard +"@lumino/dragdrop@npm:^2.1.4": + version: 2.1.4 + resolution: "@lumino/dragdrop@npm:2.1.4" + dependencies: + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + checksum: 43d82484b13b38b612e7dfb424a840ed6a38d0db778af10655c4ba235c67b5b12db1683929b35a36ab2845f77466066dfd1ee25c1c273e8e175677eba9dc560d + languageName: node + linkType: hard + "@lumino/keyboard@npm:^2.0.1": version: 2.0.1 resolution: "@lumino/keyboard@npm:2.0.1" @@ -3216,6 +3583,72 @@ __metadata: languageName: node linkType: hard +"@lumino/widgets@npm:^1.37.2 || ^2.3.1, @lumino/widgets@npm:^2.3.1": + version: 2.3.1 + resolution: "@lumino/widgets@npm:2.3.1" + dependencies: + "@lumino/algorithm": ^2.0.1 + "@lumino/commands": ^2.2.0 + "@lumino/coreutils": ^2.1.2 + "@lumino/disposable": ^2.1.2 + "@lumino/domutils": ^2.0.1 + "@lumino/dragdrop": ^2.1.4 + "@lumino/keyboard": ^2.0.1 + "@lumino/messaging": ^2.0.1 + "@lumino/properties": ^2.0.1 + "@lumino/signaling": ^2.1.2 + "@lumino/virtualdom": ^2.0.1 + checksum: ba7b8f8839c1cd2a41dbda13281094eb6981a270cccf4f25a0cf83686dcc526a2d8044a20204317630bb7dd4a04d65361408c7623a921549c781afca84b91c67 + languageName: node + linkType: hard + +"@microsoft/fast-colors@npm:^5.3.1": + version: 5.3.1 + resolution: "@microsoft/fast-colors@npm:5.3.1" + checksum: ff87f402faadb4b5aeee3d27762566c11807f927cd4012b8bbc7f073ca68de0e2197f95330ff5dfd7038f4b4f0e2f51b11feb64c5d570f5c598d37850a5daf60 + languageName: node + linkType: hard + +"@microsoft/fast-element@npm:^1.12.0": + version: 1.12.0 + resolution: "@microsoft/fast-element@npm:1.12.0" + checksum: bbff4e9c83106d1d74f3eeedc87bf84832429e78fee59c6a4ae8164ee4f42667503f586896bea72341b4d2c76c244a3cb0d4fd0d5d3732755f00357714dd609e + languageName: node + linkType: hard + +"@microsoft/fast-foundation@npm:^2.49.4, @microsoft/fast-foundation@npm:^2.49.5": + version: 2.49.5 + resolution: "@microsoft/fast-foundation@npm:2.49.5" + dependencies: + "@microsoft/fast-element": ^1.12.0 + "@microsoft/fast-web-utilities": ^5.4.1 + tabbable: ^5.2.0 + tslib: ^1.13.0 + checksum: 8a4729e8193ee93f780dc88fac26561b42f2636e3f0a8e89bb1dfe256f50a01a21ed1d8e4d31ce40678807dc833e25f31ba735cb5d3c247b65219aeb2560c82c + languageName: node + linkType: hard + +"@microsoft/fast-react-wrapper@npm:^0.3.22": + version: 0.3.23 + resolution: "@microsoft/fast-react-wrapper@npm:0.3.23" + dependencies: + "@microsoft/fast-element": ^1.12.0 + "@microsoft/fast-foundation": ^2.49.5 + peerDependencies: + react: ">=16.9.0" + checksum: 45885e1868916d2aa9059e99c341c97da434331d9340a57128d4218081df68b5e1107031c608db9a550d6d1c3b010d516ed4f8dc5a8a2470058da6750dcd204a + languageName: node + linkType: hard + +"@microsoft/fast-web-utilities@npm:^5.4.1": + version: 5.4.1 + resolution: "@microsoft/fast-web-utilities@npm:5.4.1" + dependencies: + exenv-es6: ^1.1.1 + checksum: 303e87847f962944f474e3716c3eb305668243916ca9e0719e26bb9a32346144bc958d915c103776b3e552cea0f0f6233f839fad66adfdf96a8436b947288ca7 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -3786,6 +4219,22 @@ __metadata: languageName: node linkType: hard +"@rjsf/core@npm:^5.13.4": + version: 5.17.1 + resolution: "@rjsf/core@npm:5.17.1" + dependencies: + lodash: ^4.17.21 + lodash-es: ^4.17.21 + markdown-to-jsx: ^7.4.1 + nanoid: ^3.3.7 + prop-types: ^15.8.1 + peerDependencies: + "@rjsf/utils": ^5.16.x + react: ^16.14.0 || >=17 + checksum: 2dead2886a4db152d259d3e85281c1fa5975eeac5f05c2840201ccc583ef1cf9d48c922cd404d515133e140eae7a8fca4aa63ccde0bcfe63d0b3fbe3cd621aed + languageName: node + linkType: hard + "@rjsf/utils@npm:^5.1.0": version: 5.6.2 resolution: "@rjsf/utils@npm:5.6.2" @@ -3801,6 +4250,21 @@ __metadata: languageName: node linkType: hard +"@rjsf/utils@npm:^5.13.4": + version: 5.17.1 + resolution: "@rjsf/utils@npm:5.17.1" + dependencies: + json-schema-merge-allof: ^0.8.1 + jsonpointer: ^5.0.1 + lodash: ^4.17.21 + lodash-es: ^4.17.21 + react-is: ^18.2.0 + peerDependencies: + react: ^16.14.0 || >=17 + checksum: 83010de66b06f1046b023a0b7d0bf30b5f47b152893c3b12f1f42faa89e7c7d18b2f04fe2e9035e5f63454317f09e6d5753fc014d43b933c8023b71fc50c3acf + languageName: node + linkType: hard + "@sigstore/protobuf-specs@npm:^0.1.0": version: 0.1.0 resolution: "@sigstore/protobuf-specs@npm:0.1.0" @@ -6727,6 +7191,13 @@ __metadata: languageName: node linkType: hard +"exenv-es6@npm:^1.1.1": + version: 1.1.1 + resolution: "exenv-es6@npm:1.1.1" + checksum: 7f2aa12025e6f06c48dc286f380cf3183bb19c6017b36d91695034a3e5124a7235c4f8ff24ca2eb88ae801322f0f99605cedfcfd996a5fcbba7669320e2a448e + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -9575,6 +10046,15 @@ __metadata: languageName: node linkType: hard +"markdown-to-jsx@npm:^7.4.1": + version: 7.4.1 + resolution: "markdown-to-jsx@npm:7.4.1" + peerDependencies: + react: ">= 0.14.0" + checksum: 2888cb2389cb810ab35454a59d0623474a60a78e28f281ae0081f87053f6c59b033232a2cd269cc383a5edcaa1eab8ca4b3cf639fe4e1aa3fb418648d14bcc7d + languageName: node + linkType: hard + "marked@npm:^4.2.12": version: 4.3.0 resolution: "marked@npm:4.3.0" @@ -9946,6 +10426,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 + languageName: node + linkType: hard + "napi-macros@npm:~2.0.0": version: 2.0.0 resolution: "napi-macros@npm:2.0.0" @@ -11372,7 +11861,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0": +"react@npm:>=17.0.0 <19.0.0, react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" dependencies: @@ -12574,6 +13063,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^5.2.0": + version: 5.3.3 + resolution: "tabbable@npm:5.3.3" + checksum: 1aa56e1bb617cc10616c407f4e756f0607f3e2d30f9803664d70b85db037ca27e75918ed1c71443f3dc902e21dc9f991ce4b52d63a538c9b69b3218d3babcd70 + languageName: node + linkType: hard + "table@npm:^6.8.1": version: 6.8.1 resolution: "table@npm:6.8.1" @@ -12871,7 +13367,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1": +"tslib@npm:^1.13.0, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd