@jupyter/docprovider 1.2.0 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/awareness.d.ts +12 -8
- package/lib/awareness.js +17 -17
- package/lib/tokens.d.ts +2 -9
- package/lib/utils.d.ts +11 -0
- package/lib/utils.js +17 -0
- package/lib/ydrive.js +8 -2
- package/lib/yprovider.d.ts +5 -0
- package/lib/yprovider.js +60 -1
- package/package.json +5 -5
package/lib/awareness.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { User } from '@jupyterlab/services';
|
|
2
2
|
import { IDisposable } from '@lumino/disposable';
|
|
3
|
-
import {
|
|
3
|
+
import { IStream } from '@lumino/signaling';
|
|
4
|
+
import { IAwareness } from '@jupyter/ydoc';
|
|
4
5
|
import { WebsocketProvider } from 'y-websocket';
|
|
5
|
-
import {
|
|
6
|
-
export
|
|
7
|
-
|
|
6
|
+
import { IAwarenessProvider } from './tokens';
|
|
7
|
+
export interface IContent {
|
|
8
|
+
type: string;
|
|
9
|
+
body: string;
|
|
8
10
|
}
|
|
9
11
|
export interface IChatMessage {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
sender: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
content: IContent;
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
17
|
* A class to provide Yjs synchronization over WebSocket.
|
|
@@ -27,7 +30,7 @@ export declare class WebSocketAwarenessProvider extends WebsocketProvider implem
|
|
|
27
30
|
/**
|
|
28
31
|
* A signal to subscribe for incoming messages.
|
|
29
32
|
*/
|
|
30
|
-
get
|
|
33
|
+
get messageStream(): IStream<this, IChatMessage>;
|
|
31
34
|
dispose(): void;
|
|
32
35
|
/**
|
|
33
36
|
* Send a message to every collaborator.
|
|
@@ -37,8 +40,9 @@ export declare class WebSocketAwarenessProvider extends WebsocketProvider implem
|
|
|
37
40
|
sendMessage(msg: string): void;
|
|
38
41
|
private _onUserChanged;
|
|
39
42
|
private _isDisposed;
|
|
43
|
+
private _user;
|
|
40
44
|
private _awareness;
|
|
41
|
-
private
|
|
45
|
+
private _messageStream;
|
|
42
46
|
}
|
|
43
47
|
/**
|
|
44
48
|
* A namespace for WebSocketAwarenessProvider statics.
|
package/lib/awareness.js
CHANGED
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
| Copyright (c) Jupyter Development Team.
|
|
3
3
|
| Distributed under the terms of the Modified BSD License.
|
|
4
4
|
|----------------------------------------------------------------------------*/
|
|
5
|
-
import {
|
|
5
|
+
import { Stream } from '@lumino/signaling';
|
|
6
6
|
import * as decoding from 'lib0/decoding';
|
|
7
7
|
import * as encoding from 'lib0/encoding';
|
|
8
8
|
import { WebsocketProvider } from 'y-websocket';
|
|
9
|
-
|
|
10
|
-
(function (MessageType) {
|
|
11
|
-
MessageType[MessageType["CHAT"] = 125] = "CHAT";
|
|
12
|
-
})(MessageType || (MessageType = {}));
|
|
9
|
+
import { MessageType } from './utils';
|
|
13
10
|
/**
|
|
14
11
|
* A class to provide Yjs synchronization over WebSocket.
|
|
15
12
|
*
|
|
@@ -28,17 +25,16 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
|
|
|
28
25
|
});
|
|
29
26
|
this._isDisposed = false;
|
|
30
27
|
this._awareness = options.awareness;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.then(() => this._onUserChanged(
|
|
28
|
+
this._user = options.user;
|
|
29
|
+
this._user.ready
|
|
30
|
+
.then(() => this._onUserChanged(this._user))
|
|
34
31
|
.catch(e => console.error(e));
|
|
35
|
-
|
|
36
|
-
this.
|
|
32
|
+
this._user.userChanged.connect(this._onUserChanged, this);
|
|
33
|
+
this._messageStream = new Stream(this);
|
|
37
34
|
this.messageHandlers[MessageType.CHAT] = (encoder, decoder, provider, emitSynced, messageType) => {
|
|
38
35
|
const content = decoding.readVarString(decoder);
|
|
39
36
|
const data = JSON.parse(content);
|
|
40
|
-
|
|
41
|
-
this._chatMessage.emit(data);
|
|
37
|
+
this._messageStream.emit(data);
|
|
42
38
|
};
|
|
43
39
|
}
|
|
44
40
|
get isDisposed() {
|
|
@@ -47,15 +43,16 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
|
|
|
47
43
|
/**
|
|
48
44
|
* A signal to subscribe for incoming messages.
|
|
49
45
|
*/
|
|
50
|
-
get
|
|
51
|
-
return this.
|
|
46
|
+
get messageStream() {
|
|
47
|
+
return this._messageStream;
|
|
52
48
|
}
|
|
53
49
|
dispose() {
|
|
54
50
|
if (this._isDisposed) {
|
|
55
51
|
return;
|
|
56
52
|
}
|
|
57
|
-
this.
|
|
53
|
+
this._user.userChanged.disconnect(this._onUserChanged, this);
|
|
58
54
|
this._isDisposed = true;
|
|
55
|
+
this.destroy();
|
|
59
56
|
}
|
|
60
57
|
/**
|
|
61
58
|
* Send a message to every collaborator.
|
|
@@ -63,10 +60,13 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
|
|
|
63
60
|
* @param msg message
|
|
64
61
|
*/
|
|
65
62
|
sendMessage(msg) {
|
|
66
|
-
|
|
63
|
+
const data = {
|
|
64
|
+
type: 'text',
|
|
65
|
+
body: msg
|
|
66
|
+
};
|
|
67
67
|
const encoder = encoding.createEncoder();
|
|
68
68
|
encoding.writeVarUint(encoder, MessageType.CHAT);
|
|
69
|
-
encoding.writeVarString(encoder,
|
|
69
|
+
encoding.writeVarString(encoder, JSON.stringify(data));
|
|
70
70
|
this.ws.send(encoding.toUint8Array(encoder));
|
|
71
71
|
}
|
|
72
72
|
_onUserChanged(user) {
|
package/lib/tokens.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { DocumentChange, YDocument } from '@jupyter/ydoc';
|
|
2
2
|
import { Contents } from '@jupyterlab/services';
|
|
3
3
|
import { Token } from '@lumino/coreutils';
|
|
4
|
-
import {
|
|
5
|
-
import type { Awareness } from 'y-protocols/awareness';
|
|
4
|
+
import { IStream } from '@lumino/signaling';
|
|
6
5
|
import { IChatMessage } from './awareness';
|
|
7
6
|
/**
|
|
8
7
|
* The collaborative drive.
|
|
@@ -34,12 +33,6 @@ export interface ISharedModelFactory extends Contents.ISharedFactory {
|
|
|
34
33
|
*/
|
|
35
34
|
registerDocumentFactory(type: Contents.ContentType, factory: SharedDocumentFactory): void;
|
|
36
35
|
}
|
|
37
|
-
/**
|
|
38
|
-
* The awareness interface.
|
|
39
|
-
*
|
|
40
|
-
* TODO: Move to @jupyter/YDoc
|
|
41
|
-
*/
|
|
42
|
-
export type IAwareness = Awareness;
|
|
43
36
|
/**
|
|
44
37
|
* A provider interface for global awareness features.
|
|
45
38
|
*/
|
|
@@ -47,7 +40,7 @@ export interface IAwarenessProvider {
|
|
|
47
40
|
/**
|
|
48
41
|
* A signal to subscribe for incoming messages.
|
|
49
42
|
*/
|
|
50
|
-
|
|
43
|
+
readonly messageStream: IStream<this, IChatMessage>;
|
|
51
44
|
/**
|
|
52
45
|
* Send a message to every collaborator.
|
|
53
46
|
*
|
package/lib/utils.d.ts
ADDED
package/lib/utils.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* -----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|----------------------------------------------------------------------------*/
|
|
5
|
+
export var MessageType;
|
|
6
|
+
(function (MessageType) {
|
|
7
|
+
MessageType[MessageType["ROOM"] = 124] = "ROOM";
|
|
8
|
+
MessageType[MessageType["CHAT"] = 125] = "CHAT";
|
|
9
|
+
})(MessageType || (MessageType = {}));
|
|
10
|
+
export var RoomMessage;
|
|
11
|
+
(function (RoomMessage) {
|
|
12
|
+
RoomMessage[RoomMessage["RELOAD"] = 0] = "RELOAD";
|
|
13
|
+
RoomMessage[RoomMessage["OVERWRITE"] = 1] = "OVERWRITE";
|
|
14
|
+
RoomMessage[RoomMessage["FILE_CHANGED"] = 2] = "FILE_CHANGED";
|
|
15
|
+
RoomMessage[RoomMessage["FILE_OVERWRITTEN"] = 3] = "FILE_OVERWRITTEN";
|
|
16
|
+
RoomMessage[RoomMessage["DOC_OVERWRITTEN"] = 4] = "DOC_OVERWRITTEN";
|
|
17
|
+
})(RoomMessage || (RoomMessage = {}));
|
package/lib/ydrive.js
CHANGED
|
@@ -82,8 +82,14 @@ export class YDrive extends Drive {
|
|
|
82
82
|
const key = `${options.format}:${options.type}:${localPath}`;
|
|
83
83
|
const provider = this._providers.get(key);
|
|
84
84
|
if (provider) {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
// If the document does't exist, `super.get` will reject with an
|
|
86
|
+
// error and the provider will never be resolved.
|
|
87
|
+
// Use `Promise.all` to reject as soon as possible. The Context will
|
|
88
|
+
// show a dialog to the user.
|
|
89
|
+
const [model] = await Promise.all([
|
|
90
|
+
super.get(localPath, { ...options, content: false }),
|
|
91
|
+
provider.ready
|
|
92
|
+
]);
|
|
87
93
|
return model;
|
|
88
94
|
}
|
|
89
95
|
}
|
package/lib/yprovider.d.ts
CHANGED
|
@@ -40,6 +40,11 @@ export declare class WebSocketProvider implements IDocumentProvider {
|
|
|
40
40
|
private _onUserChanged;
|
|
41
41
|
private _onConnectionClosed;
|
|
42
42
|
private _onSync;
|
|
43
|
+
private _handleRoomMessage;
|
|
44
|
+
private _handleFileChanged;
|
|
45
|
+
private _sendReloadMsg;
|
|
46
|
+
private _sendOverwriteMsg;
|
|
47
|
+
private _dialog;
|
|
43
48
|
private _awareness;
|
|
44
49
|
private _contentType;
|
|
45
50
|
private _format;
|
package/lib/yprovider.js
CHANGED
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
import { showErrorMessage, Dialog } from '@jupyterlab/apputils';
|
|
6
6
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
7
7
|
import { Signal } from '@lumino/signaling';
|
|
8
|
+
import * as decoding from 'lib0/decoding';
|
|
9
|
+
import * as encoding from 'lib0/encoding';
|
|
8
10
|
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
9
11
|
import { requestDocSession } from './requests';
|
|
12
|
+
import { MessageType, RoomMessage } from './utils';
|
|
10
13
|
/**
|
|
11
14
|
* A class to provide Yjs synchronization over WebSocket.
|
|
12
15
|
*
|
|
@@ -38,6 +41,7 @@ export class WebSocketProvider {
|
|
|
38
41
|
(_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.off('sync', this._onSync);
|
|
39
42
|
}
|
|
40
43
|
};
|
|
44
|
+
this._dialog = null;
|
|
41
45
|
this._ready = new PromiseDelegate();
|
|
42
46
|
this._isDisposed = false;
|
|
43
47
|
this._path = options.path;
|
|
@@ -85,15 +89,70 @@ export class WebSocketProvider {
|
|
|
85
89
|
}
|
|
86
90
|
async _connect() {
|
|
87
91
|
const session = await requestDocSession(this._format, this._contentType, this._path);
|
|
92
|
+
const params = session.sessionId !== null ? { sessionId: session.sessionId } : undefined;
|
|
88
93
|
this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, `${session.format}:${session.type}:${session.fileId}`, this._sharedModel.ydoc, {
|
|
89
94
|
disableBc: true,
|
|
90
|
-
params
|
|
95
|
+
params,
|
|
91
96
|
awareness: this._awareness
|
|
92
97
|
});
|
|
93
98
|
this._yWebsocketProvider.on('sync', this._onSync);
|
|
94
99
|
this._yWebsocketProvider.on('connection-close', this._onConnectionClosed);
|
|
100
|
+
this._yWebsocketProvider.messageHandlers[MessageType.ROOM] = (encoder, decoder, provider, emitSynced, messageType) => {
|
|
101
|
+
const msgType = decoding.readVarUint(decoder);
|
|
102
|
+
const data = decoding.readVarString(decoder);
|
|
103
|
+
this._handleRoomMessage(msgType, data);
|
|
104
|
+
};
|
|
95
105
|
}
|
|
96
106
|
_onUserChanged(user) {
|
|
97
107
|
this._awareness.setLocalStateField('user', user.identity);
|
|
98
108
|
}
|
|
109
|
+
_handleRoomMessage(type, data) {
|
|
110
|
+
switch (type) {
|
|
111
|
+
case RoomMessage.FILE_CHANGED:
|
|
112
|
+
this._handleFileChanged(data);
|
|
113
|
+
break;
|
|
114
|
+
case RoomMessage.DOC_OVERWRITTEN:
|
|
115
|
+
case RoomMessage.FILE_OVERWRITTEN:
|
|
116
|
+
if (this._dialog) {
|
|
117
|
+
this._dialog.close();
|
|
118
|
+
this._dialog = null;
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
_handleFileChanged(data) {
|
|
124
|
+
this._dialog = new Dialog({
|
|
125
|
+
title: this._trans.__('File changed'),
|
|
126
|
+
body: this._trans.__('Do you want to overwrite the file or reload it?'),
|
|
127
|
+
buttons: [
|
|
128
|
+
Dialog.okButton({ label: 'Reload' }),
|
|
129
|
+
Dialog.warnButton({ label: 'Overwrite' })
|
|
130
|
+
],
|
|
131
|
+
hasClose: false
|
|
132
|
+
});
|
|
133
|
+
this._dialog.launch().then(resp => {
|
|
134
|
+
if (resp.button.label === 'Reload') {
|
|
135
|
+
this._sendReloadMsg(data);
|
|
136
|
+
}
|
|
137
|
+
else if (resp.button.label === 'Overwrite') {
|
|
138
|
+
this._sendOverwriteMsg(data);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
_sendReloadMsg(data) {
|
|
143
|
+
var _a;
|
|
144
|
+
const encoder = encoding.createEncoder();
|
|
145
|
+
encoding.writeVarUint(encoder, MessageType.ROOM);
|
|
146
|
+
encoding.writeVarUint(encoder, RoomMessage.RELOAD);
|
|
147
|
+
encoding.writeVarString(encoder, data);
|
|
148
|
+
(_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.ws.send(encoding.toUint8Array(encoder));
|
|
149
|
+
}
|
|
150
|
+
_sendOverwriteMsg(data) {
|
|
151
|
+
var _a;
|
|
152
|
+
const encoder = encoding.createEncoder();
|
|
153
|
+
encoding.writeVarUint(encoder, MessageType.ROOM);
|
|
154
|
+
encoding.writeVarUint(encoder, RoomMessage.OVERWRITE);
|
|
155
|
+
encoding.writeVarString(encoder, data);
|
|
156
|
+
(_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.ws.send(encoding.toUint8Array(encoder));
|
|
157
|
+
}
|
|
99
158
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyter/docprovider",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.1",
|
|
4
4
|
"description": "JupyterLab - Document Provider",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyter_collaboration",
|
|
6
6
|
"bugs": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"watch": "tsc -b --watch"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@jupyter/ydoc": "^1.0
|
|
45
|
-
"@jupyterlab/coreutils": "^6.0.
|
|
46
|
-
"@jupyterlab/services": "^7.0.
|
|
44
|
+
"@jupyter/ydoc": "^1.1.0-a0",
|
|
45
|
+
"@jupyterlab/coreutils": "^6.0.5",
|
|
46
|
+
"@jupyterlab/services": "^7.0.5",
|
|
47
47
|
"@lumino/coreutils": "^2.1.0",
|
|
48
48
|
"@lumino/disposable": "^2.1.0",
|
|
49
49
|
"@lumino/signaling": "^2.1.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"yjs": "^13.5.40"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@jupyterlab/testing": "^4.0.
|
|
55
|
+
"@jupyterlab/testing": "^4.0.5",
|
|
56
56
|
"@types/jest": "^29.2.0",
|
|
57
57
|
"jest": "^29.5.0",
|
|
58
58
|
"rimraf": "^4.1.2",
|