@fails-components/jupyter-applet-view 0.0.1-alpha.10

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,31 @@
1
+ {
2
+ "jupyter.lab.shortcuts": [],
3
+ "title": "Applet view for Fails",
4
+ "description": "@fails-components/jupyter-applet-view settings.",
5
+ "type": "object",
6
+ "properties": {},
7
+ "additionalProperties": false,
8
+ "jupyter.lab.menus": {
9
+ "context": [
10
+ {
11
+ "type": "separator",
12
+ "selector": ".jp-Notebook .jp-Cell",
13
+ "rank": 70
14
+ },
15
+ {
16
+ "command": "fails-components-jupyter-applet-view:add_to_view",
17
+ "selector": ".jp-Notebook .jp-Cell",
18
+ "rank": 71
19
+ }
20
+ ]
21
+ },
22
+ "jupyter.lab.toolbars": {
23
+ "Cell": [
24
+ {
25
+ "name": "add_to_view",
26
+ "command": "fails-components-jupyter-applet-view:add_to_view",
27
+ "rank": 10
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "jupyter.lab.shortcuts": [],
3
+ "title": "Toolbar for applet view provided for Fails",
4
+ "description": "@fails-components/jupyter-applet-view toolbar.",
5
+ "type": "object",
6
+ "properties": {},
7
+ "additionalProperties": false
8
+ }
@@ -0,0 +1,395 @@
1
+ import { JupyterFrontEnd, ILayoutRestorer } from '@jupyterlab/application';
2
+ import { Cell } from '@jupyterlab/cells';
3
+ import { IDocumentManager } from '@jupyterlab/docmanager';
4
+ import {
5
+ INotebookTracker,
6
+ NotebookWidgetFactory,
7
+ NotebookTracker,
8
+ NotebookPanel
9
+ } from '@jupyterlab/notebook';
10
+ import { RestorablePool } from '@jupyterlab/statedb';
11
+ import { ITranslator } from '@jupyterlab/translation';
12
+ import {
13
+ addIcon,
14
+ moveUpIcon,
15
+ moveDownIcon,
16
+ caretUpIcon,
17
+ caretDownIcon,
18
+ deleteIcon
19
+ } from '@jupyterlab/ui-components';
20
+ import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
21
+ import { SplitViewNotebookWidgetFactory } from './splitviewnotebookpanel';
22
+ import { SplitViewNotebookPanel } from './splitviewnotebookpanel';
23
+ import { IFailsLauncherInfo } from '@fails-components/jupyter-launcher';
24
+ import { IFailsInterceptor } from '@fails-components/jupyter-interceptor';
25
+
26
+ // portions used from Jupyterlab:
27
+ /* -----------------------------------------------------------------------------
28
+ | Copyright (c) Jupyter Development Team.
29
+ | Distributed under the terms of the Modified BSD License.
30
+ |----------------------------------------------------------------------------*/
31
+ // This code contains portions from or is inspired by Jupyter lab's notebook extension, especially the createOutputView part
32
+ // Also a lot is taken from the cell toolbar related parts.
33
+ export function activateAppletView(
34
+ app: JupyterFrontEnd,
35
+ docManager: IDocumentManager,
36
+ notebookTracker: INotebookTracker,
37
+ translator: ITranslator,
38
+ restorer: ILayoutRestorer | null,
39
+ failsLauncherInfo: IFailsLauncherInfo | null,
40
+ failsInterceptor: IFailsInterceptor | null
41
+ ): void {
42
+ if (app.namespace === 'JupyterLite Server') {
43
+ return;
44
+ }
45
+ console.log(
46
+ 'JupyterLab extension @fails-components/jupyter-applet-view is activated!'
47
+ );
48
+ const trans = translator.load('fails_components_jupyter_applet_view');
49
+ const addToViewID = 'fails-components-jupyter-applet-view:add_to_view';
50
+ const moveViewUpID = 'fails-components-jupyter-applet-view:move_view_up';
51
+ const moveViewDownID = 'fails-components-jupyter-applet-view:move_view_down';
52
+ const moveViewAppUpID =
53
+ 'fails-components-jupyter-applet-view:move_view_app_up';
54
+ const moveViewAppDownID =
55
+ 'fails-components-jupyter-applet-view:move_view_app_down';
56
+ const deleteViewID = 'fails-components-jupyter-applet-view:delete_view';
57
+ /*const appletViewOutputs = new WidgetTracker<
58
+ MainAreaWidget<Private.AppletViewOutputArea>
59
+ >({
60
+ namespace: 'cloned-outputs'
61
+ });
62
+
63
+ if (restorer) {
64
+ void restorer.restore(appletViewOutputs, {
65
+ command: commandID,
66
+ args: widget => ({
67
+ path: widget.content.path,
68
+ indices: widget.content.indices
69
+ }),
70
+ name: widget =>
71
+ `${widget.content.path}:${widget.content.indices.join(':')}`,
72
+ when: notebookTracker.restored // After the notebook widgets (but not contents).
73
+ });
74
+ } */
75
+ const { commands, shell /* , serviceManager: services */ } = app;
76
+
77
+ const realFactory: NotebookWidgetFactory | undefined =
78
+ app.docRegistry.getWidgetFactory(
79
+ 'Notebook'
80
+ ) as unknown as NotebookWidgetFactory;
81
+ const factoryName = 'Notebook'; //'SplitViewNotebook';
82
+ if (realFactory !== undefined) {
83
+ const factory = new SplitViewNotebookWidgetFactory({
84
+ name: factoryName,
85
+ label: trans.__('Notebook'),
86
+ fileTypes: ['notebook'],
87
+ modelName: 'notebook',
88
+ defaultFor: ['notebook'],
89
+ preferKernel: realFactory.preferKernel,
90
+ canStartKernel: true,
91
+ rendermime: realFactory.rendermime,
92
+ contentFactory: realFactory.contentFactory,
93
+ editorConfig: realFactory.editorConfig,
94
+ notebookConfig: realFactory.notebookConfig,
95
+ mimeTypeService: realFactory.mimeTypeService,
96
+ toolbarFactory: realFactory['_toolbarFactory'],
97
+ translator,
98
+ failsLauncherInfo:
99
+ failsLauncherInfo !== null ? failsLauncherInfo : undefined,
100
+ failsInterceptor: failsInterceptor !== null ? failsInterceptor : undefined
101
+ });
102
+ let id = 0;
103
+ // we need to clone the registration with the tracker from the plugin:
104
+ factory.widgetCreated.connect((sender, widget) => {
105
+ // If the notebook panel does not have an ID, assign it one.
106
+ widget.id = widget.id || `splitviewnotebook-${++id}`;
107
+ const ft = app.docRegistry.getFileType('notebook');
108
+ // Set up the title icon
109
+ widget.title.icon = ft?.icon;
110
+ widget.title.iconClass = ft?.iconClass ?? '';
111
+ widget.title.iconLabel = ft?.iconLabel ?? '';
112
+
113
+ // Notify the widget tracker if restore data needs to update.
114
+ const tracker = notebookTracker as NotebookTracker; // dirty hack, does only work as long we do not add anything to the model
115
+
116
+ /* widget.context.pathChanged.connect(() => {
117
+ void tracker.save(widget);
118
+ }); // may be we need this */
119
+ // Add the notebook panel to the tracker.
120
+ // void tracker.add(widget);
121
+ widget.context.fileChanged.connect(() => {
122
+ const model = widget.context.model;
123
+ const failsData = model.getMetadata('failsApp');
124
+ const currentSplitView = widget as SplitViewNotebookPanel;
125
+ if (currentSplitView.appletViewWidget) {
126
+ if (failsData) {
127
+ const outputarea = currentSplitView.appletViewWidget;
128
+ if (outputarea !== undefined) {
129
+ outputarea.loadData(failsData);
130
+ }
131
+ }
132
+ }
133
+ });
134
+ widget.context.saveState.connect((slot, savestate) => {
135
+ if (savestate === 'started') {
136
+ const currentSplitView = widget as SplitViewNotebookPanel;
137
+ const outputarea = currentSplitView.appletViewWidget;
138
+ if (outputarea !== undefined) {
139
+ const failsData = outputarea.saveData();
140
+ if (failsData) {
141
+ const model = widget.context.model;
142
+ model.setMetadata('failsApp', failsData);
143
+ }
144
+ }
145
+ }
146
+ });
147
+
148
+ // notebookTracker.inject(widget);
149
+ tracker.add(widget);
150
+ if (!notebookTracker.currentWidget) {
151
+ const pool = tracker['_pool'] as RestorablePool;
152
+ pool.current = widget;
153
+ }
154
+ });
155
+ // Handle state restoration.
156
+ // No the notebook should do this.
157
+ /* if (restorer) {
158
+ const tracker = notebookTracker as NotebookTracker;
159
+ void restorer.restore(tracker, {
160
+ command: 'docmanager:open',
161
+ args: panel => ({ path: panel.context.path, factory: factoryName }),
162
+ name: panel => panel.context.path,
163
+ when: services.ready
164
+ });
165
+ } */
166
+ // remove from registry, this is bad monkey patching
167
+ if (app.docRegistry['_widgetFactories']['notebook']) {
168
+ delete app.docRegistry['_widgetFactories']['notebook'];
169
+ }
170
+
171
+ app.docRegistry.addWidgetFactory(factory);
172
+ app.docRegistry.setDefaultWidgetFactory(
173
+ 'notebook',
174
+ /* 'SplitViewNotebook'*/ 'Notebook'
175
+ );
176
+ // we have to register extensions previously added to the system, FIXME: maybe changed after decoupling from jupyter lab
177
+ /* const itExtension = app.docRegistry.widgetExtensions('Notebook');
178
+ for (const extension of itExtension) {
179
+ app.docRegistry.addWidgetExtension(factoryName, extension);
180
+ }*/
181
+ }
182
+
183
+ const canBeActivated = (): boolean => {
184
+ if (
185
+ notebookTracker.currentWidget === null ||
186
+ notebookTracker.currentWidget !== shell.currentWidget
187
+ ) {
188
+ return false;
189
+ }
190
+ const { content } = notebookTracker.currentWidget!;
191
+ const index = content.activeCellIndex;
192
+ // If there are selections that are not the active cell,
193
+ // this command is confusing, so disable it.
194
+ for (let i = 0; i < content.widgets.length; ++i) {
195
+ if (content.isSelected(content.widgets[i]) && i !== index) {
196
+ return false;
197
+ }
198
+ }
199
+ // If the cell is already added we deactivate as well
200
+ const currentSplitView =
201
+ notebookTracker.currentWidget as SplitViewNotebookPanel;
202
+ if (currentSplitView.appletViewWidget) {
203
+ const outputarea = currentSplitView.appletViewWidget;
204
+ if (outputarea !== undefined && outputarea.firstHasIndex(index)) {
205
+ return false;
206
+ }
207
+ }
208
+ return true;
209
+ };
210
+
211
+ commands.addCommand(addToViewID, {
212
+ label: /* trans.__(*/ 'Add Output to first Applet view' /*)*/,
213
+ execute: async args => {
214
+ const path = args.path as string | undefined | null;
215
+ let index = args.index as number | undefined | null;
216
+ let current: NotebookPanel | undefined | null;
217
+ let cell: Cell | undefined;
218
+
219
+ // console.log('Add Output for path and index', path, index, args);
220
+ if (path && index !== undefined && index !== null) {
221
+ current = docManager.findWidget(
222
+ path,
223
+ 'Notebook' /* may be needs adjustment later*/
224
+ ) as unknown as NotebookPanel;
225
+ if (!current) {
226
+ return;
227
+ }
228
+ } else {
229
+ current = notebookTracker.currentWidget;
230
+ if (!current) {
231
+ return;
232
+ }
233
+ cell = current.content.activeCell as Cell;
234
+ index = current.content.activeCellIndex;
235
+ }
236
+ // const pathid = current.context.path;
237
+ // console.log('debug current cell index', current, cell, index);
238
+ // TODO: Find area if it already exists, and add content
239
+ const currentSplitView = current as SplitViewNotebookPanel;
240
+ if (currentSplitView.appletViewWidget) {
241
+ const outputarea = currentSplitView.appletViewWidget;
242
+ if (outputarea !== undefined && !outputarea.firstHasIndex(index)) {
243
+ outputarea.addPart(undefined, { cell, index });
244
+ }
245
+ }
246
+ },
247
+ icon: args => (args.toolbar ? addIcon : undefined),
248
+ isEnabled: canBeActivated,
249
+ isVisible: canBeActivated
250
+ });
251
+
252
+ function getCurrentNotebook(
253
+ args: ReadonlyPartialJSONObject
254
+ ): NotebookPanel | undefined | null {
255
+ let current: NotebookPanel | undefined | null;
256
+ if (typeof args['notebookpath'] !== 'string') {
257
+ current = notebookTracker.currentWidget;
258
+ if (!current) {
259
+ return;
260
+ }
261
+ } else {
262
+ const path: string = args['notebookpath'];
263
+ current = docManager.findWidget(
264
+ path,
265
+ 'Notebook' /* may be needs adjustment later*/
266
+ ) as unknown as NotebookPanel;
267
+ if (!current) {
268
+ return;
269
+ }
270
+ }
271
+ return current;
272
+ }
273
+
274
+ function moveWidgets(args: ReadonlyPartialJSONObject, delta: number) {
275
+ const current = getCurrentNotebook(args);
276
+ if (!current) {
277
+ return;
278
+ }
279
+ const currentSplitView = current as SplitViewNotebookPanel;
280
+ if (currentSplitView.appletViewWidget) {
281
+ const outputarea = currentSplitView.appletViewWidget;
282
+ const cellid = args.cellid as string;
283
+ const widgetid = args.widgetid as string;
284
+ const appid = outputarea.getWidgetAppId(widgetid);
285
+ if (typeof appid !== 'undefined') {
286
+ outputarea.movePart(appid, cellid, delta);
287
+ }
288
+ }
289
+ }
290
+
291
+ function moveWidgetsApp(args: ReadonlyPartialJSONObject, delta: number) {
292
+ const current = getCurrentNotebook(args);
293
+ if (!current) {
294
+ return;
295
+ }
296
+ const currentSplitView = current as SplitViewNotebookPanel;
297
+ if (currentSplitView.appletViewWidget) {
298
+ const outputarea = currentSplitView.appletViewWidget;
299
+ const cellid = args.cellid as string;
300
+ const widgetid = args.widgetid as string;
301
+ const appid = outputarea.getWidgetAppId(widgetid);
302
+ if (typeof appid !== 'undefined') {
303
+ outputarea.moveApp(appid, cellid, delta);
304
+ }
305
+ }
306
+ }
307
+
308
+ /*
309
+ function canMoveWidgetsApp(
310
+ args: ReadonlyPartialJSONObject,
311
+ delta: number
312
+ ): boolean {
313
+ const current = getCurrentNotebook(args);
314
+ if (!current) {
315
+ return false;
316
+ }
317
+ const currentSplitView = current as Private.SplitViewNotebookPanel;
318
+ if (currentSplitView.appletViewWidget) {
319
+ const outputarea = currentSplitView.appletViewWidget;
320
+ const cellid = args.cellid as string;
321
+ const widgetid = args.widgetid as string;
322
+ const appid = outputarea.getWidgetAppId(widgetid);
323
+ if (typeof appid !== 'undefined') {
324
+ return outputarea.canMoveApp(appid, cellid, delta);
325
+ }
326
+ }
327
+ return false;
328
+ }
329
+ */
330
+ commands.addCommand(moveViewUpID, {
331
+ label: /* trans.__(*/ 'Move view up' /*)*/,
332
+ execute: async args => {
333
+ moveWidgets(args, -1);
334
+ },
335
+ icon: args => (args.toolbar ? moveUpIcon : undefined),
336
+ isEnabled: () => true,
337
+ isVisible: () => true
338
+ });
339
+ commands.addCommand(moveViewDownID, {
340
+ label: /* trans.__(*/ 'Move view down' /*)*/,
341
+ execute: async args => {
342
+ moveWidgets(args, 1);
343
+ },
344
+ icon: args => (args.toolbar ? moveDownIcon : undefined),
345
+ isEnabled: () => true,
346
+ isVisible: () => true
347
+ });
348
+ commands.addCommand(moveViewAppUpID, {
349
+ label: /* trans.__(*/ 'Move view up to other app' /*)*/,
350
+ execute: async args => {
351
+ moveWidgetsApp(args, -1);
352
+ },
353
+ icon: args => (args.toolbar ? caretUpIcon : undefined),
354
+ isEnabled: () => true,
355
+ /* isEnabled: args => {
356
+ return canMoveWidgetsApp(args, -1);
357
+ },*/
358
+ isVisible: () => true
359
+ });
360
+ commands.addCommand(moveViewAppDownID, {
361
+ label: /* trans.__(*/ 'Move view down to other app' /*)*/,
362
+ execute: async args => {
363
+ moveWidgetsApp(args, 1);
364
+ },
365
+ icon: args => (args.toolbar ? caretDownIcon : undefined),
366
+ isEnabled: () => true,
367
+ /*isEnabled: args => {
368
+ return canMoveWidgetsApp(args, 1);
369
+ },*/
370
+ isVisible: () => true
371
+ });
372
+
373
+ commands.addCommand(deleteViewID, {
374
+ label: /* trans.__(*/ 'Delete view' /*)*/,
375
+ execute: async args => {
376
+ const current = getCurrentNotebook(args);
377
+ if (!current) {
378
+ return;
379
+ }
380
+ const currentSplitView = current as SplitViewNotebookPanel;
381
+ if (currentSplitView.appletViewWidget) {
382
+ const outputarea = currentSplitView.appletViewWidget;
383
+ const cellid = args.cellid as string;
384
+ const widgetid = args.widgetid as string;
385
+ const appid = outputarea.getWidgetAppId(widgetid);
386
+ if (typeof appid !== 'undefined') {
387
+ outputarea.deletePart(appid, cellid);
388
+ }
389
+ }
390
+ },
391
+ icon: args => (args.toolbar ? deleteIcon : undefined),
392
+ isEnabled: () => true,
393
+ isVisible: () => true
394
+ });
395
+ }