@jupyter/docprovider 3.0.0-beta.2 → 3.0.0-beta.4

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.
@@ -0,0 +1,11 @@
1
+ import { ReactWidget } from '@jupyterlab/apputils';
2
+ import { IForkProvider } from './ydrive';
3
+ export declare class TimelineWidget extends ReactWidget {
4
+ private apiURL;
5
+ private provider;
6
+ private contentType;
7
+ private format;
8
+ constructor(apiURL: string, provider: IForkProvider, contentType: string, format: string);
9
+ render(): JSX.Element;
10
+ updateContent(apiURL: string, provider: IForkProvider): void;
11
+ }
@@ -0,0 +1,27 @@
1
+ /* -----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |----------------------------------------------------------------------------*/
5
+ import { ReactWidget } from '@jupyterlab/apputils';
6
+ import { TimelineSliderComponent } from './component';
7
+ import * as React from 'react';
8
+ export class TimelineWidget extends ReactWidget {
9
+ constructor(apiURL, provider, contentType, format) {
10
+ super();
11
+ this.apiURL = apiURL;
12
+ this.provider = provider;
13
+ this.contentType = contentType;
14
+ this.format = format;
15
+ this.addClass('jp-timelineSliderWrapper');
16
+ }
17
+ render() {
18
+ return (React.createElement(TimelineSliderComponent, { key: this.apiURL, apiURL: this.apiURL, provider: this.provider, contentType: this.contentType, format: this.format }));
19
+ }
20
+ updateContent(apiURL, provider) {
21
+ this.apiURL = apiURL;
22
+ this.provider = provider;
23
+ this.contentType = this.provider.contentType;
24
+ this.format = this.provider.format;
25
+ this.update();
26
+ }
27
+ }
@@ -1,25 +1,18 @@
1
1
  import { User } from '@jupyterlab/services';
2
2
  import { IDisposable } from '@lumino/disposable';
3
- import { IStream } from '@lumino/signaling';
4
3
  import { IAwareness } from '@jupyter/ydoc';
5
4
  import { WebsocketProvider } from 'y-websocket';
6
- import { IAwarenessProvider } from './tokens';
7
5
  export interface IContent {
8
6
  type: string;
9
7
  body: string;
10
8
  }
11
- export interface IChatMessage {
12
- sender: string;
13
- timestamp: number;
14
- content: IContent;
15
- }
16
9
  /**
17
10
  * A class to provide Yjs synchronization over WebSocket.
18
11
  *
19
12
  * We specify custom messages that the server can interpret. For reference please look in yjs_ws_server.
20
13
  *
21
14
  */
22
- export declare class WebSocketAwarenessProvider extends WebsocketProvider implements IAwarenessProvider, IDisposable {
15
+ export declare class WebSocketAwarenessProvider extends WebsocketProvider implements IDisposable {
23
16
  /**
24
17
  * Construct a new WebSocketAwarenessProvider
25
18
  *
@@ -27,22 +20,11 @@ export declare class WebSocketAwarenessProvider extends WebsocketProvider implem
27
20
  */
28
21
  constructor(options: WebSocketAwarenessProvider.IOptions);
29
22
  get isDisposed(): boolean;
30
- /**
31
- * A signal to subscribe for incoming messages.
32
- */
33
- get messageStream(): IStream<this, IChatMessage>;
34
23
  dispose(): void;
35
- /**
36
- * Send a message to every collaborator.
37
- *
38
- * @param msg message
39
- */
40
- sendMessage(msg: string): void;
41
24
  private _onUserChanged;
42
25
  private _isDisposed;
43
26
  private _user;
44
27
  private _awareness;
45
- private _messageStream;
46
28
  }
47
29
  /**
48
30
  * A namespace for WebSocketAwarenessProvider statics.
package/lib/awareness.js CHANGED
@@ -2,11 +2,7 @@
2
2
  | Copyright (c) Jupyter Development Team.
3
3
  | Distributed under the terms of the Modified BSD License.
4
4
  |----------------------------------------------------------------------------*/
5
- import { Stream } from '@lumino/signaling';
6
- import * as decoding from 'lib0/decoding';
7
- import * as encoding from 'lib0/encoding';
8
5
  import { WebsocketProvider } from 'y-websocket';
9
- import { MessageType } from './utils';
10
6
  /**
11
7
  * A class to provide Yjs synchronization over WebSocket.
12
8
  *
@@ -30,22 +26,10 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
30
26
  .then(() => this._onUserChanged(this._user))
31
27
  .catch(e => console.error(e));
32
28
  this._user.userChanged.connect(this._onUserChanged, this);
33
- this._messageStream = new Stream(this);
34
- this.messageHandlers[MessageType.CHAT] = (encoder, decoder, provider, emitSynced, messageType) => {
35
- const content = decoding.readVarString(decoder);
36
- const data = JSON.parse(content);
37
- this._messageStream.emit(data);
38
- };
39
29
  }
40
30
  get isDisposed() {
41
31
  return this._isDisposed;
42
32
  }
43
- /**
44
- * A signal to subscribe for incoming messages.
45
- */
46
- get messageStream() {
47
- return this._messageStream;
48
- }
49
33
  dispose() {
50
34
  if (this._isDisposed) {
51
35
  return;
@@ -54,21 +38,6 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
54
38
  this._isDisposed = true;
55
39
  this.destroy();
56
40
  }
57
- /**
58
- * Send a message to every collaborator.
59
- *
60
- * @param msg message
61
- */
62
- sendMessage(msg) {
63
- const data = {
64
- type: 'text',
65
- body: msg
66
- };
67
- const encoder = encoding.createEncoder();
68
- encoding.writeVarUint(encoder, MessageType.CHAT);
69
- encoding.writeVarString(encoder, JSON.stringify(data));
70
- this.ws.send(encoding.toUint8Array(encoder));
71
- }
72
41
  _onUserChanged(user) {
73
42
  this._awareness.setLocalStateField('user', user.identity);
74
43
  }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import '../style/slider.css';
3
+ import { IForkProvider } from './ydrive';
4
+ type Props = {
5
+ apiURL: string;
6
+ provider: IForkProvider;
7
+ contentType: string;
8
+ format: string;
9
+ };
10
+ export declare const TimelineSliderComponent: React.FC<Props>;
11
+ export {};
@@ -0,0 +1,135 @@
1
+ /* -----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |----------------------------------------------------------------------------*/
5
+ import React, { useState, useRef } from 'react';
6
+ import '../style/slider.css';
7
+ import { requestUndoRedo, requestDocSession, requestDocumentTimeline } from './requests';
8
+ import { historyIcon } from '@jupyterlab/ui-components';
9
+ import { Notification } from '@jupyterlab/apputils';
10
+ export const TimelineSliderComponent = ({ apiURL, provider, contentType, format }) => {
11
+ const [data, setData] = useState({
12
+ roomId: '',
13
+ timestamps: [],
14
+ forkRoom: '',
15
+ sessionId: ''
16
+ });
17
+ const [currentTimestampIndex, setCurrentTimestampIndex] = useState(data.timestamps.length - 1);
18
+ const [toggle, setToggle] = useState(false);
19
+ const [isBtn, setIsBtn] = useState(false);
20
+ const isFirstChange = useRef(true);
21
+ const isFirstSliderChange = useRef(true);
22
+ const sessionRef = useRef(null);
23
+ async function fetchTimeline(notebookPath) {
24
+ try {
25
+ if (isFirstChange.current) {
26
+ const response = await requestDocumentTimeline(format, contentType, notebookPath);
27
+ if (!response.ok) {
28
+ if (response.status === 404) {
29
+ throw new Error('Not found');
30
+ }
31
+ else if (response.status === 503) {
32
+ throw new Error('WebSocket closed');
33
+ }
34
+ else {
35
+ throw new Error(`Failed to fetch data: ${response.statusText}`);
36
+ }
37
+ }
38
+ const text = await response.text();
39
+ let data = { roomId: '', timestamps: [], forkRoom: '', sessionId: '' };
40
+ if (text) {
41
+ Notification.warning('Document is now in read-only mode. Changes will not be saved.', { autoClose: 2500 });
42
+ data = JSON.parse(text);
43
+ setData(data);
44
+ setCurrentTimestampIndex(data.timestamps.length - 1);
45
+ provider.connectToForkDoc(data.forkRoom, data.sessionId);
46
+ sessionRef.current = await requestDocSession(format, contentType, extractFilenameFromURL(apiURL));
47
+ }
48
+ setToggle(true);
49
+ isFirstChange.current = false;
50
+ return data;
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.error('Error fetching data:', error);
55
+ }
56
+ }
57
+ const handleRestore = async () => {
58
+ if (!sessionRef.current) {
59
+ console.error('Session is not initialized');
60
+ return;
61
+ }
62
+ const response = await requestUndoRedo(`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`, 'restore', 0, data.forkRoom);
63
+ if (response.code === 200) {
64
+ Notification.success(response.status, { autoClose: 4000 });
65
+ provider.reconnect();
66
+ setToggle(false);
67
+ isFirstChange.current = true;
68
+ }
69
+ else {
70
+ Notification.error(response.status, { autoClose: 4000 });
71
+ }
72
+ };
73
+ const handleSliderChange = async (event) => {
74
+ const currentTimestamp = parseInt(event.target.value);
75
+ const steps = Math.abs(currentTimestamp - currentTimestampIndex);
76
+ try {
77
+ const action = determineAction(currentTimestamp);
78
+ setCurrentTimestampIndex(currentTimestamp);
79
+ if (isFirstSliderChange.current) {
80
+ setIsBtn(true);
81
+ isFirstSliderChange.current = false;
82
+ }
83
+ if (!sessionRef.current) {
84
+ console.error('Session is not initialized');
85
+ return;
86
+ }
87
+ await requestUndoRedo(`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`, action, steps, data.forkRoom);
88
+ }
89
+ catch (error) {
90
+ console.error('Error fetching or applying updates:', error);
91
+ }
92
+ };
93
+ function determineAction(currentTimestamp) {
94
+ return currentTimestamp < currentTimestampIndex ? 'undo' : 'redo';
95
+ }
96
+ function extractFilenameFromURL(url) {
97
+ try {
98
+ const parsedURL = new URL(url);
99
+ const pathname = parsedURL.pathname;
100
+ const segments = pathname.split('/');
101
+ return segments.slice(4 - segments.length).join('/');
102
+ }
103
+ catch (error) {
104
+ console.error('Invalid URL:', error);
105
+ return '';
106
+ }
107
+ }
108
+ const formatTimestamp = (timestamp) => {
109
+ const date = new Date(timestamp * 1000);
110
+ const year = date.getFullYear();
111
+ const month = String(date.getMonth() + 1).padStart(2, '0');
112
+ const day = String(date.getDate()).padStart(2, '0');
113
+ const hours = String(date.getHours()).padStart(2, '0');
114
+ const minutes = String(date.getMinutes()).padStart(2, '0');
115
+ const seconds = String(date.getSeconds()).padStart(2, '0');
116
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
117
+ };
118
+ return (React.createElement("div", { className: "jp-sliderContainer" },
119
+ React.createElement("div", { onClick: () => {
120
+ fetchTimeline(extractFilenameFromURL(apiURL));
121
+ }, className: "jp-mod-highlighted", title: "Document Timeline" },
122
+ React.createElement(historyIcon.react, { marginRight: "4px" })),
123
+ toggle && (React.createElement("div", { className: "jp-timestampDisplay" },
124
+ React.createElement("input", { type: "range", min: 0, max: data.timestamps.length - 1, value: currentTimestampIndex, onChange: handleSliderChange, className: "jp-Slider" }),
125
+ React.createElement("div", null,
126
+ React.createElement("strong", null,
127
+ extractFilenameFromURL(apiURL).split('/').pop(),
128
+ " "),
129
+ ' '),
130
+ isBtn && (React.createElement("div", { className: "jp-restoreBtnContainer" },
131
+ React.createElement("button", { onClick: handleRestore, className: "jp-ToolbarButtonComponent jp-restoreBtn" },
132
+ "Restore version",
133
+ ' ',
134
+ formatTimestamp(data.timestamps[currentTimestampIndex]))))))));
135
+ };
package/lib/index.d.ts CHANGED
@@ -8,3 +8,4 @@ export * from './requests';
8
8
  export * from './ydrive';
9
9
  export * from './yprovider';
10
10
  export * from './tokens';
11
+ export * from './TimelineSlider';
package/lib/index.js CHANGED
@@ -12,3 +12,4 @@ export * from './requests';
12
12
  export * from './ydrive';
13
13
  export * from './yprovider';
14
14
  export * from './tokens';
15
+ export * from './TimelineSlider';
package/lib/requests.d.ts CHANGED
@@ -21,3 +21,5 @@ export interface ISessionModel {
21
21
  sessionId: string;
22
22
  }
23
23
  export declare function requestDocSession(format: string, type: string, path: string): Promise<ISessionModel>;
24
+ export declare function requestDocumentTimeline(format: string, type: string, path: string): Promise<any>;
25
+ export declare function requestUndoRedo(roomid: string, action: 'undo' | 'redo' | 'restore', steps: number, forkRoom: string): Promise<any>;
package/lib/requests.js CHANGED
@@ -9,6 +9,8 @@ import { ServerConnection } from '@jupyterlab/services';
9
9
  * See https://github.com/jupyterlab/jupyter_collaboration
10
10
  */
11
11
  const DOC_SESSION_URL = 'api/collaboration/session';
12
+ const DOC_FORK_URL = 'api/collaboration/undo_redo';
13
+ const TIMELINE_URL = 'api/collaboration/timeline';
12
14
  export async function requestDocSession(format, type, path) {
13
15
  const settings = ServerConnection.makeSettings();
14
16
  const url = URLExt.join(settings.baseUrl, DOC_SESSION_URL, encodeURIComponent(path));
@@ -37,3 +39,45 @@ export async function requestDocSession(format, type, path) {
37
39
  }
38
40
  return data;
39
41
  }
42
+ export async function requestDocumentTimeline(format, type, path) {
43
+ const settings = ServerConnection.makeSettings();
44
+ let url = URLExt.join(settings.baseUrl, TIMELINE_URL, path);
45
+ url = url.concat(`?format=${format}&&type=${type}`);
46
+ const body = {
47
+ method: 'GET'
48
+ };
49
+ let response;
50
+ try {
51
+ response = await ServerConnection.makeRequest(url, body, settings);
52
+ }
53
+ catch (error) {
54
+ throw new ServerConnection.NetworkError(error);
55
+ }
56
+ return response;
57
+ }
58
+ export async function requestUndoRedo(roomid, action, steps, forkRoom) {
59
+ const settings = ServerConnection.makeSettings();
60
+ let url = URLExt.join(settings.baseUrl, DOC_FORK_URL, encodeURIComponent(roomid));
61
+ url = url.concat(`?action=${action}&&steps=${steps}&&forkRoom=${forkRoom}`);
62
+ const body = { method: 'PUT' };
63
+ let response;
64
+ try {
65
+ response = await ServerConnection.makeRequest(url, body, settings);
66
+ }
67
+ catch (error) {
68
+ throw new ServerConnection.NetworkError(error);
69
+ }
70
+ let data = await response.text();
71
+ if (data.length > 0) {
72
+ try {
73
+ data = JSON.parse(data);
74
+ }
75
+ catch (error) {
76
+ console.log('Not a JSON response body.', response);
77
+ }
78
+ }
79
+ if (!response.ok) {
80
+ throw new ServerConnection.ResponseError(response, data.message || data);
81
+ }
82
+ return data;
83
+ }
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 { IStream } from '@lumino/signaling';
5
- import { IChatMessage } from './awareness';
4
+ import { WebSocketProvider } from './yprovider';
6
5
  /**
7
6
  * The collaborative drive.
8
7
  */
@@ -24,6 +23,7 @@ export interface ICollaborativeDrive extends Contents.IDrive {
24
23
  * SharedModel factory for the YDrive.
25
24
  */
26
25
  readonly sharedModelFactory: ISharedModelFactory;
26
+ readonly providers: Map<string, WebSocketProvider>;
27
27
  }
28
28
  /**
29
29
  * Yjs sharedModel factory for real-time collaboration.
@@ -37,18 +37,3 @@ export interface ISharedModelFactory extends Contents.ISharedFactory {
37
37
  */
38
38
  registerDocumentFactory(type: Contents.ContentType, factory: SharedDocumentFactory): void;
39
39
  }
40
- /**
41
- * A provider interface for global awareness features.
42
- */
43
- export interface IAwarenessProvider {
44
- /**
45
- * A signal to subscribe for incoming messages.
46
- */
47
- readonly messageStream: IStream<this, IChatMessage>;
48
- /**
49
- * Send a message to every collaborator.
50
- *
51
- * @param msg message
52
- */
53
- sendMessage(msg: string): void;
54
- }
package/lib/ydrive.d.ts CHANGED
@@ -1,7 +1,15 @@
1
1
  import { TranslationBundle } from '@jupyterlab/translation';
2
2
  import { Contents, Drive, User } from '@jupyterlab/services';
3
+ import { ISignal } from '@lumino/signaling';
4
+ import { WebSocketProvider } from './yprovider';
3
5
  import { ICollaborativeDrive, ISharedModelFactory } from './tokens';
4
6
  import { Awareness } from 'y-protocols/awareness';
7
+ export interface IForkProvider {
8
+ connectToForkDoc: (forkRoomId: string, sessionId: string) => Promise<void>;
9
+ reconnect: () => Promise<void>;
10
+ contentType: string;
11
+ format: string;
12
+ }
5
13
  /**
6
14
  * A Collaborative implementation for an `IDrive`, talking to the
7
15
  * server using the Jupyter REST API and a WebSocket connection.
@@ -17,6 +25,7 @@ export declare class YDrive extends Drive implements ICollaborativeDrive {
17
25
  * SharedModel factory for the YDrive.
18
26
  */
19
27
  readonly sharedModelFactory: ISharedModelFactory;
28
+ get providers(): Map<string, WebSocketProvider>;
20
29
  /**
21
30
  * Dispose of the resources held by the manager.
22
31
  */
@@ -44,9 +53,14 @@ export declare class YDrive extends Drive implements ICollaborativeDrive {
44
53
  * file is saved.
45
54
  */
46
55
  save(localPath: string, options?: Partial<Contents.IModel>): Promise<Contents.IModel>;
56
+ /**
57
+ * A signal emitted when a file operation takes place.
58
+ */
59
+ get fileChanged(): ISignal<this, Contents.IChangedArgs>;
47
60
  private _onCreate;
48
61
  private _user;
49
62
  private _trans;
50
63
  private _providers;
51
64
  private _globalAwareness;
65
+ private _ydriveFileChanged;
52
66
  }
package/lib/ydrive.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
  import { PageConfig, URLExt } from '@jupyterlab/coreutils';
4
4
  import { Drive } from '@jupyterlab/services';
5
+ import { Signal } from '@lumino/signaling';
5
6
  import { WebSocketProvider } from './yprovider';
6
7
  const DISABLE_RTC = PageConfig.getOption('disableRTC') === 'true' ? true : false;
7
8
  /**
@@ -44,6 +45,30 @@ export class YDrive extends Drive {
44
45
  }
45
46
  const key = `${options.format}:${options.contentType}:${options.path}`;
46
47
  this._providers.set(key, provider);
48
+ sharedModel.changed.connect(async (_, change) => {
49
+ if (!change.stateChange) {
50
+ return;
51
+ }
52
+ const hashChanges = change.stateChange.filter(change => change.name === 'hash');
53
+ if (hashChanges.length === 0) {
54
+ return;
55
+ }
56
+ if (hashChanges.length > 1) {
57
+ console.error('Unexpected multiple changes to hash value in a single transaction');
58
+ }
59
+ const hashChange = hashChanges[0];
60
+ // A change in hash signifies that a save occurred on the server-side
61
+ // (e.g. a collaborator performed the save) - we want to notify the
62
+ // observers about this change so that they can store the new hash value.
63
+ const model = await this.get(options.path, { content: false });
64
+ this._ydriveFileChanged.emit({
65
+ type: 'save',
66
+ newValue: { ...model, hash: hashChange.newValue },
67
+ // we do not have the old model because it was discarded when server made the change,
68
+ // we only have the old hash here (which may be empty if the file was newly created!)
69
+ oldValue: { hash: hashChange.oldValue }
70
+ });
71
+ });
47
72
  sharedModel.disposed.connect(() => {
48
73
  var _a, _b;
49
74
  const provider = this._providers.get(key);
@@ -67,11 +92,19 @@ export class YDrive extends Drive {
67
92
  console.error(`Failed to open websocket connection for ${options.path}.\n:${error}`);
68
93
  }
69
94
  };
95
+ this._ydriveFileChanged = new Signal(this);
70
96
  this._user = user;
71
97
  this._trans = translator;
72
98
  this._globalAwareness = globalAwareness;
73
99
  this._providers = new Map();
74
100
  this.sharedModelFactory = new SharedModelFactory(this._onCreate);
101
+ super.fileChanged.connect((_, change) => {
102
+ // pass through any events from the Drive superclass
103
+ this._ydriveFileChanged.emit(change);
104
+ });
105
+ }
106
+ get providers() {
107
+ return this._providers;
75
108
  }
76
109
  /**
77
110
  * Dispose of the resources held by the manager.
@@ -100,7 +133,7 @@ export class YDrive extends Drive {
100
133
  const key = `${options.format}:${options.type}:${localPath}`;
101
134
  const provider = this._providers.get(key);
102
135
  if (provider) {
103
- // If the document does't exist, `super.get` will reject with an
136
+ // If the document doesn't exist, `super.get` will reject with an
104
137
  // error and the provider will never be resolved.
105
138
  // Use `Promise.all` to reject as soon as possible. The Context will
106
139
  // show a dialog to the user.
@@ -140,6 +173,12 @@ export class YDrive extends Drive {
140
173
  }
141
174
  return super.save(localPath, options);
142
175
  }
176
+ /**
177
+ * A signal emitted when a file operation takes place.
178
+ */
179
+ get fileChanged() {
180
+ return this._ydriveFileChanged;
181
+ }
143
182
  }
144
183
  /**
145
184
  * Yjs sharedModel factory for real-time collaboration.
@@ -2,6 +2,8 @@ import { User } from '@jupyterlab/services';
2
2
  import { TranslationBundle } from '@jupyterlab/translation';
3
3
  import { IDisposable } from '@lumino/disposable';
4
4
  import { DocumentChange, YDocument } from '@jupyter/ydoc';
5
+ import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
6
+ import { IForkProvider } from './ydrive';
5
7
  /**
6
8
  * An interface for a document provider.
7
9
  */
@@ -17,7 +19,7 @@ export interface IDocumentProvider extends IDisposable {
17
19
  * We specify custom messages that the server can interpret. For reference please look in yjs_ws_server.
18
20
  *
19
21
  */
20
- export declare class WebSocketProvider implements IDocumentProvider {
22
+ export declare class WebSocketProvider implements IDocumentProvider, IForkProvider {
21
23
  /**
22
24
  * Construct a new WebSocketProvider
23
25
  *
@@ -32,11 +34,17 @@ export declare class WebSocketProvider implements IDocumentProvider {
32
34
  * A promise that resolves when the document provider is ready.
33
35
  */
34
36
  get ready(): Promise<void>;
37
+ get contentType(): string;
38
+ get format(): string;
35
39
  /**
36
40
  * Dispose of the resources held by the object.
37
41
  */
38
42
  dispose(): void;
43
+ reconnect(): Promise<void>;
39
44
  private _connect;
45
+ connectToForkDoc(forkRoomId: string, sessionId: string): Promise<void>;
46
+ get wsProvider(): YWebsocketProvider | null;
47
+ private _disconnect;
40
48
  private _onUserChanged;
41
49
  private _onConnectionClosed;
42
50
  private _onSync;
package/lib/yprovider.js CHANGED
@@ -72,6 +72,12 @@ export class WebSocketProvider {
72
72
  get ready() {
73
73
  return this._ready.promise;
74
74
  }
75
+ get contentType() {
76
+ return this._contentType;
77
+ }
78
+ get format() {
79
+ return this._format;
80
+ }
75
81
  /**
76
82
  * Dispose of the resources held by the object.
77
83
  */
@@ -84,8 +90,13 @@ export class WebSocketProvider {
84
90
  (_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.off('connection-close', this._onConnectionClosed);
85
91
  (_b = this._yWebsocketProvider) === null || _b === void 0 ? void 0 : _b.off('sync', this._onSync);
86
92
  (_c = this._yWebsocketProvider) === null || _c === void 0 ? void 0 : _c.destroy();
93
+ this._disconnect();
87
94
  Signal.clearData(this);
88
95
  }
96
+ async reconnect() {
97
+ this._disconnect();
98
+ this._connect();
99
+ }
89
100
  async _connect() {
90
101
  const session = await requestDocSession(this._format, this._contentType, this._path);
91
102
  this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, `${session.format}:${session.type}:${session.fileId}`, this._sharedModel.ydoc, {
@@ -96,6 +107,24 @@ export class WebSocketProvider {
96
107
  this._yWebsocketProvider.on('sync', this._onSync);
97
108
  this._yWebsocketProvider.on('connection-close', this._onConnectionClosed);
98
109
  }
110
+ async connectToForkDoc(forkRoomId, sessionId) {
111
+ this._disconnect();
112
+ this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, forkRoomId, this._sharedModel.ydoc, {
113
+ disableBc: true,
114
+ params: { sessionId },
115
+ awareness: this._awareness
116
+ });
117
+ }
118
+ get wsProvider() {
119
+ return this._yWebsocketProvider;
120
+ }
121
+ _disconnect() {
122
+ var _a, _b, _c;
123
+ (_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.off('connection-close', this._onConnectionClosed);
124
+ (_b = this._yWebsocketProvider) === null || _b === void 0 ? void 0 : _b.off('sync', this._onSync);
125
+ (_c = this._yWebsocketProvider) === null || _c === void 0 ? void 0 : _c.destroy();
126
+ this._yWebsocketProvider = null;
127
+ }
99
128
  _onUserChanged(user) {
100
129
  this._awareness.setLocalStateField('user', user.identity);
101
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyter/docprovider",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0-beta.4",
4
4
  "description": "JupyterLab - Document Provider",
5
5
  "homepage": "https://github.com/jupyterlab/jupyter-collaboration",
6
6
  "bugs": {
@@ -1,8 +1,6 @@
1
1
  /* -----------------------------------------------------------------------------
2
2
  | Copyright (c) Jupyter Development Team.
3
3
  | Distributed under the terms of the Modified BSD License.
4
- |----------------------------------------------------------------------------*/
5
- export var MessageType;
6
- (function (MessageType) {
7
- MessageType[MessageType["CHAT"] = 125] = "CHAT";
8
- })(MessageType || (MessageType = {}));
4
+ |---------------------------------------------------------------------------- */
5
+
6
+ @import url('./slider.css');
@@ -0,0 +1,6 @@
1
+ /* -----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |---------------------------------------------------------------------------- */
5
+
6
+ @import url('./base.css');
package/style/index.js ADDED
@@ -0,0 +1,6 @@
1
+ /*-----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |----------------------------------------------------------------------------*/
5
+
6
+ import './base.css';
@@ -0,0 +1,35 @@
1
+ /* -----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ | Distributed under the terms of the Modified BSD License.
4
+ |---------------------------------------------------------------------------- */
5
+
6
+ .jp-timelineSliderWrapper .jp-sliderContainer{
7
+ display: flex;
8
+ align-items: center;
9
+ }
10
+
11
+ .jp-Slider {
12
+ height: 4.5px
13
+ }
14
+
15
+ #jp-slider-status-bar {
16
+ display: flex;
17
+ }
18
+
19
+ .jp-timestampDisplay {
20
+ display: flex;
21
+ flex-direction: row;
22
+ align-items: center;
23
+ gap: 6px;
24
+ }
25
+
26
+ .jp-restoreBtnContainer {
27
+ width: 192px;
28
+ }
29
+
30
+ .jp-ToolbarButtonComponent.jp-restoreBtn {
31
+ cursor: pointer;
32
+ color: var(--jp-layout-color2);
33
+ width: 100%;
34
+ background: var(--jp-accept-color-normal)
35
+ }
package/lib/utils.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export declare enum MessageType {
2
- CHAT = 125
3
- }