@jupyter/collaboration-extension 1.0.0-alpha.0
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/README.md +3 -0
- package/lib/collaboration.d.ts +22 -0
- package/lib/collaboration.js +131 -0
- package/lib/filebrowser.d.ts +6 -0
- package/lib/filebrowser.js +79 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +19 -0
- package/package.json +105 -0
- package/schema/user-menu-bar.json +15 -0
- package/style/base.css +4 -0
- package/style/index.css +6 -0
- package/style/index.js +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* @module collaboration-extension
|
|
4
|
+
*/
|
|
5
|
+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
6
|
+
import { IAwareness, IUserMenu } from '@jupyter/collaboration';
|
|
7
|
+
/**
|
|
8
|
+
* Jupyter plugin providing the IUserMenu.
|
|
9
|
+
*/
|
|
10
|
+
export declare const userMenuPlugin: JupyterFrontEndPlugin<IUserMenu>;
|
|
11
|
+
/**
|
|
12
|
+
* Jupyter plugin adding the IUserMenu to the menu bar if collaborative flag enabled.
|
|
13
|
+
*/
|
|
14
|
+
export declare const menuBarPlugin: JupyterFrontEndPlugin<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Jupyter plugin creating a global awareness for RTC.
|
|
17
|
+
*/
|
|
18
|
+
export declare const rtcGlobalAwarenessPlugin: JupyterFrontEndPlugin<IAwareness>;
|
|
19
|
+
/**
|
|
20
|
+
* Jupyter plugin adding the RTC information to the application left panel if collaborative flag enabled.
|
|
21
|
+
*/
|
|
22
|
+
export declare const rtcPanelPlugin: JupyterFrontEndPlugin<void>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/**
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
* @module collaboration-extension
|
|
6
|
+
*/
|
|
7
|
+
import { PageConfig } from '@jupyterlab/coreutils';
|
|
8
|
+
import { DOMUtils } from '@jupyterlab/apputils';
|
|
9
|
+
import { AwarenessMock, CollaboratorsPanel, IGlobalAwareness, IUserMenu, RendererUserMenu, UserInfoPanel, UserMenu } from '@jupyter/collaboration';
|
|
10
|
+
import { SidePanel, usersIcon } from '@jupyterlab/ui-components';
|
|
11
|
+
import { MenuBar } from '@lumino/widgets';
|
|
12
|
+
import { URLExt } from '@jupyterlab/coreutils';
|
|
13
|
+
import { ServerConnection } from '@jupyterlab/services';
|
|
14
|
+
import { IStateDB } from '@jupyterlab/statedb';
|
|
15
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
16
|
+
import * as Y from 'yjs';
|
|
17
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
18
|
+
import { WebsocketProvider } from 'y-websocket';
|
|
19
|
+
/**
|
|
20
|
+
* Jupyter plugin providing the IUserMenu.
|
|
21
|
+
*/
|
|
22
|
+
export const userMenuPlugin = {
|
|
23
|
+
id: '@jupyter/collaboration-extension:userMenu',
|
|
24
|
+
autoStart: true,
|
|
25
|
+
requires: [],
|
|
26
|
+
provides: IUserMenu,
|
|
27
|
+
activate: (app) => {
|
|
28
|
+
const { commands } = app;
|
|
29
|
+
const { user } = app.serviceManager;
|
|
30
|
+
return new UserMenu({ commands, user });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Jupyter plugin adding the IUserMenu to the menu bar if collaborative flag enabled.
|
|
35
|
+
*/
|
|
36
|
+
export const menuBarPlugin = {
|
|
37
|
+
id: '@jupyter/collaboration-extension:userMenuBar',
|
|
38
|
+
autoStart: true,
|
|
39
|
+
requires: [IUserMenu],
|
|
40
|
+
activate: async (app, menu) => {
|
|
41
|
+
const { shell } = app;
|
|
42
|
+
const { user } = app.serviceManager;
|
|
43
|
+
if (PageConfig.getOption('collaborative') !== 'true') {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const menuBar = new MenuBar({
|
|
47
|
+
forceItemsPosition: {
|
|
48
|
+
forceX: false,
|
|
49
|
+
forceY: false
|
|
50
|
+
},
|
|
51
|
+
renderer: new RendererUserMenu(user)
|
|
52
|
+
});
|
|
53
|
+
menuBar.id = 'jp-UserMenu';
|
|
54
|
+
user.userChanged.connect(() => menuBar.update());
|
|
55
|
+
menuBar.addMenu(menu);
|
|
56
|
+
shell.add(menuBar, 'top', { rank: 1000 });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Jupyter plugin creating a global awareness for RTC.
|
|
61
|
+
*/
|
|
62
|
+
export const rtcGlobalAwarenessPlugin = {
|
|
63
|
+
id: '@jupyter/collaboration-extension:rtcGlobalAwareness',
|
|
64
|
+
autoStart: true,
|
|
65
|
+
requires: [IStateDB],
|
|
66
|
+
provides: IGlobalAwareness,
|
|
67
|
+
activate: (app, state) => {
|
|
68
|
+
const { user } = app.serviceManager;
|
|
69
|
+
const ydoc = new Y.Doc();
|
|
70
|
+
if (PageConfig.getOption('collaborative') !== 'true') {
|
|
71
|
+
return new AwarenessMock(ydoc);
|
|
72
|
+
}
|
|
73
|
+
const awareness = new Awareness(ydoc);
|
|
74
|
+
const server = ServerConnection.makeSettings();
|
|
75
|
+
const url = URLExt.join(server.wsUrl, 'api/yjs');
|
|
76
|
+
new WebsocketProvider(url, 'JupyterLab:globalAwareness', ydoc, {
|
|
77
|
+
awareness: awareness
|
|
78
|
+
});
|
|
79
|
+
const userChanged = () => {
|
|
80
|
+
awareness.setLocalStateField('user', user.identity);
|
|
81
|
+
};
|
|
82
|
+
if (user.isReady) {
|
|
83
|
+
userChanged();
|
|
84
|
+
}
|
|
85
|
+
user.ready.then(userChanged).catch(e => console.error(e));
|
|
86
|
+
user.userChanged.connect(userChanged);
|
|
87
|
+
state.changed.connect(async () => {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
const data = await state.toJSON();
|
|
90
|
+
const current = ((_b = (_a = data['layout-restorer:data']) === null || _a === void 0 ? void 0 : _a.main) === null || _b === void 0 ? void 0 : _b.current) || '';
|
|
91
|
+
if (current.startsWith('editor') || current.startsWith('notebook')) {
|
|
92
|
+
awareness.setLocalStateField('current', current);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
awareness.setLocalStateField('current', null);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return awareness;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Jupyter plugin adding the RTC information to the application left panel if collaborative flag enabled.
|
|
103
|
+
*/
|
|
104
|
+
export const rtcPanelPlugin = {
|
|
105
|
+
id: '@jupyter/collaboration-extension:rtcPanel',
|
|
106
|
+
autoStart: true,
|
|
107
|
+
requires: [IGlobalAwareness, ITranslator],
|
|
108
|
+
activate: (app, awareness, translator) => {
|
|
109
|
+
if (PageConfig.getOption('collaborative') !== 'true') {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const { user } = app.serviceManager;
|
|
113
|
+
const trans = translator.load('jupyterlab');
|
|
114
|
+
const userPanel = new SidePanel();
|
|
115
|
+
userPanel.id = DOMUtils.createDomID();
|
|
116
|
+
userPanel.title.icon = usersIcon;
|
|
117
|
+
userPanel.title.caption = trans.__('Collaboration');
|
|
118
|
+
userPanel.addClass('jp-RTCPanel');
|
|
119
|
+
app.shell.add(userPanel, 'left', { rank: 300 });
|
|
120
|
+
const currentUserPanel = new UserInfoPanel(user);
|
|
121
|
+
currentUserPanel.title.label = trans.__('User info');
|
|
122
|
+
currentUserPanel.title.caption = trans.__('User information');
|
|
123
|
+
userPanel.addWidget(currentUserPanel);
|
|
124
|
+
const fileopener = (path) => {
|
|
125
|
+
void app.commands.execute('docmanager:open', { path });
|
|
126
|
+
};
|
|
127
|
+
const collaboratorsPanel = new CollaboratorsPanel(user, awareness, fileopener);
|
|
128
|
+
collaboratorsPanel.title.label = trans.__('Online Collaborators');
|
|
129
|
+
userPanel.addWidget(collaboratorsPanel);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
|
+
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
|
|
3
|
+
/**
|
|
4
|
+
* The default file browser factory provider.
|
|
5
|
+
*/
|
|
6
|
+
export declare const defaultFileBrowser: JupyterFrontEndPlugin<IDefaultFileBrowser>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ILabShell, IRouter, JupyterFrontEnd } from '@jupyterlab/application';
|
|
2
|
+
import { IDefaultFileBrowser, IFileBrowserFactory } from '@jupyterlab/filebrowser';
|
|
3
|
+
import { YDrive } from '@jupyter/docprovider';
|
|
4
|
+
/**
|
|
5
|
+
* The command IDs used by the file browser plugin.
|
|
6
|
+
*/
|
|
7
|
+
var CommandIDs;
|
|
8
|
+
(function (CommandIDs) {
|
|
9
|
+
CommandIDs.openPath = 'filebrowser:open-path';
|
|
10
|
+
})(CommandIDs || (CommandIDs = {}));
|
|
11
|
+
/**
|
|
12
|
+
* The default file browser factory provider.
|
|
13
|
+
*/
|
|
14
|
+
export const defaultFileBrowser = {
|
|
15
|
+
id: '@jupyter/collaboration-extension:defaultFileBrowser',
|
|
16
|
+
provides: IDefaultFileBrowser,
|
|
17
|
+
requires: [IFileBrowserFactory],
|
|
18
|
+
optional: [IRouter, JupyterFrontEnd.ITreeResolver, ILabShell],
|
|
19
|
+
activate: async (app, fileBrowserFactory, router, tree, labShell) => {
|
|
20
|
+
console.debug('@jupyter/collaboration-extension:defaultFileBrowser: activated');
|
|
21
|
+
const { commands } = app;
|
|
22
|
+
const drive = new YDrive(app.serviceManager.user);
|
|
23
|
+
app.serviceManager.contents.addDrive(drive);
|
|
24
|
+
// Manually restore and load the default file browser.
|
|
25
|
+
const defaultBrowser = fileBrowserFactory.createFileBrowser('filebrowser', {
|
|
26
|
+
auto: false,
|
|
27
|
+
restore: false,
|
|
28
|
+
driveName: 'YDrive'
|
|
29
|
+
});
|
|
30
|
+
void Private.restoreBrowser(defaultBrowser, commands, router, tree, labShell);
|
|
31
|
+
return defaultBrowser;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var Private;
|
|
35
|
+
(function (Private) {
|
|
36
|
+
/**
|
|
37
|
+
* Restores file browser state and overrides state if tree resolver resolves.
|
|
38
|
+
*/
|
|
39
|
+
async function restoreBrowser(browser, commands, router, tree, labShell) {
|
|
40
|
+
const restoring = 'jp-mod-restoring';
|
|
41
|
+
browser.addClass(restoring);
|
|
42
|
+
if (!router) {
|
|
43
|
+
await browser.model.restore(browser.id);
|
|
44
|
+
await browser.model.refresh();
|
|
45
|
+
browser.removeClass(restoring);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const listener = async () => {
|
|
49
|
+
router.routed.disconnect(listener);
|
|
50
|
+
const paths = await (tree === null || tree === void 0 ? void 0 : tree.paths);
|
|
51
|
+
if ((paths === null || paths === void 0 ? void 0 : paths.file) || (paths === null || paths === void 0 ? void 0 : paths.browser)) {
|
|
52
|
+
// Restore the model without populating it.
|
|
53
|
+
await browser.model.restore(browser.id, false);
|
|
54
|
+
if (paths.file) {
|
|
55
|
+
await commands.execute(CommandIDs.openPath, {
|
|
56
|
+
path: paths.file,
|
|
57
|
+
dontShowBrowser: true
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (paths.browser) {
|
|
61
|
+
await commands.execute(CommandIDs.openPath, {
|
|
62
|
+
path: paths.browser,
|
|
63
|
+
dontShowBrowser: true
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await browser.model.restore(browser.id);
|
|
69
|
+
await browser.model.refresh();
|
|
70
|
+
}
|
|
71
|
+
browser.removeClass(restoring);
|
|
72
|
+
if (labShell === null || labShell === void 0 ? void 0 : labShell.isEmpty('main')) {
|
|
73
|
+
void commands.execute('launcher:create');
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
router.routed.connect(listener);
|
|
77
|
+
}
|
|
78
|
+
Private.restoreBrowser = restoreBrowser;
|
|
79
|
+
})(Private || (Private = {}));
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* @module collaboration-extension
|
|
4
|
+
*/
|
|
5
|
+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
6
|
+
/**
|
|
7
|
+
* Export the plugins as default.
|
|
8
|
+
*/
|
|
9
|
+
declare const plugins: JupyterFrontEndPlugin<any>[];
|
|
10
|
+
export default plugins;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/**
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
* @module collaboration-extension
|
|
6
|
+
*/
|
|
7
|
+
import { defaultFileBrowser } from './filebrowser';
|
|
8
|
+
import { userMenuPlugin, menuBarPlugin, rtcGlobalAwarenessPlugin, rtcPanelPlugin } from './collaboration';
|
|
9
|
+
/**
|
|
10
|
+
* Export the plugins as default.
|
|
11
|
+
*/
|
|
12
|
+
const plugins = [
|
|
13
|
+
defaultFileBrowser,
|
|
14
|
+
userMenuPlugin,
|
|
15
|
+
menuBarPlugin,
|
|
16
|
+
rtcGlobalAwarenessPlugin,
|
|
17
|
+
rtcPanelPlugin
|
|
18
|
+
];
|
|
19
|
+
export default plugins;
|
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jupyter/collaboration-extension",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "JupyterLab - Real-Time Collaboration Extension",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jupyter",
|
|
7
|
+
"jupyterlab",
|
|
8
|
+
"jupyterlab-extension"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/jupyterlab/jupyter_collaboration",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/jupyterlab/jupyter_collaboration/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/jupyterlab/jupyter_collaboration.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "BSD-3-Clause",
|
|
19
|
+
"author": "Project Jupyter",
|
|
20
|
+
"sideEffects": [
|
|
21
|
+
"style/*.css",
|
|
22
|
+
"style/index.js"
|
|
23
|
+
],
|
|
24
|
+
"main": "lib/index.js",
|
|
25
|
+
"types": "lib/index.d.ts",
|
|
26
|
+
"style": "style/index.css",
|
|
27
|
+
"styleModule": "style/index.js",
|
|
28
|
+
"directories": {
|
|
29
|
+
"lib": "lib/"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"lib/*.d.ts",
|
|
33
|
+
"lib/*.js.map",
|
|
34
|
+
"lib/*.js",
|
|
35
|
+
"schema/*.json",
|
|
36
|
+
"style/*.css",
|
|
37
|
+
"style/index.js"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "jlpm run build:lib && jlpm run build:labextension:dev",
|
|
41
|
+
"build:lib": "tsc",
|
|
42
|
+
"build:prod": "jlpm run clean && jlpm run build:lib && jlpm run build:labextension",
|
|
43
|
+
"build:labextension": "jupyter labextension build .",
|
|
44
|
+
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
45
|
+
"clean": "jlpm run clean:lib",
|
|
46
|
+
"clean:lib": "rimraf lib tsconfig.tsbuildinfo node_modules",
|
|
47
|
+
"clean:labextension": "rimraf ../../jupyter_collaboration/labextension",
|
|
48
|
+
"clean:all": "jlpm run clean:lib && jlpm run clean:labextension",
|
|
49
|
+
"install:extension": "jlpm run build",
|
|
50
|
+
"watch": "run-p watch:src watch:labextension",
|
|
51
|
+
"watch:src": "tsc -w",
|
|
52
|
+
"watch:labextension": "jupyter labextension watch ."
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@jupyter/collaboration": "^1.0.0-alpha.0",
|
|
56
|
+
"@jupyter/docprovider": "^1.0.0-alpha.0",
|
|
57
|
+
"@jupyterlab/application": "^4.0.0-alpha.18",
|
|
58
|
+
"@jupyterlab/apputils": "^4.0.0-alpha.18",
|
|
59
|
+
"@jupyterlab/coreutils": "^6.0.0-alpha.18",
|
|
60
|
+
"@jupyterlab/filebrowser": "^4.0.0-alpha.18",
|
|
61
|
+
"@jupyterlab/services": "^7.0.0-alpha.18",
|
|
62
|
+
"@jupyterlab/statedb": "^4.0.0-alpha.18",
|
|
63
|
+
"@jupyterlab/translation": "^4.0.0-alpha.18",
|
|
64
|
+
"@jupyterlab/ui-components": "^4.0.0-alpha.33",
|
|
65
|
+
"@lumino/commands": "^2.0.0-alpha.6",
|
|
66
|
+
"@lumino/widgets": "^2.0.0-alpha.6",
|
|
67
|
+
"y-protocols": "^1.0.5",
|
|
68
|
+
"y-websocket": "^1.3.15",
|
|
69
|
+
"yjs": "^13.5.40"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@jupyterlab/builder": "^4.0.0-alpha.18",
|
|
73
|
+
"@types/react": "^18.0.27",
|
|
74
|
+
"npm-run-all": "^4.1.5",
|
|
75
|
+
"rimraf": "~3.0.0",
|
|
76
|
+
"typescript": "~4.7.3"
|
|
77
|
+
},
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"access": "public"
|
|
80
|
+
},
|
|
81
|
+
"typedoc": {
|
|
82
|
+
"entryPoint": "./src/index.ts",
|
|
83
|
+
"readmeFile": "./README.md",
|
|
84
|
+
"displayName": "@jupyter/collaboration-extension",
|
|
85
|
+
"tsconfig": "./tsconfig.json"
|
|
86
|
+
},
|
|
87
|
+
"jupyterlab": {
|
|
88
|
+
"extension": true,
|
|
89
|
+
"schemaDir": "./schema",
|
|
90
|
+
"outputDir": "../../jupyter_collaboration/labextension",
|
|
91
|
+
"disabledExtensions": [
|
|
92
|
+
"@jupyterlab/filebrowser-extension:defaultFileBrowser"
|
|
93
|
+
],
|
|
94
|
+
"sharedPackages": {
|
|
95
|
+
"@jupyter/collaboration": {
|
|
96
|
+
"bundled": true,
|
|
97
|
+
"singleton": true
|
|
98
|
+
},
|
|
99
|
+
"@jupyter/docprovider": {
|
|
100
|
+
"bundled": true,
|
|
101
|
+
"singleton": true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "User Menu Bar",
|
|
3
|
+
"description": "User Menu Bar settings.",
|
|
4
|
+
"jupyter.lab.toolbars": {
|
|
5
|
+
"TopBar": [
|
|
6
|
+
{
|
|
7
|
+
"name": "user-menu",
|
|
8
|
+
"rank": 100
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"properties": {},
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"type": "object"
|
|
15
|
+
}
|
package/style/base.css
ADDED
package/style/index.css
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 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';
|