@fails-components/jupyter-launcher 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.
- package/LICENSE +29 -0
- package/README.md +101 -0
- package/lib/index.d.ts +139 -0
- package/lib/index.js +631 -0
- package/package.json +214 -0
- package/src/index.ts +936 -0
- package/style/base.css +5 -0
- package/style/index.css +1 -0
- package/style/index.js +2 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
import { ILabStatus } from '@jupyterlab/application';
|
|
2
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
3
|
+
import { ServerConnection } from '@jupyterlab/services';
|
|
4
|
+
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
5
|
+
import { INotebookShell } from '@jupyter-notebook/application';
|
|
6
|
+
import { NotebookActions } from '@jupyterlab/notebook';
|
|
7
|
+
import { Token } from '@lumino/coreutils';
|
|
8
|
+
import { Signal } from '@lumino/signaling';
|
|
9
|
+
export const IFailsLauncherInfo = new Token('@fails-components/jupyter-fails:IFailsLauncherInfo', 'A service to communicate with FAILS.');
|
|
10
|
+
let screenShotPatchInstalled = false;
|
|
11
|
+
const installScreenShotPatches = () => {
|
|
12
|
+
if (screenShotPatchInstalled) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
// Monkey patch the HTMLCanvasObject
|
|
16
|
+
const oldGetContext = HTMLCanvasElement.prototype.getContext;
|
|
17
|
+
// @ts-expect-error type do not matter
|
|
18
|
+
HTMLCanvasElement.prototype.getContext = function (contexttype, contextAttributes) {
|
|
19
|
+
if (contexttype === 'webgl' || contexttype === 'webgl2') {
|
|
20
|
+
const newcontext = { ...contextAttributes };
|
|
21
|
+
newcontext.preserveDrawingBuffer = true;
|
|
22
|
+
return oldGetContext.apply(this, [contexttype, newcontext]);
|
|
23
|
+
}
|
|
24
|
+
return oldGetContext.apply(this, [contexttype, contextAttributes]);
|
|
25
|
+
};
|
|
26
|
+
screenShotPatchInstalled = true;
|
|
27
|
+
};
|
|
28
|
+
// Monkey patch fetch, e.g. for GDPR compliance
|
|
29
|
+
let fetchPatchInstalled = false;
|
|
30
|
+
const installFetchPatches = ({ allowedSites = undefined, proxySites = undefined, proxyURL }) => {
|
|
31
|
+
if (fetchPatchInstalled) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const allowedOrigins = [location.origin, ...(allowedSites || [])];
|
|
35
|
+
const allowedOriginsString = allowedOrigins
|
|
36
|
+
.map(el => "'" + new URL(el).origin + "'")
|
|
37
|
+
.join(',');
|
|
38
|
+
const proxySitesString = (proxySites || [])
|
|
39
|
+
.map(el => "'" + new URL(el).origin + "'")
|
|
40
|
+
.join(',');
|
|
41
|
+
// Monkey patch fetch
|
|
42
|
+
const oldFetch = globalThis.fetch;
|
|
43
|
+
globalThis.fetch = async (url, options = {}) => {
|
|
44
|
+
const urlObj = url instanceof URL
|
|
45
|
+
? url
|
|
46
|
+
: new URL(url instanceof Request ? url.url : url, location.href);
|
|
47
|
+
if (allowedOrigins.includes(urlObj.origin)) {
|
|
48
|
+
return oldFetch(url instanceof Request ? url : urlObj, options);
|
|
49
|
+
}
|
|
50
|
+
if (proxySites && proxySites.includes(urlObj.origin)) {
|
|
51
|
+
// rewrite the URL and response
|
|
52
|
+
const resURL = proxyURL + urlObj.hostname + urlObj.pathname;
|
|
53
|
+
if (url instanceof Request) {
|
|
54
|
+
const request = url;
|
|
55
|
+
const newRequest = new Request(resURL, {
|
|
56
|
+
method: request.method,
|
|
57
|
+
headers: request.headers,
|
|
58
|
+
body: request.body,
|
|
59
|
+
credentials: request.credentials,
|
|
60
|
+
mode: request.mode,
|
|
61
|
+
integrity: request.integrity,
|
|
62
|
+
keepalive: request.keepalive,
|
|
63
|
+
referrerPolicy: request.referrerPolicy,
|
|
64
|
+
cache: request.cache,
|
|
65
|
+
redirect: request.redirect,
|
|
66
|
+
referrer: request.referrer,
|
|
67
|
+
signal: request.signal
|
|
68
|
+
});
|
|
69
|
+
return oldFetch(newRequest, options);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
urlObj.href = resURL;
|
|
73
|
+
return oldFetch(urlObj, options);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log('alien fetch URL:', urlObj.href);
|
|
77
|
+
return new Response('Blocked domain, access forbidden', {
|
|
78
|
+
status: 403,
|
|
79
|
+
statusText: 'Forbidden',
|
|
80
|
+
headers: { 'Content-Type': 'text/plain' }
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const oldImportScripts = globalThis.importScripts;
|
|
84
|
+
globalThis.importScripts = (...args) => {
|
|
85
|
+
const newargs = args.map(url => {
|
|
86
|
+
const urlObj = new URL(url, location.href);
|
|
87
|
+
if (allowedOrigins.includes(urlObj.origin)) {
|
|
88
|
+
return url;
|
|
89
|
+
}
|
|
90
|
+
if (proxySites && proxySites.includes(urlObj.origin)) {
|
|
91
|
+
return proxyURL + urlObj.hostname + urlObj.pathname;
|
|
92
|
+
}
|
|
93
|
+
throw new Error('Script is from blocked URL');
|
|
94
|
+
});
|
|
95
|
+
return oldImportScripts(...newargs);
|
|
96
|
+
};
|
|
97
|
+
const oldWorker = Worker;
|
|
98
|
+
const NewWorker = function (script, options) {
|
|
99
|
+
const scriptURL = script instanceof URL ? script : new URL(script, location.href);
|
|
100
|
+
if (!allowedOrigins.includes(scriptURL.origin)) {
|
|
101
|
+
console.log('Creating worker from blocked origin:', scriptURL.origin);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
console.log('Tap into creating worker:', scriptURL.href);
|
|
105
|
+
const injectPrefix = `(function() {
|
|
106
|
+
const allowedOrigins = [ ${allowedOriginsString} ];
|
|
107
|
+
const proxySites = [ ${proxySitesString} ];
|
|
108
|
+
const proxyURL = '${proxyURL}';
|
|
109
|
+
const oldFetch = globalThis.fetch;
|
|
110
|
+
globalThis.fetch = async function(url, options = {}) {
|
|
111
|
+
const urlObj = url instanceof URL ? url : new URL(url instanceof Request ? url.url : url, location.href);
|
|
112
|
+
if (allowedOrigins.includes(urlObj.origin)) {
|
|
113
|
+
return oldFetch(url instanceof Request ? url : urlObj, options);
|
|
114
|
+
}
|
|
115
|
+
if (proxySites && proxySites.includes(urlObj.origin)) {
|
|
116
|
+
// rewrite the URL and response
|
|
117
|
+
const resURL = proxyURL + urlObj.hostname + urlObj.pathname;
|
|
118
|
+
console.log('proxy url debug', resURL, urlObj.href);
|
|
119
|
+
if (url instanceof Request) {
|
|
120
|
+
const request = url;
|
|
121
|
+
const newRequest = new Request(resURL, {
|
|
122
|
+
method: request.method,
|
|
123
|
+
headers: request.headers,
|
|
124
|
+
body: request.body,
|
|
125
|
+
credentials: request.credentials,
|
|
126
|
+
mode: request.mode,
|
|
127
|
+
integrity: request.integrity,
|
|
128
|
+
keepalive: request.keepalive,
|
|
129
|
+
referrerPolicy: request.referrerPolicy,
|
|
130
|
+
cache: request.cache,
|
|
131
|
+
redirect: request.redirect,
|
|
132
|
+
referrer: request.referrer,
|
|
133
|
+
signal: request.signal
|
|
134
|
+
});
|
|
135
|
+
console.log('proxy request debug', newRequest, request);
|
|
136
|
+
return oldFetch(newRequest, options);
|
|
137
|
+
} else {
|
|
138
|
+
urlObj.href = resURL;
|
|
139
|
+
return oldFetch(urlObj, options);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log('alien fetch URL worker:', urlObj.href);
|
|
143
|
+
return new Response('Blocked domain, access forbidden',{
|
|
144
|
+
status: 403,
|
|
145
|
+
statusText: 'Forbidden',
|
|
146
|
+
headers: { 'Content-Type': 'text/plain' }
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const oldImportScripts = globalThis.importScripts;
|
|
150
|
+
globalThis.importScripts = (...args) => {
|
|
151
|
+
const newargs = args.map(url => {
|
|
152
|
+
const urlObj = new URL(url, location.href);
|
|
153
|
+
if (allowedOrigins.includes(urlObj.origin)) {
|
|
154
|
+
return url;
|
|
155
|
+
}
|
|
156
|
+
if (proxySites && proxySites.includes(urlObj.origin)) {
|
|
157
|
+
return proxyURL + urlObj.hostname + urlObj.pathname;
|
|
158
|
+
}
|
|
159
|
+
throw new Error('Script is from blocked URL');
|
|
160
|
+
});
|
|
161
|
+
return oldImportScripts(...newargs);
|
|
162
|
+
};
|
|
163
|
+
Object.defineProperty(globalThis, 'location', {
|
|
164
|
+
value: new URL('${scriptURL.href}'),
|
|
165
|
+
writable: false,
|
|
166
|
+
configurable: false,
|
|
167
|
+
enumerable: true,
|
|
168
|
+
});
|
|
169
|
+
})();`;
|
|
170
|
+
const inject = injectPrefix +
|
|
171
|
+
((options === null || options === void 0 ? void 0 : options.type) === 'module'
|
|
172
|
+
? `
|
|
173
|
+
import('${scriptURL.href}').catch(error => {
|
|
174
|
+
console.error('Can not load module patching Worker:', error);
|
|
175
|
+
});`
|
|
176
|
+
: `
|
|
177
|
+
importScripts('${scriptURL.href}')
|
|
178
|
+
`);
|
|
179
|
+
const blob = new Blob([inject], { type: 'application/javascript' });
|
|
180
|
+
const url = URL.createObjectURL(blob);
|
|
181
|
+
const newWorker = new oldWorker(url, options);
|
|
182
|
+
newWorker.addEventListener('message', () => {
|
|
183
|
+
URL.revokeObjectURL(url);
|
|
184
|
+
}, { once: true });
|
|
185
|
+
return newWorker;
|
|
186
|
+
};
|
|
187
|
+
NewWorker.prototype = oldWorker.prototype;
|
|
188
|
+
NewWorker.prototype.constructor = NewWorker;
|
|
189
|
+
globalThis.Worker = NewWorker;
|
|
190
|
+
fetchPatchInstalled = true;
|
|
191
|
+
};
|
|
192
|
+
class FailsLauncherInfo {
|
|
193
|
+
constructor(options) {
|
|
194
|
+
var _a, _b;
|
|
195
|
+
this._inLectureChanged = new Signal(this);
|
|
196
|
+
this._selectedAppidChanged = new Signal(this);
|
|
197
|
+
this._remoteUpdateMessageArrived = new Signal(this);
|
|
198
|
+
this._appletSizes = {};
|
|
199
|
+
this._appletSizesChanged = new Signal(this);
|
|
200
|
+
this._appletSizesProxy = new Proxy(this._appletSizes, {
|
|
201
|
+
get: (target, property) => {
|
|
202
|
+
if (typeof property !== 'symbol') {
|
|
203
|
+
return target[property];
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
set: (target, property, value) => {
|
|
207
|
+
if (typeof property !== 'symbol') {
|
|
208
|
+
if (target[property] !== value) {
|
|
209
|
+
target[property] = value;
|
|
210
|
+
this._appletSizesChanged.emit(target);
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
this._inLecture = (_a = options === null || options === void 0 ? void 0 : options.inLecture) !== null && _a !== void 0 ? _a : false;
|
|
218
|
+
this._selectedAppid = (_b = options === null || options === void 0 ? void 0 : options.selectedAppid) !== null && _b !== void 0 ? _b : undefined;
|
|
219
|
+
}
|
|
220
|
+
get inLectureChanged() {
|
|
221
|
+
return this._inLectureChanged;
|
|
222
|
+
}
|
|
223
|
+
get inLecture() {
|
|
224
|
+
return this._inLecture;
|
|
225
|
+
}
|
|
226
|
+
set inLecture(value) {
|
|
227
|
+
if (value === this._inLecture) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this._inLecture = value;
|
|
231
|
+
if (value === false && this._selectedAppid) {
|
|
232
|
+
this._selectedAppid = undefined;
|
|
233
|
+
this._selectedAppidChanged.emit(undefined);
|
|
234
|
+
}
|
|
235
|
+
this._inLectureChanged.emit(value);
|
|
236
|
+
}
|
|
237
|
+
get selectedAppid() {
|
|
238
|
+
return this._selectedAppid;
|
|
239
|
+
}
|
|
240
|
+
get selectedAppidChanged() {
|
|
241
|
+
return this._selectedAppidChanged;
|
|
242
|
+
}
|
|
243
|
+
set selectedAppid(appid) {
|
|
244
|
+
if (appid === this._selectedAppid) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
this._selectedAppid = appid;
|
|
248
|
+
if (!this._inLecture && typeof appid !== 'undefined') {
|
|
249
|
+
this._inLecture = true;
|
|
250
|
+
this._inLectureChanged.emit(true);
|
|
251
|
+
}
|
|
252
|
+
else if (this._inLecture && typeof appid === 'undefined') {
|
|
253
|
+
this._inLecture = false;
|
|
254
|
+
this._inLectureChanged.emit(false);
|
|
255
|
+
}
|
|
256
|
+
this._selectedAppidChanged.emit(appid);
|
|
257
|
+
}
|
|
258
|
+
get appletSizes() {
|
|
259
|
+
return this._appletSizesProxy;
|
|
260
|
+
}
|
|
261
|
+
get appletSizesChanged() {
|
|
262
|
+
return this._appletSizesChanged;
|
|
263
|
+
}
|
|
264
|
+
get updateMessageArrived() {
|
|
265
|
+
return this._updateMessageArrived;
|
|
266
|
+
}
|
|
267
|
+
set updateMessageArrived(updateMessageArrived) {
|
|
268
|
+
this._updateMessageArrived = updateMessageArrived;
|
|
269
|
+
}
|
|
270
|
+
get remoteUpdateMessageArrived() {
|
|
271
|
+
return this._remoteUpdateMessageArrived;
|
|
272
|
+
}
|
|
273
|
+
receiveRemoteUpdateMessage(message) {
|
|
274
|
+
this._remoteUpdateMessageArrived.emit(message);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function activateFailsLauncher(app, docManager, status, shell) {
|
|
278
|
+
// parts taken from repl-extension
|
|
279
|
+
const { /* commands, */ serviceManager, started } = app;
|
|
280
|
+
Promise.all([started, serviceManager.ready]).then(async () => {
|
|
281
|
+
/* commands.execute('notebook:create-new', {
|
|
282
|
+
kernelId: undefined,
|
|
283
|
+
kernelName: undefined
|
|
284
|
+
}); */
|
|
285
|
+
// TODO select kernel and replace with content
|
|
286
|
+
});
|
|
287
|
+
// TODO steer with messages
|
|
288
|
+
const { docRegistry } = app;
|
|
289
|
+
const failsLauncherInfo = new FailsLauncherInfo();
|
|
290
|
+
let currentDocWidget;
|
|
291
|
+
const serverSettings = app.serviceManager.serverSettings;
|
|
292
|
+
const licensesUrl = URLExt.join(PageConfig.getBaseUrl(), PageConfig.getOption('licensesUrl')) +
|
|
293
|
+
'/';
|
|
294
|
+
// Install Messagehandler
|
|
295
|
+
if (!window.failsCallbacks) {
|
|
296
|
+
window.failsCallbacks = {};
|
|
297
|
+
}
|
|
298
|
+
let senderOrigin;
|
|
299
|
+
const _failsCallbacks = window.failsCallbacks;
|
|
300
|
+
_failsCallbacks.postMessageToFails = (message, transfer) => {
|
|
301
|
+
if (typeof senderOrigin !== 'undefined') {
|
|
302
|
+
window.parent.postMessage(message, senderOrigin, transfer);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
status.dirtySignal.connect((sender, dirty) => {
|
|
306
|
+
_failsCallbacks.postMessageToFails({
|
|
307
|
+
task: 'docDirty',
|
|
308
|
+
dirty
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
failsLauncherInfo.reportMetadata = metadata => {
|
|
312
|
+
_failsCallbacks.postMessageToFails({
|
|
313
|
+
task: 'reportMetadata',
|
|
314
|
+
metadata
|
|
315
|
+
});
|
|
316
|
+
};
|
|
317
|
+
failsLauncherInfo.inLectureChanged.connect((sender, inLecture) => {
|
|
318
|
+
if (shell !== null) {
|
|
319
|
+
shell.menu.setHidden(inLecture);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
failsLauncherInfo.appletSizesChanged.connect((sender, appletSizes) => {
|
|
323
|
+
_failsCallbacks.postMessageToFails({
|
|
324
|
+
task: 'reportFailsAppletSizes',
|
|
325
|
+
appletSizes
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
let interceptorActivated = false;
|
|
329
|
+
const sendInterceptorUpdate = (sender, message) => {
|
|
330
|
+
_failsCallbacks.postMessageToFails({
|
|
331
|
+
task: 'sendInterceptorUpdate',
|
|
332
|
+
...message
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
window.addEventListener('message', (event) => {
|
|
336
|
+
var _a;
|
|
337
|
+
// TODO identify the embedding page.
|
|
338
|
+
if (typeof senderOrigin === 'undefined') {
|
|
339
|
+
senderOrigin = event.origin;
|
|
340
|
+
}
|
|
341
|
+
// handle FAILS control messages
|
|
342
|
+
switch (event.data.type) {
|
|
343
|
+
case 'loadJupyter':
|
|
344
|
+
{
|
|
345
|
+
const loadJupyterInfo = event.data;
|
|
346
|
+
failsLauncherInfo.inLecture =
|
|
347
|
+
(_a = loadJupyterInfo.inLecture) !== null && _a !== void 0 ? _a : failsLauncherInfo.inLecture;
|
|
348
|
+
docManager.autosave = false; // do not autosave
|
|
349
|
+
if (loadJupyterInfo.installScreenShotPatches) {
|
|
350
|
+
installScreenShotPatches();
|
|
351
|
+
}
|
|
352
|
+
if (loadJupyterInfo.installGDPRProxy) {
|
|
353
|
+
installFetchPatches(loadJupyterInfo.installGDPRProxy);
|
|
354
|
+
}
|
|
355
|
+
// TODO send fileData to contents together with filename, and wait for fullfillment
|
|
356
|
+
// may be use a promise for fullfillment, e.g. pass a resolve
|
|
357
|
+
// afterwards we load the file or new file into to the contexts
|
|
358
|
+
// we may also send license information
|
|
359
|
+
_failsCallbacks.callContents({
|
|
360
|
+
task: 'loadFile',
|
|
361
|
+
fileData: loadJupyterInfo.fileData,
|
|
362
|
+
fileName: loadJupyterInfo.fileName
|
|
363
|
+
})
|
|
364
|
+
.then(() => {
|
|
365
|
+
// ok the file is placed inside the file system now load it into the app
|
|
366
|
+
const kernel = {
|
|
367
|
+
name: loadJupyterInfo.kernelName || 'python' // 'xpython' for xeus
|
|
368
|
+
};
|
|
369
|
+
const defaultFactory = docRegistry.defaultWidgetFactory(loadJupyterInfo.fileName).name;
|
|
370
|
+
const factory = defaultFactory;
|
|
371
|
+
currentDocWidget = docManager.open(loadJupyterInfo.fileName, factory, kernel, {
|
|
372
|
+
ref: '_noref'
|
|
373
|
+
});
|
|
374
|
+
if (loadJupyterInfo.appid) {
|
|
375
|
+
failsLauncherInfo.selectedAppid = loadJupyterInfo.appid;
|
|
376
|
+
}
|
|
377
|
+
let rerunAfterKernelStart = loadJupyterInfo.rerunAtStartup;
|
|
378
|
+
if (typeof currentDocWidget !== 'undefined') {
|
|
379
|
+
const notebookPanel = currentDocWidget;
|
|
380
|
+
notebookPanel.sessionContext.statusChanged.connect((context, status) => {
|
|
381
|
+
_failsCallbacks.postMessageToFails({
|
|
382
|
+
task: 'reportKernelStatus',
|
|
383
|
+
status
|
|
384
|
+
});
|
|
385
|
+
if (status === 'idle' && rerunAfterKernelStart) {
|
|
386
|
+
console.log('Run all cells after startup');
|
|
387
|
+
const { context, content } = notebookPanel;
|
|
388
|
+
const cells = content.widgets;
|
|
389
|
+
NotebookActions.runCells(content, cells, context.sessionContext)
|
|
390
|
+
.then(() => {
|
|
391
|
+
console.log('Run all cells after startup finished');
|
|
392
|
+
})
|
|
393
|
+
.catch(error => {
|
|
394
|
+
console.log('Run all cells after startup error', error);
|
|
395
|
+
});
|
|
396
|
+
rerunAfterKernelStart = false;
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
})
|
|
401
|
+
.catch(error => {
|
|
402
|
+
console.log('Problem task load file', error);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
406
|
+
case 'saveJupyter':
|
|
407
|
+
{
|
|
408
|
+
const saveJupyter = event.data;
|
|
409
|
+
if (typeof currentDocWidget === 'undefined') {
|
|
410
|
+
_failsCallbacks.postMessageToFails({
|
|
411
|
+
requestId: event.data.requestId,
|
|
412
|
+
task: 'saveJupyter',
|
|
413
|
+
error: 'No document loaded'
|
|
414
|
+
});
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
const context = docManager.contextForWidget(currentDocWidget);
|
|
418
|
+
if (typeof context === 'undefined') {
|
|
419
|
+
_failsCallbacks.postMessageToFails({
|
|
420
|
+
requestId: event.data.requestId,
|
|
421
|
+
task: 'saveJupyter',
|
|
422
|
+
error: 'No document context'
|
|
423
|
+
});
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
context
|
|
427
|
+
.save()
|
|
428
|
+
.then(() => {
|
|
429
|
+
// ok it was save to our virtual disk
|
|
430
|
+
return _failsCallbacks.callContents({
|
|
431
|
+
task: 'savedFile',
|
|
432
|
+
fileName: saveJupyter.fileName
|
|
433
|
+
});
|
|
434
|
+
})
|
|
435
|
+
.then(({ fileData }) => {
|
|
436
|
+
_failsCallbacks.postMessageToFails({
|
|
437
|
+
requestId: event.data.requestId,
|
|
438
|
+
task: 'saveJupyter',
|
|
439
|
+
fileData
|
|
440
|
+
});
|
|
441
|
+
})
|
|
442
|
+
.catch((error) => {
|
|
443
|
+
_failsCallbacks.postMessageToFails({
|
|
444
|
+
requestId: event.data.requestId,
|
|
445
|
+
task: 'saveJupyter',
|
|
446
|
+
error: error.toString()
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
case 'activateApp':
|
|
452
|
+
{
|
|
453
|
+
const activateApp = event.data;
|
|
454
|
+
if (activateApp.inLecture) {
|
|
455
|
+
failsLauncherInfo.selectedAppid = activateApp.appid;
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
failsLauncherInfo.inLecture = false;
|
|
459
|
+
}
|
|
460
|
+
_failsCallbacks.postMessageToFails({
|
|
461
|
+
requestId: event.data.requestId,
|
|
462
|
+
task: 'activateApp'
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
case 'screenshotApp':
|
|
467
|
+
{
|
|
468
|
+
const screenshotApp = event.data;
|
|
469
|
+
const notebookPanel = currentDocWidget;
|
|
470
|
+
if (!(typeof notebookPanel['takeAppScreenshot'] === 'function')) {
|
|
471
|
+
_failsCallbacks.postMessageToFails({
|
|
472
|
+
requestId: event.data.requestId,
|
|
473
|
+
task: 'screenshotApp',
|
|
474
|
+
error: 'Take App Screenshot unsupported'
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
const screenShotTaker = notebookPanel;
|
|
478
|
+
screenShotTaker
|
|
479
|
+
.takeAppScreenshot({ dpi: screenshotApp.dpi })
|
|
480
|
+
.then(async (screenshot) => {
|
|
481
|
+
var _a, _b;
|
|
482
|
+
if (screenshot) {
|
|
483
|
+
const data = await screenshot.arrayBuffer();
|
|
484
|
+
(_a = _failsCallbacks.postMessageToFails) === null || _a === void 0 ? void 0 : _a.call(_failsCallbacks, {
|
|
485
|
+
requestId: event.data.requestId,
|
|
486
|
+
task: 'screenshotApp',
|
|
487
|
+
screenshot: { data, type: screenshot.type }
|
|
488
|
+
}, [data]); // TODO add transferable
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
(_b = _failsCallbacks.postMessageToFails) === null || _b === void 0 ? void 0 : _b.call(_failsCallbacks, {
|
|
492
|
+
requestId: event.data.requestId,
|
|
493
|
+
task: 'screenshotApp',
|
|
494
|
+
error: 'Screenshot failed?'
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
})
|
|
498
|
+
.catch((error) => {
|
|
499
|
+
console.log('Screenshot error', error);
|
|
500
|
+
_failsCallbacks.postMessageToFails({
|
|
501
|
+
requestId: event.data.requestId,
|
|
502
|
+
task: 'screenshotApp',
|
|
503
|
+
error: error.toString()
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
case 'activateInterceptor':
|
|
509
|
+
{
|
|
510
|
+
const activateInterceptor = event.data;
|
|
511
|
+
if (interceptorActivated !== activateInterceptor.activate &&
|
|
512
|
+
failsLauncherInfo.updateMessageArrived) {
|
|
513
|
+
if (!interceptorActivated) {
|
|
514
|
+
failsLauncherInfo.updateMessageArrived.connect(sendInterceptorUpdate);
|
|
515
|
+
interceptorActivated = true;
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
failsLauncherInfo.updateMessageArrived.disconnect(sendInterceptorUpdate);
|
|
519
|
+
interceptorActivated = false;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
_failsCallbacks.postMessageToFails({
|
|
523
|
+
requestId: event.data.requestId,
|
|
524
|
+
task: 'activateInterceptor'
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
break;
|
|
528
|
+
case 'receiveInterceptorUpdate':
|
|
529
|
+
{
|
|
530
|
+
const receiveInterceptorUpdate = event.data;
|
|
531
|
+
const { path, mime, state } = receiveInterceptorUpdate;
|
|
532
|
+
const launcherInfo = failsLauncherInfo;
|
|
533
|
+
launcherInfo.receiveRemoteUpdateMessage({ path, mime, state });
|
|
534
|
+
_failsCallbacks.postMessageToFails({
|
|
535
|
+
requestId: event.data.requestId,
|
|
536
|
+
task: 'receiveInterceptorUpdate'
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
break;
|
|
540
|
+
case 'restartKernelAndRerunCells':
|
|
541
|
+
{
|
|
542
|
+
if (typeof currentDocWidget === 'undefined') {
|
|
543
|
+
_failsCallbacks.postMessageToFails({
|
|
544
|
+
requestId: event.data.requestId,
|
|
545
|
+
task: 'restartKernelAndRerunCell',
|
|
546
|
+
error: 'No document loaded'
|
|
547
|
+
});
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
const notebookPanel = currentDocWidget;
|
|
551
|
+
const { context, content } = notebookPanel;
|
|
552
|
+
const cells = content.widgets;
|
|
553
|
+
console.log('rerun kernel hook');
|
|
554
|
+
notebookPanel.sessionContext
|
|
555
|
+
.restartKernel()
|
|
556
|
+
.then(async () => {
|
|
557
|
+
await NotebookActions.runCells(content, cells, context.sessionContext);
|
|
558
|
+
_failsCallbacks.postMessageToFails({
|
|
559
|
+
requestId: event.data.requestId,
|
|
560
|
+
task: 'restartKernelAndRerunCell',
|
|
561
|
+
success: true
|
|
562
|
+
});
|
|
563
|
+
})
|
|
564
|
+
.catch((error) => {
|
|
565
|
+
_failsCallbacks.postMessageToFails({
|
|
566
|
+
requestId: event.data.requestId,
|
|
567
|
+
task: 'restartKernelAndRerunCell',
|
|
568
|
+
error: error.toString()
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
case 'getLicenses':
|
|
574
|
+
{
|
|
575
|
+
ServerConnection.makeRequest(licensesUrl, {}, serverSettings)
|
|
576
|
+
.then(async (response) => {
|
|
577
|
+
const json = await response.json();
|
|
578
|
+
_failsCallbacks.postMessageToFails({
|
|
579
|
+
requestId: event.data.requestId,
|
|
580
|
+
task: 'getLicenses',
|
|
581
|
+
licenses: json
|
|
582
|
+
});
|
|
583
|
+
})
|
|
584
|
+
.catch(error => {
|
|
585
|
+
_failsCallbacks.postMessageToFails({
|
|
586
|
+
requestId: event.data.requestId,
|
|
587
|
+
task: 'getLicenses',
|
|
588
|
+
error: error.toString()
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
app.started.then(async () => {
|
|
596
|
+
if (window.parent) {
|
|
597
|
+
window.parent.postMessage({
|
|
598
|
+
task: 'appLoaded'
|
|
599
|
+
}, '*' // this is relatively safe, as we only say we are ready
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
if (shell) {
|
|
603
|
+
// we have a notebook
|
|
604
|
+
shell.collapseTop();
|
|
605
|
+
if (failsLauncherInfo.inLecture) {
|
|
606
|
+
const menuWrapper = shell['_menuWrapper'];
|
|
607
|
+
menuWrapper.hide();
|
|
608
|
+
// const main = shell['_main'] as SplitViewNotebookPanel;
|
|
609
|
+
// main.toolbar.hide();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
return failsLauncherInfo;
|
|
614
|
+
}
|
|
615
|
+
const failsLauncher = {
|
|
616
|
+
id: '@fails-components/jupyter-fails:launcher',
|
|
617
|
+
description: 'Configures the notebooks application over messages',
|
|
618
|
+
autoStart: true,
|
|
619
|
+
activate: activateFailsLauncher,
|
|
620
|
+
provides: IFailsLauncherInfo,
|
|
621
|
+
requires: [IDocumentManager, ILabStatus],
|
|
622
|
+
optional: [INotebookShell]
|
|
623
|
+
};
|
|
624
|
+
/**
|
|
625
|
+
* Initialization data for the @fails-components/jupyter-launcher extension.
|
|
626
|
+
*/
|
|
627
|
+
const plugins = [
|
|
628
|
+
// all JupyterFrontEndPlugins
|
|
629
|
+
failsLauncher
|
|
630
|
+
];
|
|
631
|
+
export default plugins;
|