@jupytergis/jupytergis-lab 0.2.1 → 0.4.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/lib/index.js CHANGED
@@ -6,7 +6,7 @@ import { IMainMenu } from '@jupyterlab/mainmenu';
6
6
  import { IStateDB } from '@jupyterlab/statedb';
7
7
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
8
8
  import { Menu } from '@lumino/widgets';
9
- import { notebookRenderePlugin } from './notebookrenderer';
9
+ import { notebookRendererPlugin } from './notebookrenderer';
10
10
  const NAME_SPACE = 'jupytergis';
11
11
  const plugin = {
12
12
  id: 'jupytergis:lab:main-menu',
@@ -29,6 +29,7 @@ const plugin = {
29
29
  const stateDbManager = GlobalStateDbManager.getInstance();
30
30
  stateDbManager.initialize(state);
31
31
  addCommands(app, tracker, translator, formSchemaRegistry, layerBrowserRegistry, state, completionProviderManager);
32
+ app.shell.addClass('data-jgis-keybinding');
32
33
  // SOURCES context menu
33
34
  const newSourceSubMenu = new Menu({ commands: app.commands });
34
35
  newSourceSubMenu.title.label = translator
@@ -71,21 +72,11 @@ const plugin = {
71
72
  rank: 1,
72
73
  command: CommandIDs.removeSource
73
74
  });
74
- app.commands.addKeyBinding({
75
- command: CommandIDs.removeSource,
76
- keys: ['Delete'],
77
- selector: '.jp-gis-source.jp-gis-sourceUnused'
78
- });
79
75
  app.contextMenu.addItem({
80
76
  selector: '.jp-gis-source',
81
77
  rank: 1,
82
78
  command: CommandIDs.renameSource
83
79
  });
84
- app.commands.addKeyBinding({
85
- command: CommandIDs.renameSource,
86
- keys: ['F2'],
87
- selector: '.jp-gis-sourceItem'
88
- });
89
80
  // LAYERS and LAYER GROUPS context menu
90
81
  app.contextMenu.addItem({
91
82
  command: CommandIDs.symbology,
@@ -103,20 +94,15 @@ const plugin = {
103
94
  selector: '.jp-gis-layerItem',
104
95
  rank: 2
105
96
  });
106
- app.commands.addKeyBinding({
107
- command: CommandIDs.removeLayer,
108
- keys: ['Delete'],
109
- selector: '.jp-gis-layerItem'
110
- });
111
97
  app.contextMenu.addItem({
112
98
  command: CommandIDs.renameLayer,
113
99
  selector: '.jp-gis-layerItem',
114
100
  rank: 2
115
101
  });
116
- app.commands.addKeyBinding({
117
- command: CommandIDs.renameLayer,
118
- keys: ['F2'],
119
- selector: '.jp-gis-layerItem'
102
+ app.contextMenu.addItem({
103
+ command: CommandIDs.zoomToLayer,
104
+ selector: '.jp-gis-layerItem',
105
+ rank: 2
120
106
  });
121
107
  const moveLayerSubmenu = new Menu({ commands: app.commands });
122
108
  moveLayerSubmenu.title.label = translator
@@ -135,21 +121,11 @@ const plugin = {
135
121
  selector: '.jp-gis-layerGroupHeader',
136
122
  rank: 2
137
123
  });
138
- app.commands.addKeyBinding({
139
- command: CommandIDs.removeGroup,
140
- keys: ['Delete'],
141
- selector: '.jp-gis-layerGroupHeader'
142
- });
143
124
  app.contextMenu.addItem({
144
125
  command: CommandIDs.renameGroup,
145
126
  selector: '.jp-gis-layerGroupHeader',
146
127
  rank: 2
147
128
  });
148
- app.commands.addKeyBinding({
149
- command: CommandIDs.renameGroup,
150
- keys: ['F2'],
151
- selector: '.jp-gis-layerGroupHeader'
152
- });
153
129
  // Separator
154
130
  app.contextMenu.addItem({
155
131
  type: 'separator',
@@ -181,25 +157,13 @@ const plugin = {
181
157
  command: CommandIDs.newImageLayer,
182
158
  args: { from: 'contextMenu' }
183
159
  });
160
+ newLayerSubMenu.addItem({
161
+ command: CommandIDs.newHeatmapLayer,
162
+ args: { from: 'contextMenu' }
163
+ });
184
164
  if (mainMenu) {
185
165
  populateMenus(mainMenu, isEnabled);
186
166
  }
187
- // Keybindings for the console
188
- app.commands.addKeyBinding({
189
- command: CommandIDs.executeConsole,
190
- keys: ['Shift Enter'],
191
- selector: ".jpgis-console .jp-CodeConsole[data-jp-interaction-mode='notebook'] .jp-CodeConsole-promptCell"
192
- });
193
- app.commands.addKeyBinding({
194
- command: CommandIDs.invokeCompleter,
195
- keys: ['Tab'],
196
- selector: '.jpgis-console .jp-CodeConsole-promptCell .jp-mod-completer-enabled'
197
- });
198
- app.commands.addKeyBinding({
199
- command: CommandIDs.selectCompleter,
200
- keys: ['Enter'],
201
- selector: '.jpgis-console .jp-ConsolePanel .jp-mod-completer-active'
202
- });
203
167
  }
204
168
  };
205
169
  const controlPanel = {
@@ -217,7 +181,8 @@ const controlPanel = {
217
181
  const leftControlPanel = new LeftPanelWidget({
218
182
  model: controlModel,
219
183
  tracker,
220
- state
184
+ state,
185
+ commands: app.commands
221
186
  });
222
187
  leftControlPanel.id = 'jupytergis::leftControlPanel';
223
188
  leftControlPanel.title.caption = 'JupyterGIS Control Panel';
@@ -258,10 +223,10 @@ function populateMenus(mainMenu, isEnabled) {
258
223
  */
259
224
  function buildGroupsMenu(contextMenu, tracker) {
260
225
  var _a, _b, _c, _d;
261
- if (!((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.model)) {
226
+ if (!((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model)) {
262
227
  return;
263
228
  }
264
- const model = (_b = tracker.currentWidget) === null || _b === void 0 ? void 0 : _b.context.model;
229
+ const model = (_b = tracker.currentWidget) === null || _b === void 0 ? void 0 : _b.model;
265
230
  const submenu = (_d = (_c = contextMenu.menu.items.find(item => {
266
231
  var _a;
267
232
  return item.type === 'submenu' &&
@@ -307,4 +272,4 @@ function buildGroupsMenu(contextMenu, tracker) {
307
272
  command: CommandIDs.moveLayerToNewGroup
308
273
  });
309
274
  }
310
- export default [plugin, controlPanel, notebookRenderePlugin];
275
+ export default [plugin, controlPanel, notebookRendererPlugin];
@@ -1,5 +1,7 @@
1
- import { JupyterGISModel } from '@jupytergis/schema';
1
+ import { JupyterGISOutputWidget, JupyterGISTracker } from '@jupytergis/base';
2
+ import { IJGISExternalCommandRegistry, JupyterGISModel } from '@jupytergis/schema';
2
3
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
4
+ import { CommandRegistry } from '@lumino/commands';
3
5
  import { Panel } from '@lumino/widgets';
4
6
  import { JupyterYModel } from 'yjs-widgets';
5
7
  export interface ICommMetadata {
@@ -14,10 +16,20 @@ export declare class YJupyterGISModel extends JupyterYModel {
14
16
  jupyterGISModel: JupyterGISModel;
15
17
  }
16
18
  export declare class YJupyterGISLuminoWidget extends Panel {
17
- constructor(options: {
18
- model: JupyterGISModel;
19
- });
20
- onResize: () => void;
19
+ constructor(options: IOptions);
20
+ get jgisWidget(): JupyterGISOutputWidget;
21
+ /**
22
+ * Build the widget and add it to the panel.
23
+ * @param options
24
+ */
25
+ private _buildWidget;
21
26
  private _jgisWidget;
22
27
  }
23
- export declare const notebookRenderePlugin: JupyterFrontEndPlugin<void>;
28
+ interface IOptions {
29
+ commands: CommandRegistry;
30
+ model: JupyterGISModel;
31
+ externalCommands?: IJGISExternalCommandRegistry;
32
+ tracker?: JupyterGISTracker;
33
+ }
34
+ export declare const notebookRendererPlugin: JupyterFrontEndPlugin<void>;
35
+ export {};
@@ -1,44 +1,114 @@
1
1
  import { ICollaborativeDrive } from '@jupyter/collaborative-drive';
2
- import { JupyterGISPanel } from '@jupytergis/base';
3
- import { JupyterGISModel } from '@jupytergis/schema';
2
+ import { JupyterGISOutputWidget, JupyterGISPanel, ToolbarWidget } from '@jupytergis/base';
3
+ import { IJGISExternalCommandRegistryToken, IJupyterGISDocTracker, JupyterGISModel } from '@jupytergis/schema';
4
+ import { showErrorMessage } from '@jupyterlab/apputils';
5
+ import { ConsolePanel } from '@jupyterlab/console';
6
+ import { PathExt } from '@jupyterlab/coreutils';
7
+ import { NotebookPanel } from '@jupyterlab/notebook';
4
8
  import { MessageLoop } from '@lumino/messaging';
5
9
  import { Panel, Widget } from '@lumino/widgets';
6
- import { IJupyterYWidgetManager, JupyterYModel } from 'yjs-widgets';
10
+ import { IJupyterYWidgetManager, JupyterYDoc, JupyterYModel } from 'yjs-widgets';
7
11
  export const CLASS_NAME = 'jupytergis-notebook-widget';
8
12
  export class YJupyterGISModel extends JupyterYModel {
9
13
  }
10
14
  export class YJupyterGISLuminoWidget extends Panel {
11
15
  constructor(options) {
12
16
  super();
13
- this.onResize = () => {
14
- if (this._jgisWidget) {
15
- MessageLoop.sendMessage(this._jgisWidget, Widget.ResizeMessage.UnknownSize);
17
+ /**
18
+ * Build the widget and add it to the panel.
19
+ * @param options
20
+ */
21
+ this._buildWidget = (options) => {
22
+ const { commands, model, externalCommands, tracker } = options;
23
+ const content = new JupyterGISPanel({ model, commandRegistry: commands });
24
+ let toolbar = undefined;
25
+ if (model.filePath) {
26
+ toolbar = new ToolbarWidget({
27
+ commands,
28
+ model,
29
+ externalCommands: (externalCommands === null || externalCommands === void 0 ? void 0 : externalCommands.getCommands()) || []
30
+ });
16
31
  }
32
+ this._jgisWidget = new JupyterGISOutputWidget({
33
+ model,
34
+ content,
35
+ toolbar
36
+ });
37
+ this.addWidget(this._jgisWidget);
38
+ tracker === null || tracker === void 0 ? void 0 : tracker.add(this._jgisWidget);
17
39
  };
40
+ const { model } = options;
18
41
  this.addClass(CLASS_NAME);
19
- this._jgisWidget = new JupyterGISPanel(options);
20
- this.addWidget(this._jgisWidget);
42
+ this._buildWidget(options);
43
+ // If the filepath was not set when building the widget, the toolbar is not built.
44
+ // The widget has to be built again to include the toolbar.
45
+ const onchange = (_, args) => {
46
+ if (args.stateChange) {
47
+ args.stateChange.forEach((change) => {
48
+ var _a;
49
+ if (change.name === 'path') {
50
+ (_a = this.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(this._jgisWidget);
51
+ this._jgisWidget.dispose();
52
+ this._buildWidget(options);
53
+ }
54
+ });
55
+ }
56
+ };
57
+ model.sharedModel.changed.connect(onchange);
58
+ }
59
+ get jgisWidget() {
60
+ return this._jgisWidget;
21
61
  }
22
62
  }
23
- export const notebookRenderePlugin = {
63
+ export const notebookRendererPlugin = {
24
64
  id: 'jupytergis:yjswidget-plugin',
25
65
  autoStart: true,
26
- optional: [IJupyterYWidgetManager, ICollaborativeDrive],
27
- activate: (app, yWidgetManager, drive) => {
66
+ optional: [
67
+ IJGISExternalCommandRegistryToken,
68
+ IJupyterGISDocTracker,
69
+ IJupyterYWidgetManager,
70
+ ICollaborativeDrive
71
+ ],
72
+ activate: (app, externalCommandRegistry, jgisTracker, yWidgetManager, drive) => {
28
73
  if (!yWidgetManager) {
29
74
  console.error('Missing IJupyterYWidgetManager token!');
30
75
  return;
31
76
  }
32
- if (!drive) {
33
- console.error('Cannot setup JupyterGIS Python API without a collaborative drive');
34
- return;
35
- }
36
77
  class YJupyterGISModelFactory extends YJupyterGISModel {
37
- ydocFactory(commMetadata) {
78
+ async initialize(commMetadata) {
38
79
  const { path, format, contentType } = commMetadata;
39
80
  const fileFormat = format;
81
+ if (!drive) {
82
+ showErrorMessage('Error using the JupyterGIS Python API', 'You cannot use the JupyterGIS Python API without a collaborative drive. You need to install a package providing collaboration features (e.g. jupyter-collaboration).');
83
+ throw new Error('Failed to create the YDoc without a collaborative drive');
84
+ }
85
+ // The path of the project is relative to the path of the notebook
86
+ let currentWidgetPath = '';
87
+ const currentWidget = app.shell.currentWidget;
88
+ if (currentWidget instanceof NotebookPanel ||
89
+ currentWidget instanceof ConsolePanel) {
90
+ currentWidgetPath = currentWidget.sessionContext.path;
91
+ }
92
+ let localPath = '';
93
+ if (path) {
94
+ localPath = PathExt.join(PathExt.dirname(currentWidgetPath), path);
95
+ // If the file does not exist yet, create it
96
+ try {
97
+ await app.serviceManager.contents.get(localPath);
98
+ }
99
+ catch (e) {
100
+ await app.serviceManager.contents.save(localPath, {
101
+ content: btoa('{}'),
102
+ format: 'base64'
103
+ });
104
+ }
105
+ }
106
+ else {
107
+ // If the user did not provide a path, do not create
108
+ localPath = PathExt.join(PathExt.dirname(currentWidgetPath), 'unsaved_project');
109
+ }
40
110
  const sharedModel = drive.sharedModelFactory.createNew({
41
- path,
111
+ path: localPath,
42
112
  format: fileFormat,
43
113
  contentType,
44
114
  collaborative: true
@@ -46,7 +116,10 @@ export const notebookRenderePlugin = {
46
116
  this.jupyterGISModel = new JupyterGISModel({
47
117
  sharedModel: sharedModel
48
118
  });
49
- return this.jupyterGISModel.sharedModel.ydoc;
119
+ this.jupyterGISModel.contentsManager = app.serviceManager.contents;
120
+ this.jupyterGISModel.filePath = localPath;
121
+ this.ydoc = this.jupyterGISModel.sharedModel.ydoc;
122
+ this.sharedModel = new JupyterYDoc(commMetadata, this.ydoc);
50
123
  }
51
124
  }
52
125
  class YJupyterGISWidget {
@@ -54,13 +127,20 @@ export const notebookRenderePlugin = {
54
127
  this.yModel = yModel;
55
128
  this.node = node;
56
129
  const widget = new YJupyterGISLuminoWidget({
57
- model: yModel.jupyterGISModel
130
+ commands: app.commands,
131
+ model: yModel.jupyterGISModel,
132
+ externalCommands: externalCommandRegistry,
133
+ tracker: jgisTracker
58
134
  });
59
- // Widget.attach(widget, node);
135
+ this._jgisWidget = widget.jgisWidget;
60
136
  MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
61
137
  node.appendChild(widget.node);
62
138
  MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
63
139
  }
140
+ dispose() {
141
+ // Dispose of the widget.
142
+ this._jgisWidget.dispose();
143
+ }
64
144
  }
65
145
  yWidgetManager.registerWidget('@jupytergis:widget', YJupyterGISModelFactory, YJupyterGISWidget);
66
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupytergis/jupytergis-lab",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "JupyterGIS Lab extension.",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -53,23 +53,23 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@jupyter/collaborative-drive": "^3.0.0",
56
- "@jupytergis/base": "^0.2.1",
57
- "@jupytergis/jupytergis-core": "^0.2.1",
58
- "@jupytergis/schema": "^0.2.1",
56
+ "@jupytergis/base": "^0.4.0",
57
+ "@jupytergis/schema": "^0.4.0",
59
58
  "@jupyterlab/application": "^4.3.0",
60
59
  "@jupyterlab/apputils": "^4.3.0",
60
+ "@jupyterlab/completer": "^4.3.0",
61
+ "@jupyterlab/console": "^4.3.0",
61
62
  "@jupyterlab/coreutils": "^6.3.0",
62
- "@jupyterlab/docregistry": "^4.3.0",
63
- "@jupyterlab/filebrowser": "^4.3.0",
64
- "@jupyterlab/launcher": "^4.3.0",
65
63
  "@jupyterlab/mainmenu": "^4.3.0",
64
+ "@jupyterlab/notebook": "^4.3.0",
66
65
  "@jupyterlab/services": "^7.3.0",
66
+ "@jupyterlab/statedb": "^4.3.0",
67
67
  "@jupyterlab/translation": "^4.3.0",
68
- "@jupyterlab/ui-components": "^4.3.0",
68
+ "@lumino/commands": "^2.0.0",
69
69
  "@lumino/messaging": "^2.0.0",
70
70
  "@lumino/widgets": "^2.0.0",
71
71
  "react": "^18.0.1",
72
- "yjs-widgets": "^0.3.5"
72
+ "yjs-widgets": "^0.4"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@jupyterlab/builder": "^4.3.0",
package/style/base.css CHANGED
@@ -212,10 +212,16 @@ div.jGIS-toolbar-widget > div.jp-Toolbar-item:last-child {
212
212
  border: solid 1.5px red;
213
213
  }
214
214
 
215
+ .jGIS-Mainview-Container {
216
+ display: flex;
217
+ flex-direction: column;
218
+ height: 100%;
219
+ }
220
+
215
221
  .jGIS-Mainview {
216
222
  width: 100%;
217
- height: 100%;
218
223
  box-sizing: border-box;
224
+ flex: 1;
219
225
  }
220
226
 
221
227
  .jGIS-Popup-Wrapper {
@@ -523,6 +529,7 @@ div.jGIS-toolbar-widget > div.jp-Toolbar-item:last-child {
523
529
  order: 1;
524
530
  margin-top: 2px;
525
531
  margin-bottom: 2px;
532
+ text-transform: capitalize;
526
533
  }
527
534
 
528
535
  .jGIS-property-panel
@@ -731,3 +738,11 @@ div.jGIS-toolbar-widget > div.jp-Toolbar-item:last-child {
731
738
  .jgis-identify-grid-body:last-of-type strong:last-of-type {
732
739
  padding-bottom: 8px;
733
740
  }
741
+
742
+ /* Style the file path text */
743
+ .file-container {
744
+ display: flex;
745
+ align-items: center;
746
+ margin-bottom: 16px;
747
+ gap: 10px;
748
+ }