@jupyter-notebook/application-extension 7.0.0-alpha.1
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.d.ts +6 -0
- package/lib/index.js +604 -0
- package/package.json +72 -0
- package/schema/menus.json +24 -0
- package/schema/top.json +16 -0
- package/style/base.css +14 -0
- package/style/index.css +4 -0
- package/style/index.js +4 -0
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
import { ILabStatus, IRouter, ITreePathUpdater, JupyterFrontEnd, Router } from '@jupyterlab/application';
|
|
4
|
+
import { sessionContextDialogs, ISessionContextDialogs, DOMUtils, ICommandPalette } from '@jupyterlab/apputils';
|
|
5
|
+
import { ConsolePanel } from '@jupyterlab/console';
|
|
6
|
+
import { PageConfig, PathExt, URLExt } from '@jupyterlab/coreutils';
|
|
7
|
+
import { IDocumentManager, renameDialog } from '@jupyterlab/docmanager';
|
|
8
|
+
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
9
|
+
import { IMainMenu } from '@jupyterlab/mainmenu';
|
|
10
|
+
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
11
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
12
|
+
import { NotebookApp, NotebookShell, INotebookShell } from '@jupyter-notebook/application';
|
|
13
|
+
import { jupyterIcon } from '@jupyter-notebook/ui-components';
|
|
14
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
15
|
+
import { DisposableDelegate, DisposableSet } from '@lumino/disposable';
|
|
16
|
+
import { Widget } from '@lumino/widgets';
|
|
17
|
+
/**
|
|
18
|
+
* The default notebook factory.
|
|
19
|
+
*/
|
|
20
|
+
const NOTEBOOK_FACTORY = 'Notebook';
|
|
21
|
+
/**
|
|
22
|
+
* The editor factory.
|
|
23
|
+
*/
|
|
24
|
+
const EDITOR_FACTORY = 'Editor';
|
|
25
|
+
/**
|
|
26
|
+
* A regular expression to match path to notebooks and documents
|
|
27
|
+
*/
|
|
28
|
+
const TREE_PATTERN = new RegExp('/(notebooks|edit)/(.*)');
|
|
29
|
+
/**
|
|
30
|
+
* A regular expression to suppress the file extension from display for .ipynb files.
|
|
31
|
+
*/
|
|
32
|
+
const STRIP_IPYNB = /\.ipynb$/;
|
|
33
|
+
/**
|
|
34
|
+
* The command IDs used by the application plugin.
|
|
35
|
+
*/
|
|
36
|
+
var CommandIDs;
|
|
37
|
+
(function (CommandIDs) {
|
|
38
|
+
/**
|
|
39
|
+
* Toggle Top Bar visibility
|
|
40
|
+
*/
|
|
41
|
+
CommandIDs.toggleTop = 'application:toggle-top';
|
|
42
|
+
/**
|
|
43
|
+
* Toggle the Zen mode
|
|
44
|
+
*/
|
|
45
|
+
CommandIDs.toggleZen = 'application:toggle-zen';
|
|
46
|
+
/**
|
|
47
|
+
* Open JupyterLab
|
|
48
|
+
*/
|
|
49
|
+
CommandIDs.openLab = 'application:open-lab';
|
|
50
|
+
/**
|
|
51
|
+
* Open the tree page.
|
|
52
|
+
*/
|
|
53
|
+
CommandIDs.openTree = 'application:open-tree';
|
|
54
|
+
/**
|
|
55
|
+
* Rename the current document
|
|
56
|
+
*/
|
|
57
|
+
CommandIDs.rename = 'application:rename';
|
|
58
|
+
/**
|
|
59
|
+
* Resolve tree path
|
|
60
|
+
*/
|
|
61
|
+
CommandIDs.resolveTree = 'application:resolve-tree';
|
|
62
|
+
})(CommandIDs || (CommandIDs = {}));
|
|
63
|
+
/**
|
|
64
|
+
* Check if the application is dirty before closing the browser tab.
|
|
65
|
+
*/
|
|
66
|
+
const dirty = {
|
|
67
|
+
id: '@jupyter-notebook/application-extension:dirty',
|
|
68
|
+
autoStart: true,
|
|
69
|
+
requires: [ILabStatus, ITranslator],
|
|
70
|
+
activate: (app, status, translator) => {
|
|
71
|
+
if (!(app instanceof NotebookApp)) {
|
|
72
|
+
throw new Error(`${dirty.id} must be activated in Jupyter Notebook.`);
|
|
73
|
+
}
|
|
74
|
+
const trans = translator.load('notebook');
|
|
75
|
+
const message = trans.__('Are you sure you want to exit Jupyter Notebook?\n\nAny unsaved changes will be lost.');
|
|
76
|
+
window.addEventListener('beforeunload', event => {
|
|
77
|
+
if (app.status.isDirty) {
|
|
78
|
+
return (event.returnValue = message);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* The logo plugin.
|
|
85
|
+
*/
|
|
86
|
+
const logo = {
|
|
87
|
+
id: '@jupyter-notebook/application-extension:logo',
|
|
88
|
+
autoStart: true,
|
|
89
|
+
activate: (app) => {
|
|
90
|
+
const baseUrl = PageConfig.getBaseUrl();
|
|
91
|
+
const node = document.createElement('a');
|
|
92
|
+
node.href = `${baseUrl}tree`;
|
|
93
|
+
node.target = '_blank';
|
|
94
|
+
node.rel = 'noopener noreferrer';
|
|
95
|
+
const logo = new Widget({ node });
|
|
96
|
+
jupyterIcon.element({
|
|
97
|
+
container: node,
|
|
98
|
+
elementPosition: 'center',
|
|
99
|
+
padding: '2px 2px 2px 8px',
|
|
100
|
+
height: '28px',
|
|
101
|
+
width: 'auto'
|
|
102
|
+
});
|
|
103
|
+
logo.id = 'jp-NotebookLogo';
|
|
104
|
+
app.shell.add(logo, 'top', { rank: 0 });
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* A plugin to open documents in the main area.
|
|
109
|
+
*/
|
|
110
|
+
const opener = {
|
|
111
|
+
id: '@jupyter-notebook/application-extension:opener',
|
|
112
|
+
autoStart: true,
|
|
113
|
+
requires: [IRouter, IDocumentManager],
|
|
114
|
+
activate: (app, router, docManager) => {
|
|
115
|
+
const { commands } = app;
|
|
116
|
+
const command = 'router:tree';
|
|
117
|
+
commands.addCommand(command, {
|
|
118
|
+
execute: (args) => {
|
|
119
|
+
var _a;
|
|
120
|
+
const parsed = args;
|
|
121
|
+
const matches = (_a = parsed.path.match(TREE_PATTERN)) !== null && _a !== void 0 ? _a : [];
|
|
122
|
+
const [, , path] = matches;
|
|
123
|
+
if (!path) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const file = decodeURIComponent(path);
|
|
127
|
+
const ext = PathExt.extname(file);
|
|
128
|
+
app.restored.then(async () => {
|
|
129
|
+
// TODO: get factory from file type instead?
|
|
130
|
+
if (ext === '.ipynb') {
|
|
131
|
+
docManager.open(file, NOTEBOOK_FACTORY, undefined, {
|
|
132
|
+
ref: '_noref'
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
docManager.open(file, EDITOR_FACTORY, undefined, {
|
|
137
|
+
ref: '_noref'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
router.register({ command, pattern: TREE_PATTERN });
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* A plugin to customize menus
|
|
148
|
+
*
|
|
149
|
+
* TODO: use this plugin to customize the menu items and their order
|
|
150
|
+
*/
|
|
151
|
+
const menus = {
|
|
152
|
+
id: '@jupyter-notebook/application-extension:menus',
|
|
153
|
+
requires: [IMainMenu],
|
|
154
|
+
autoStart: true,
|
|
155
|
+
activate: (app, menu) => {
|
|
156
|
+
// always disable the Tabs menu
|
|
157
|
+
menu.tabsMenu.dispose();
|
|
158
|
+
const page = PageConfig.getOption('notebookPage');
|
|
159
|
+
switch (page) {
|
|
160
|
+
case 'consoles':
|
|
161
|
+
case 'terminals':
|
|
162
|
+
case 'tree':
|
|
163
|
+
menu.editMenu.dispose();
|
|
164
|
+
menu.kernelMenu.dispose();
|
|
165
|
+
menu.runMenu.dispose();
|
|
166
|
+
break;
|
|
167
|
+
case 'edit':
|
|
168
|
+
menu.kernelMenu.dispose();
|
|
169
|
+
menu.runMenu.dispose();
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Add commands to open the tree and running pages.
|
|
178
|
+
*/
|
|
179
|
+
const pages = {
|
|
180
|
+
id: '@jupyter-notebook/application-extension:pages',
|
|
181
|
+
autoStart: true,
|
|
182
|
+
requires: [ITranslator],
|
|
183
|
+
optional: [ICommandPalette, IMainMenu],
|
|
184
|
+
activate: (app, translator, palette, menu) => {
|
|
185
|
+
const trans = translator.load('notebook');
|
|
186
|
+
const baseUrl = PageConfig.getBaseUrl();
|
|
187
|
+
app.commands.addCommand(CommandIDs.openLab, {
|
|
188
|
+
label: trans.__('Open JupyterLab'),
|
|
189
|
+
execute: () => {
|
|
190
|
+
window.open(`${baseUrl}lab`);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
app.commands.addCommand(CommandIDs.openTree, {
|
|
194
|
+
label: trans.__('Open Files'),
|
|
195
|
+
execute: () => {
|
|
196
|
+
window.open(`${baseUrl}tree`);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
if (palette) {
|
|
200
|
+
[CommandIDs.openLab, CommandIDs.openTree].forEach(command => {
|
|
201
|
+
palette.addItem({ command, category: 'View' });
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (menu) {
|
|
205
|
+
menu.viewMenu.addGroup([{ command: CommandIDs.openLab }, { command: CommandIDs.openTree }], 0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* The default paths for a Jupyter Notebook app.
|
|
211
|
+
*/
|
|
212
|
+
const paths = {
|
|
213
|
+
id: '@jupyter-notebook/application-extension:paths',
|
|
214
|
+
autoStart: true,
|
|
215
|
+
provides: JupyterFrontEnd.IPaths,
|
|
216
|
+
activate: (app) => {
|
|
217
|
+
if (!(app instanceof NotebookApp)) {
|
|
218
|
+
throw new Error(`${paths.id} must be activated in Jupyter Notebook.`);
|
|
219
|
+
}
|
|
220
|
+
return app.paths;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* The default URL router provider.
|
|
225
|
+
*/
|
|
226
|
+
const router = {
|
|
227
|
+
id: '@jupyter-notebook/application-extension:router',
|
|
228
|
+
autoStart: true,
|
|
229
|
+
provides: IRouter,
|
|
230
|
+
requires: [JupyterFrontEnd.IPaths],
|
|
231
|
+
activate: (app, paths) => {
|
|
232
|
+
const { commands } = app;
|
|
233
|
+
const base = paths.urls.base;
|
|
234
|
+
const router = new Router({ base, commands });
|
|
235
|
+
void app.started.then(() => {
|
|
236
|
+
// Route the very first request on load.
|
|
237
|
+
void router.route();
|
|
238
|
+
// Route all pop state events.
|
|
239
|
+
window.addEventListener('popstate', () => {
|
|
240
|
+
void router.route();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
return router;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* The default session dialogs plugin
|
|
248
|
+
*/
|
|
249
|
+
const sessionDialogs = {
|
|
250
|
+
id: '@jupyter-notebook/application-extension:session-dialogs',
|
|
251
|
+
provides: ISessionContextDialogs,
|
|
252
|
+
autoStart: true,
|
|
253
|
+
activate: () => sessionContextDialogs
|
|
254
|
+
};
|
|
255
|
+
/**
|
|
256
|
+
* The default Jupyter Notebook application shell.
|
|
257
|
+
*/
|
|
258
|
+
const shell = {
|
|
259
|
+
id: '@jupyter-notebook/application-extension:shell',
|
|
260
|
+
activate: (app) => {
|
|
261
|
+
if (!(app.shell instanceof NotebookShell)) {
|
|
262
|
+
throw new Error(`${shell.id} did not find a NotebookShell instance.`);
|
|
263
|
+
}
|
|
264
|
+
return app.shell;
|
|
265
|
+
},
|
|
266
|
+
autoStart: true,
|
|
267
|
+
provides: INotebookShell
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* A plugin to provide a spacer at rank 900 for flex panels
|
|
271
|
+
* TODO: reuse upstream @jupyterlab/application-extension:top-spacer plugin when fixed
|
|
272
|
+
* in https://github.com/jupyterlab/jupyterlab/pull/11900
|
|
273
|
+
*/
|
|
274
|
+
const spacer = {
|
|
275
|
+
id: '@jupyter-notebook/application-extension:spacer',
|
|
276
|
+
autoStart: true,
|
|
277
|
+
activate: (app) => {
|
|
278
|
+
const top = new Widget();
|
|
279
|
+
top.id = DOMUtils.createDomID();
|
|
280
|
+
top.addClass('jp-NotebookSpacer');
|
|
281
|
+
app.shell.add(top, 'top', { rank: 900 });
|
|
282
|
+
const menu = new Widget();
|
|
283
|
+
menu.id = DOMUtils.createDomID();
|
|
284
|
+
menu.addClass('jp-NotebookSpacer');
|
|
285
|
+
app.shell.add(menu, 'menu', { rank: 900 });
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* The default JupyterLab application status provider.
|
|
290
|
+
*/
|
|
291
|
+
const status = {
|
|
292
|
+
id: '@jupyter-notebook/application-extension:status',
|
|
293
|
+
autoStart: true,
|
|
294
|
+
provides: ILabStatus,
|
|
295
|
+
activate: (app) => {
|
|
296
|
+
if (!(app instanceof NotebookApp)) {
|
|
297
|
+
throw new Error(`${status.id} must be activated in Jupyter Notebook.`);
|
|
298
|
+
}
|
|
299
|
+
return app.status;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* A plugin to display the document title in the browser tab title
|
|
304
|
+
*/
|
|
305
|
+
const tabTitle = {
|
|
306
|
+
id: '@jupyter-notebook/application-extension:tab-title',
|
|
307
|
+
autoStart: true,
|
|
308
|
+
requires: [INotebookShell],
|
|
309
|
+
activate: (app, shell) => {
|
|
310
|
+
const setTabTitle = () => {
|
|
311
|
+
const current = shell.currentWidget;
|
|
312
|
+
if (current instanceof ConsolePanel) {
|
|
313
|
+
const update = () => {
|
|
314
|
+
const title = current.sessionContext.path || current.sessionContext.name;
|
|
315
|
+
const basename = PathExt.basename(title);
|
|
316
|
+
// Strip the ".ipynb" suffix from filenames for display in tab titles.
|
|
317
|
+
document.title = basename.replace(STRIP_IPYNB, '');
|
|
318
|
+
};
|
|
319
|
+
current.sessionContext.sessionChanged.connect(update);
|
|
320
|
+
update();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
else if (current instanceof DocumentWidget) {
|
|
324
|
+
const update = () => {
|
|
325
|
+
const basename = PathExt.basename(current.context.path);
|
|
326
|
+
document.title = basename.replace(STRIP_IPYNB, '');
|
|
327
|
+
};
|
|
328
|
+
current.context.pathChanged.connect(update);
|
|
329
|
+
update();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
shell.currentChanged.connect(setTabTitle);
|
|
333
|
+
setTabTitle();
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* A plugin to display and rename the title of a file
|
|
338
|
+
*/
|
|
339
|
+
const title = {
|
|
340
|
+
id: '@jupyter-notebook/application-extension:title',
|
|
341
|
+
autoStart: true,
|
|
342
|
+
requires: [INotebookShell, ITranslator],
|
|
343
|
+
optional: [IDocumentManager, IRouter],
|
|
344
|
+
activate: (app, shell, translator, docManager, router) => {
|
|
345
|
+
const { commands } = app;
|
|
346
|
+
const trans = translator.load('notebook');
|
|
347
|
+
const widget = new Widget();
|
|
348
|
+
widget.id = 'jp-title';
|
|
349
|
+
app.shell.add(widget, 'top', { rank: 10 });
|
|
350
|
+
const addTitle = async () => {
|
|
351
|
+
const current = shell.currentWidget;
|
|
352
|
+
if (!current || !(current instanceof DocumentWidget)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (widget.node.children.length > 0) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const h = document.createElement('h1');
|
|
359
|
+
h.textContent = current.title.label.replace(STRIP_IPYNB, '');
|
|
360
|
+
widget.node.appendChild(h);
|
|
361
|
+
widget.node.style.marginLeft = '10px';
|
|
362
|
+
if (!docManager) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const isEnabled = () => {
|
|
366
|
+
const { currentWidget } = shell;
|
|
367
|
+
return !!(currentWidget && docManager.contextForWidget(currentWidget));
|
|
368
|
+
};
|
|
369
|
+
commands.addCommand(CommandIDs.rename, {
|
|
370
|
+
label: () => trans.__('Rename…'),
|
|
371
|
+
isEnabled,
|
|
372
|
+
execute: async () => {
|
|
373
|
+
var _a, _b;
|
|
374
|
+
if (!isEnabled()) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const result = await renameDialog(docManager, current.context.path);
|
|
378
|
+
// activate the current widget to bring the focus
|
|
379
|
+
if (current) {
|
|
380
|
+
current.activate();
|
|
381
|
+
}
|
|
382
|
+
if (result === null) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const newPath = (_a = current.context.path) !== null && _a !== void 0 ? _a : result.path;
|
|
386
|
+
const basename = PathExt.basename(newPath);
|
|
387
|
+
h.textContent = basename.replace(STRIP_IPYNB, '');
|
|
388
|
+
if (!router) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const matches = (_b = router.current.path.match(TREE_PATTERN)) !== null && _b !== void 0 ? _b : [];
|
|
392
|
+
const [, route, path] = matches;
|
|
393
|
+
if (!route || !path) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const encoded = encodeURIComponent(newPath);
|
|
397
|
+
router.navigate(`/${route}/${encoded}`, {
|
|
398
|
+
skipRouting: true
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
widget.node.onclick = async () => {
|
|
403
|
+
void commands.execute(CommandIDs.rename);
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
shell.currentChanged.connect(addTitle);
|
|
407
|
+
void addTitle();
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* Plugin to toggle the top header visibility.
|
|
412
|
+
*/
|
|
413
|
+
const topVisibility = {
|
|
414
|
+
id: '@jupyter-notebook/application-extension:top',
|
|
415
|
+
requires: [INotebookShell, ITranslator],
|
|
416
|
+
optional: [IMainMenu, ISettingRegistry],
|
|
417
|
+
activate: (app, notebookShell, translator, menu, settingRegistry) => {
|
|
418
|
+
const trans = translator.load('notebook');
|
|
419
|
+
const top = notebookShell.top;
|
|
420
|
+
const pluginId = topVisibility.id;
|
|
421
|
+
app.commands.addCommand(CommandIDs.toggleTop, {
|
|
422
|
+
label: trans.__('Show Header'),
|
|
423
|
+
execute: () => {
|
|
424
|
+
top.setHidden(top.isVisible);
|
|
425
|
+
if (settingRegistry) {
|
|
426
|
+
void settingRegistry.set(pluginId, 'visible', top.isVisible);
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
isToggled: () => top.isVisible
|
|
430
|
+
});
|
|
431
|
+
if (menu) {
|
|
432
|
+
menu.viewMenu.addGroup([{ command: CommandIDs.toggleTop }], 2);
|
|
433
|
+
}
|
|
434
|
+
let settingsOverride = false;
|
|
435
|
+
if (settingRegistry) {
|
|
436
|
+
const loadSettings = settingRegistry.load(pluginId);
|
|
437
|
+
const updateSettings = (settings) => {
|
|
438
|
+
const visible = settings.get('visible').composite;
|
|
439
|
+
if (settings.user.visible !== undefined) {
|
|
440
|
+
settingsOverride = true;
|
|
441
|
+
top.setHidden(!visible);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
Promise.all([loadSettings, app.restored])
|
|
445
|
+
.then(([settings]) => {
|
|
446
|
+
updateSettings(settings);
|
|
447
|
+
settings.changed.connect(settings => {
|
|
448
|
+
updateSettings(settings);
|
|
449
|
+
});
|
|
450
|
+
})
|
|
451
|
+
.catch((reason) => {
|
|
452
|
+
console.error(reason.message);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
const onChanged = () => {
|
|
456
|
+
if (settingsOverride) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (app.format === 'desktop') {
|
|
460
|
+
notebookShell.expandTop();
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
notebookShell.collapseTop();
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
// listen on format change (mobile and desktop) to make the view more compact
|
|
467
|
+
app.formatChanged.connect(onChanged);
|
|
468
|
+
},
|
|
469
|
+
autoStart: true
|
|
470
|
+
};
|
|
471
|
+
/**
|
|
472
|
+
* The default tree route resolver plugin.
|
|
473
|
+
*/
|
|
474
|
+
const tree = {
|
|
475
|
+
id: '@jupyter-notebook/application-extension:tree-resolver',
|
|
476
|
+
autoStart: true,
|
|
477
|
+
requires: [IRouter],
|
|
478
|
+
provides: JupyterFrontEnd.ITreeResolver,
|
|
479
|
+
activate: (app, router) => {
|
|
480
|
+
const { commands } = app;
|
|
481
|
+
const set = new DisposableSet();
|
|
482
|
+
const delegate = new PromiseDelegate();
|
|
483
|
+
const treePattern = new RegExp('/(/tree/.*)?');
|
|
484
|
+
set.add(commands.addCommand(CommandIDs.resolveTree, {
|
|
485
|
+
execute: (async (args) => {
|
|
486
|
+
var _a;
|
|
487
|
+
if (set.isDisposed) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const query = URLExt.queryStringToObject((_a = args.search) !== null && _a !== void 0 ? _a : '');
|
|
491
|
+
const browser = query['file-browser-path'] || '';
|
|
492
|
+
// Remove the file browser path from the query string.
|
|
493
|
+
delete query['file-browser-path'];
|
|
494
|
+
// Clean up artifacts immediately upon routing.
|
|
495
|
+
set.dispose();
|
|
496
|
+
delegate.resolve({ browser, file: PageConfig.getOption('treePath') });
|
|
497
|
+
})
|
|
498
|
+
}));
|
|
499
|
+
set.add(router.register({ command: CommandIDs.resolveTree, pattern: treePattern }));
|
|
500
|
+
// If a route is handled by the router without the tree command being
|
|
501
|
+
// invoked, resolve to `null` and clean up artifacts.
|
|
502
|
+
const listener = () => {
|
|
503
|
+
if (set.isDisposed) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
set.dispose();
|
|
507
|
+
delegate.resolve(null);
|
|
508
|
+
};
|
|
509
|
+
router.routed.connect(listener);
|
|
510
|
+
set.add(new DisposableDelegate(() => {
|
|
511
|
+
router.routed.disconnect(listener);
|
|
512
|
+
}));
|
|
513
|
+
return { paths: delegate.promise };
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
const treePathUpdater = {
|
|
517
|
+
id: '@jupyter-notebook/application-extension:tree-updater',
|
|
518
|
+
requires: [IRouter],
|
|
519
|
+
provides: ITreePathUpdater,
|
|
520
|
+
activate: (app, router) => {
|
|
521
|
+
function updateTreePath(treePath) {
|
|
522
|
+
if (treePath !== PageConfig.getOption('treePath')) {
|
|
523
|
+
const path = URLExt.join(PageConfig.getOption('baseUrl') || '/', 'tree', URLExt.encodeParts(treePath));
|
|
524
|
+
router.navigate(path, { skipRouting: true });
|
|
525
|
+
// Persist the new tree path to PageConfig as it is used elsewhere at runtime.
|
|
526
|
+
PageConfig.setOption('treePath', treePath);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return updateTreePath;
|
|
530
|
+
},
|
|
531
|
+
autoStart: true
|
|
532
|
+
};
|
|
533
|
+
/**
|
|
534
|
+
* Zen mode plugin
|
|
535
|
+
*/
|
|
536
|
+
const zen = {
|
|
537
|
+
id: '@jupyter-notebook/application-extension:zen',
|
|
538
|
+
autoStart: true,
|
|
539
|
+
requires: [ITranslator],
|
|
540
|
+
optional: [ICommandPalette, INotebookShell, IMainMenu],
|
|
541
|
+
activate: (app, translator, palette, notebookShell, menu) => {
|
|
542
|
+
const { commands } = app;
|
|
543
|
+
const elem = document.documentElement;
|
|
544
|
+
const trans = translator.load('notebook');
|
|
545
|
+
const toggleOn = () => {
|
|
546
|
+
notebookShell === null || notebookShell === void 0 ? void 0 : notebookShell.collapseTop();
|
|
547
|
+
notebookShell === null || notebookShell === void 0 ? void 0 : notebookShell.menu.setHidden(true);
|
|
548
|
+
zenModeEnabled = true;
|
|
549
|
+
};
|
|
550
|
+
const toggleOff = () => {
|
|
551
|
+
notebookShell === null || notebookShell === void 0 ? void 0 : notebookShell.expandTop();
|
|
552
|
+
notebookShell === null || notebookShell === void 0 ? void 0 : notebookShell.menu.setHidden(false);
|
|
553
|
+
zenModeEnabled = false;
|
|
554
|
+
};
|
|
555
|
+
let zenModeEnabled = false;
|
|
556
|
+
commands.addCommand(CommandIDs.toggleZen, {
|
|
557
|
+
label: trans.__('Toggle Zen Mode'),
|
|
558
|
+
execute: () => {
|
|
559
|
+
if (!zenModeEnabled) {
|
|
560
|
+
elem.requestFullscreen();
|
|
561
|
+
toggleOn();
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
document.exitFullscreen();
|
|
565
|
+
toggleOff();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
document.addEventListener('fullscreenchange', () => {
|
|
570
|
+
if (!document.fullscreenElement) {
|
|
571
|
+
toggleOff();
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
if (palette) {
|
|
575
|
+
palette.addItem({ command: CommandIDs.toggleZen, category: 'Mode' });
|
|
576
|
+
}
|
|
577
|
+
if (menu) {
|
|
578
|
+
menu.viewMenu.addGroup([{ command: CommandIDs.toggleZen }], 3);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
/**
|
|
583
|
+
* Export the plugins as default.
|
|
584
|
+
*/
|
|
585
|
+
const plugins = [
|
|
586
|
+
dirty,
|
|
587
|
+
logo,
|
|
588
|
+
menus,
|
|
589
|
+
opener,
|
|
590
|
+
pages,
|
|
591
|
+
paths,
|
|
592
|
+
router,
|
|
593
|
+
sessionDialogs,
|
|
594
|
+
shell,
|
|
595
|
+
spacer,
|
|
596
|
+
status,
|
|
597
|
+
tabTitle,
|
|
598
|
+
title,
|
|
599
|
+
topVisibility,
|
|
600
|
+
tree,
|
|
601
|
+
treePathUpdater,
|
|
602
|
+
zen
|
|
603
|
+
];
|
|
604
|
+
export default plugins;
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jupyter-notebook/application-extension",
|
|
3
|
+
"version": "7.0.0-alpha.1",
|
|
4
|
+
"description": "Jupyter Notebook - Application Extension",
|
|
5
|
+
"homepage": "https://github.com/jupyter/notebook",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/jupyter/notebook/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/jupyter/notebook.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "BSD-3-Clause",
|
|
14
|
+
"author": "Project Jupyter",
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"style/**/*.css",
|
|
17
|
+
"style/index.js"
|
|
18
|
+
],
|
|
19
|
+
"main": "lib/index.js",
|
|
20
|
+
"types": "lib/index.d.ts",
|
|
21
|
+
"style": "style/index.css",
|
|
22
|
+
"directories": {
|
|
23
|
+
"lib": "lib/"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"lib/*.d.ts",
|
|
27
|
+
"lib/*.js.map",
|
|
28
|
+
"lib/*.js",
|
|
29
|
+
"schema/*.json",
|
|
30
|
+
"style/**/*.css",
|
|
31
|
+
"style/index.js"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -b",
|
|
35
|
+
"build:prod": "tsc -b",
|
|
36
|
+
"clean": "rimraf lib && rimraf tsconfig.tsbuildinfo",
|
|
37
|
+
"docs": "typedoc src",
|
|
38
|
+
"prepublishOnly": "npm run build",
|
|
39
|
+
"watch": "tsc -b --watch"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@jupyter-notebook/application": "^7.0.0-alpha.1",
|
|
43
|
+
"@jupyter-notebook/ui-components": "^7.0.0-alpha.1",
|
|
44
|
+
"@jupyterlab/application": "^4.0.0-alpha.5",
|
|
45
|
+
"@jupyterlab/apputils": "^4.0.0-alpha.5",
|
|
46
|
+
"@jupyterlab/celltags": "^4.0.0-alpha.5",
|
|
47
|
+
"@jupyterlab/codeeditor": "^4.0.0-alpha.5",
|
|
48
|
+
"@jupyterlab/codemirror": "^4.0.0-alpha.5",
|
|
49
|
+
"@jupyterlab/console": "^4.0.0-alpha.5",
|
|
50
|
+
"@jupyterlab/coreutils": "^6.0.0-alpha.5",
|
|
51
|
+
"@jupyterlab/docmanager": "^4.0.0-alpha.5",
|
|
52
|
+
"@jupyterlab/docregistry": "^4.0.0-alpha.5",
|
|
53
|
+
"@jupyterlab/mainmenu": "^4.0.0-alpha.5",
|
|
54
|
+
"@jupyterlab/settingregistry": "^4.0.0-alpha.5",
|
|
55
|
+
"@jupyterlab/translation": "^4.0.0-alpha.4",
|
|
56
|
+
"@lumino/coreutils": "^1.12.0",
|
|
57
|
+
"@lumino/disposable": "^1.10.1",
|
|
58
|
+
"@lumino/widgets": "^1.31.1"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"rimraf": "~3.0.0",
|
|
62
|
+
"typescript": "~4.1.3"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
67
|
+
"jupyterlab": {
|
|
68
|
+
"extension": true,
|
|
69
|
+
"schemaDir": "schema"
|
|
70
|
+
},
|
|
71
|
+
"styleModule": "style/index.js"
|
|
72
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Jupyter Notebook Menu Entries",
|
|
3
|
+
"description": "Jupyter Notebook Menu Entries",
|
|
4
|
+
"jupyter.lab.menus": {
|
|
5
|
+
"main": [
|
|
6
|
+
{
|
|
7
|
+
"id": "jp-mainmenu-file",
|
|
8
|
+
"items": [
|
|
9
|
+
{
|
|
10
|
+
"command": "application:rename",
|
|
11
|
+
"rank": 4
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"command": "notebook:trust",
|
|
15
|
+
"rank": 20
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"properties": {},
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"type": "object"
|
|
24
|
+
}
|
package/schema/top.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jupyter.lab.setting-icon": "notebook-ui-components:jupyter",
|
|
3
|
+
"jupyter.lab.setting-icon-label": "Jupyter Notebook Top Area",
|
|
4
|
+
"title": "Jupyter Notebook Top Area",
|
|
5
|
+
"description": "Jupyter Notebook Top Area settings",
|
|
6
|
+
"properties": {
|
|
7
|
+
"visible": {
|
|
8
|
+
"type": "boolean",
|
|
9
|
+
"title": "Top Bar Visibility",
|
|
10
|
+
"description": "Whether to show the top bar or not",
|
|
11
|
+
"default": true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"type": "object"
|
|
16
|
+
}
|
package/style/base.css
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*-----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
|
|
|
4
|
+
| Distributed under the terms of the Modified BSD License.
|
|
5
|
+
|----------------------------------------------------------------------------*/
|
|
6
|
+
|
|
7
|
+
.jp-NotebookSpacer {
|
|
8
|
+
flex-grow: 1;
|
|
9
|
+
flex-shrink: 1;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.jp-MainAreaWidget {
|
|
13
|
+
height: 100%;
|
|
14
|
+
}
|
package/style/index.css
ADDED
package/style/index.js
ADDED