@jupyter-notebook/notebook-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 ADDED
@@ -0,0 +1,6 @@
1
+ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ /**
3
+ * Export the plugins as default.
4
+ */
5
+ declare const plugins: JupyterFrontEndPlugin<any>[];
6
+ export default plugins;
package/lib/index.js ADDED
@@ -0,0 +1,294 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+ import { DOMUtils } from '@jupyterlab/apputils';
4
+ import { Text, Time } from '@jupyterlab/coreutils';
5
+ import { IDocumentManager } from '@jupyterlab/docmanager';
6
+ import { IMainMenu } from '@jupyterlab/mainmenu';
7
+ import { NotebookPanel, INotebookTracker } from '@jupyterlab/notebook';
8
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
9
+ import { ITranslator } from '@jupyterlab/translation';
10
+ import { INotebookShell } from '@jupyter-notebook/application';
11
+ import { Poll } from '@lumino/polling';
12
+ import { Menu, Widget } from '@lumino/widgets';
13
+ /**
14
+ * The class for kernel status errors.
15
+ */
16
+ const KERNEL_STATUS_ERROR_CLASS = 'jp-NotebookKernelStatus-error';
17
+ /**
18
+ * The class for kernel status warnings.
19
+ */
20
+ const KERNEL_STATUS_WARN_CLASS = 'jp-NotebookKernelStatus-warn';
21
+ /**
22
+ * The class for kernel status infos.
23
+ */
24
+ const KERNEL_STATUS_INFO_CLASS = 'jp-NotebookKernelStatus-info';
25
+ /**
26
+ * The class to fade out the kernel status.
27
+ */
28
+ const KERNEL_STATUS_FADE_OUT_CLASS = 'jp-NotebookKernelStatus-fade';
29
+ /**
30
+ * The class for scrolled outputs
31
+ */
32
+ const SCROLLED_OUTPUTS_CLASS = 'jp-mod-outputsScrolled';
33
+ /**
34
+ * A plugin for the checkpoint indicator
35
+ */
36
+ const checkpoints = {
37
+ id: '@jupyter-notebook/notebook-extension:checkpoints',
38
+ autoStart: true,
39
+ requires: [IDocumentManager, ITranslator],
40
+ optional: [INotebookShell],
41
+ activate: (app, docManager, translator, notebookShell) => {
42
+ const { shell } = app;
43
+ const trans = translator.load('notebook');
44
+ const widget = new Widget();
45
+ widget.id = DOMUtils.createDomID();
46
+ widget.addClass('jp-NotebookCheckpoint');
47
+ app.shell.add(widget, 'top', { rank: 100 });
48
+ const onChange = async () => {
49
+ const current = shell.currentWidget;
50
+ if (!current) {
51
+ return;
52
+ }
53
+ const context = docManager.contextForWidget(current);
54
+ context === null || context === void 0 ? void 0 : context.fileChanged.disconnect(onChange);
55
+ context === null || context === void 0 ? void 0 : context.fileChanged.connect(onChange);
56
+ const checkpoints = await (context === null || context === void 0 ? void 0 : context.listCheckpoints());
57
+ if (!checkpoints) {
58
+ return;
59
+ }
60
+ const checkpoint = checkpoints[checkpoints.length - 1];
61
+ widget.node.textContent = trans.__('Last Checkpoint: %1', Time.formatHuman(new Date(checkpoint.last_modified)));
62
+ };
63
+ if (notebookShell) {
64
+ notebookShell.currentChanged.connect(onChange);
65
+ }
66
+ new Poll({
67
+ auto: true,
68
+ factory: () => onChange(),
69
+ frequency: {
70
+ interval: 2000,
71
+ backoff: false
72
+ },
73
+ standby: 'when-hidden'
74
+ });
75
+ }
76
+ };
77
+ /**
78
+ * The kernel logo plugin.
79
+ */
80
+ const kernelLogo = {
81
+ id: '@jupyter-notebook/notebook-extension:kernel-logo',
82
+ autoStart: true,
83
+ requires: [INotebookShell],
84
+ activate: (app, shell) => {
85
+ const { serviceManager } = app;
86
+ let widget;
87
+ const onChange = async () => {
88
+ var _a, _b, _c, _d, _e;
89
+ if (widget) {
90
+ widget.dispose();
91
+ widget.parent = null;
92
+ }
93
+ const current = shell.currentWidget;
94
+ if (!(current instanceof NotebookPanel)) {
95
+ return;
96
+ }
97
+ await current.sessionContext.ready;
98
+ current.sessionContext.kernelChanged.disconnect(onChange);
99
+ current.sessionContext.kernelChanged.connect(onChange);
100
+ const name = (_c = (_b = (_a = current.sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : '';
101
+ const spec = (_e = (_d = serviceManager.kernelspecs) === null || _d === void 0 ? void 0 : _d.specs) === null || _e === void 0 ? void 0 : _e.kernelspecs[name];
102
+ if (!spec) {
103
+ return;
104
+ }
105
+ const kernelIconUrl = spec.resources['logo-64x64'];
106
+ if (!kernelIconUrl) {
107
+ return;
108
+ }
109
+ const node = document.createElement('div');
110
+ const img = document.createElement('img');
111
+ img.src = kernelIconUrl;
112
+ img.title = spec.display_name;
113
+ node.appendChild(img);
114
+ widget = new Widget({ node });
115
+ widget.addClass('jp-NotebookKernelLogo');
116
+ app.shell.add(widget, 'top', { rank: 10010 });
117
+ };
118
+ app.started.then(() => {
119
+ shell.currentChanged.connect(onChange);
120
+ });
121
+ }
122
+ };
123
+ /**
124
+ * A plugin to display the kernel status;
125
+ */
126
+ const kernelStatus = {
127
+ id: '@jupyter-notebook/notebook-extension:kernel-status',
128
+ autoStart: true,
129
+ requires: [INotebookShell, ITranslator],
130
+ activate: (app, shell, translator) => {
131
+ const trans = translator.load('notebook');
132
+ const widget = new Widget();
133
+ widget.addClass('jp-NotebookKernelStatus');
134
+ app.shell.add(widget, 'menu', { rank: 10010 });
135
+ const removeClasses = () => {
136
+ widget.removeClass(KERNEL_STATUS_ERROR_CLASS);
137
+ widget.removeClass(KERNEL_STATUS_WARN_CLASS);
138
+ widget.removeClass(KERNEL_STATUS_INFO_CLASS);
139
+ widget.removeClass(KERNEL_STATUS_FADE_OUT_CLASS);
140
+ };
141
+ const onStatusChanged = (sessionContext) => {
142
+ const status = sessionContext.kernelDisplayStatus;
143
+ let text = `Kernel ${Text.titleCase(status)}`;
144
+ removeClasses();
145
+ switch (status) {
146
+ case 'busy':
147
+ case 'idle':
148
+ text = '';
149
+ widget.addClass(KERNEL_STATUS_FADE_OUT_CLASS);
150
+ break;
151
+ case 'dead':
152
+ case 'terminating':
153
+ widget.addClass(KERNEL_STATUS_ERROR_CLASS);
154
+ break;
155
+ case 'unknown':
156
+ widget.addClass(KERNEL_STATUS_WARN_CLASS);
157
+ break;
158
+ default:
159
+ widget.addClass(KERNEL_STATUS_INFO_CLASS);
160
+ widget.addClass(KERNEL_STATUS_FADE_OUT_CLASS);
161
+ break;
162
+ }
163
+ widget.node.textContent = trans.__(text);
164
+ };
165
+ const onChange = async () => {
166
+ const current = shell.currentWidget;
167
+ if (!(current instanceof NotebookPanel)) {
168
+ return;
169
+ }
170
+ const sessionContext = current.sessionContext;
171
+ sessionContext.statusChanged.connect(onStatusChanged);
172
+ };
173
+ shell.currentChanged.connect(onChange);
174
+ }
175
+ };
176
+ /**
177
+ * A plugin to customize notebook related menu entries
178
+ * TODO: switch to settings define menus when fixed upstream: https://github.com/jupyterlab/jupyterlab/issues/11754
179
+ */
180
+ const menuPlugin = {
181
+ id: '@jupyter-notebook/notebook-extension:menu-plugin',
182
+ autoStart: true,
183
+ requires: [IMainMenu, ITranslator],
184
+ activate: (app, mainMenu, translator) => {
185
+ const { commands } = app;
186
+ const trans = translator.load('notebook');
187
+ const cellTypeSubmenu = new Menu({ commands });
188
+ cellTypeSubmenu.title.label = trans._p('menu', 'Cell Type');
189
+ [
190
+ 'notebook:change-cell-to-code',
191
+ 'notebook:change-cell-to-markdown',
192
+ 'notebook:change-cell-to-raw'
193
+ ].forEach(command => {
194
+ cellTypeSubmenu.addItem({
195
+ command
196
+ });
197
+ });
198
+ mainMenu.runMenu.addItem({ type: 'separator', rank: 1000 });
199
+ mainMenu.runMenu.addItem({
200
+ type: 'submenu',
201
+ submenu: cellTypeSubmenu,
202
+ rank: 1010
203
+ });
204
+ }
205
+ };
206
+ /**
207
+ * A plugin to enable scrolling for outputs by default.
208
+ * Mimic the logic from the classic notebook, as found here:
209
+ * https://github.com/jupyter/notebook/blob/a9a31c096eeffe1bff4e9164c6a0442e0e13cdb3/notebook/static/notebook/js/outputarea.js#L96-L120
210
+ */
211
+ const scrollOutput = {
212
+ id: '@jupyter-notebook/notebook-extension:scroll-output',
213
+ autoStart: true,
214
+ requires: [INotebookTracker],
215
+ optional: [ISettingRegistry],
216
+ activate: async (app, tracker, settingRegistry) => {
217
+ const autoScrollThreshold = 100;
218
+ let autoScrollOutputs = true;
219
+ // decide whether to scroll the output of the cell based on some heuristics
220
+ const autoScroll = (cell) => {
221
+ if (!autoScrollOutputs) {
222
+ // bail if disabled via the settings
223
+ return;
224
+ }
225
+ const { outputArea } = cell;
226
+ // respect cells with an explicit scrolled state
227
+ const scrolled = cell.model.metadata.get('scrolled');
228
+ if (scrolled !== undefined) {
229
+ return;
230
+ }
231
+ const { node } = outputArea;
232
+ const height = node.scrollHeight;
233
+ const fontSize = parseFloat(node.style.fontSize.replace('px', ''));
234
+ const lineHeight = (fontSize || 14) * 1.3;
235
+ // do not set via cell.outputScrolled = true, as this would
236
+ // otherwise synchronize the scrolled state to the notebook metadata
237
+ const scroll = height > lineHeight * autoScrollThreshold;
238
+ cell.toggleClass(SCROLLED_OUTPUTS_CLASS, scroll);
239
+ };
240
+ tracker.widgetAdded.connect((sender, notebook) => {
241
+ var _a;
242
+ (_a = notebook.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect((sender, changed) => {
243
+ // process new cells only
244
+ if (!(changed.type === 'add')) {
245
+ return;
246
+ }
247
+ const [cellModel] = changed.newValues;
248
+ notebook.content.widgets.forEach(cell => {
249
+ if (cell.model.id === cellModel.id && cell.model.type === 'code') {
250
+ const codeCell = cell;
251
+ codeCell.outputArea.model.changed.connect(() => autoScroll(codeCell));
252
+ }
253
+ });
254
+ });
255
+ // when the notebook widget is created, process all the cells
256
+ // TODO: investigate why notebook.content.fullyRendered is not enough
257
+ notebook.sessionContext.ready.then(() => {
258
+ notebook.content.widgets.forEach(cell => {
259
+ if (cell.model.type === 'code') {
260
+ autoScroll(cell);
261
+ }
262
+ });
263
+ });
264
+ });
265
+ if (settingRegistry) {
266
+ const loadSettings = settingRegistry.load(scrollOutput.id);
267
+ const updateSettings = (settings) => {
268
+ autoScrollOutputs = settings.get('autoScrollOutputs')
269
+ .composite;
270
+ };
271
+ Promise.all([loadSettings, app.restored])
272
+ .then(([settings]) => {
273
+ updateSettings(settings);
274
+ settings.changed.connect(settings => {
275
+ updateSettings(settings);
276
+ });
277
+ })
278
+ .catch((reason) => {
279
+ console.error(reason.message);
280
+ });
281
+ }
282
+ }
283
+ };
284
+ /**
285
+ * Export the plugins as default.
286
+ */
287
+ const plugins = [
288
+ checkpoints,
289
+ kernelLogo,
290
+ kernelStatus,
291
+ menuPlugin,
292
+ scrollOutput
293
+ ];
294
+ export default plugins;
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@jupyter-notebook/notebook-extension",
3
+ "version": "7.0.0-alpha.1",
4
+ "description": "Jupyter Notebook - Notebook 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
+ "@jupyterlab/application": "^4.0.0-alpha.5",
44
+ "@jupyterlab/apputils": "^4.0.0-alpha.5",
45
+ "@jupyterlab/cells": "^4.0.0-alpha.5",
46
+ "@jupyterlab/docmanager": "^4.0.0-alpha.5",
47
+ "@jupyterlab/notebook": "^4.0.0-alpha.5",
48
+ "@jupyterlab/settingregistry": "^4.0.0-alpha.5",
49
+ "@jupyterlab/translation": "^4.0.0-alpha.4",
50
+ "@lumino/polling": "^1.10.0",
51
+ "@lumino/widgets": "^1.31.1"
52
+ },
53
+ "devDependencies": {
54
+ "rimraf": "~3.0.0",
55
+ "typescript": "~4.1.3"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "jupyterlab": {
61
+ "extension": true,
62
+ "schemaDir": "schema"
63
+ },
64
+ "styleModule": "style/index.js"
65
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "jupyter.lab.setting-icon": "notebook-ui-components:jupyter",
3
+ "jupyter.lab.setting-icon-label": "Jupyter Notebook Notebook",
4
+ "title": "Jupyter Notebook Notebook",
5
+ "description": "Jupyter Notebook Notebook settings",
6
+ "properties": {
7
+ "autoScrollOutputs": {
8
+ "type": "boolean",
9
+ "title": "Auto Scroll Outputs",
10
+ "description": "Whether to auto scroll the output area when the outputs become too long",
11
+ "default": true
12
+ }
13
+ },
14
+ "additionalProperties": false,
15
+ "type": "object"
16
+ }
package/style/base.css ADDED
@@ -0,0 +1,138 @@
1
+ /*-----------------------------------------------------------------------------
2
+ | Copyright (c) Jupyter Development Team.
3
+ |
4
+ | Distributed under the terms of the Modified BSD License.
5
+ |----------------------------------------------------------------------------*/
6
+
7
+ @import './variables.css';
8
+
9
+ /* Document oriented look for the notebook (scrollbar to the right of the page) */
10
+
11
+ body[data-notebook='notebooks'] .jp-NotebookPanel-toolbar {
12
+ padding-left: calc(calc(100% - var(--jp-notebook-max-width)) * 0.5);
13
+ padding-right: calc(calc(100% - var(--jp-notebook-max-width)) * 0.5);
14
+ }
15
+
16
+ body[data-notebook='notebooks'] .jp-Notebook > * {
17
+ background: var(--jp-layout-color0);
18
+ padding: var(--jp-notebook-padding);
19
+ }
20
+
21
+ body[data-notebook='notebooks']
22
+ .jp-Notebook.jp-mod-commandMode
23
+ .jp-Cell.jp-mod-active.jp-mod-selected:not(.jp-mod-multiSelected) {
24
+ background: var(--jp-layout-color0) !important;
25
+ }
26
+
27
+ body[data-notebook='notebooks'] .jp-Notebook > *:first-child {
28
+ padding-top: var(--jp-notebook-padding-offset);
29
+ margin-top: var(--jp-notebook-toolbar-margin-bottom);
30
+ }
31
+
32
+ body[data-notebook='notebooks'] .jp-Notebook {
33
+ padding-top: unset;
34
+ padding-bottom: unset;
35
+ padding-left: calc(calc(100% - var(--jp-notebook-max-width)) * 0.5);
36
+ padding-right: calc(
37
+ calc(
38
+ 100% - var(--jp-notebook-max-width) - var(--jp-notebook-padding-offset)
39
+ ) * 0.5
40
+ );
41
+ background: var(--jp-layout-color2);
42
+ }
43
+
44
+ body[data-notebook='notebooks'] .jp-Notebook.jp-mod-scrollPastEnd::after {
45
+ background: var(--jp-layout-color0);
46
+ }
47
+
48
+ /* ---- */
49
+
50
+ .jp-NotebookKernelLogo {
51
+ flex: 0 0 auto;
52
+ display: flex;
53
+ align-items: center;
54
+ text-align: center;
55
+ margin-right: 8px;
56
+ }
57
+
58
+ .jp-NotebookKernelLogo img {
59
+ max-width: 28px;
60
+ max-height: 28px;
61
+ display: flex;
62
+ }
63
+
64
+ .jp-NotebookKernelStatus {
65
+ margin: 0;
66
+ font-weight: normal;
67
+ font-size: var(--jp-ui-font-size1);
68
+ color: var(--jp-ui-font-color0);
69
+ font-family: var(--jp-ui-font-family);
70
+ line-height: var(--jp-private-title-panel-height);
71
+ padding-left: var(--jp-kernel-status-padding);
72
+ padding-right: var(--jp-kernel-status-padding);
73
+ }
74
+
75
+ .jp-NotebookKernelStatus-error {
76
+ background-color: var(--jp-error-color0);
77
+ }
78
+
79
+ .jp-NotebookKernelStatus-warn {
80
+ background-color: var(--jp-warn-color0);
81
+ }
82
+
83
+ .jp-NotebookKernelStatus-info {
84
+ background-color: var(--jp-info-color0);
85
+ }
86
+
87
+ .jp-NotebookKernelStatus-fade {
88
+ animation: 0.5s fade-out forwards;
89
+ }
90
+
91
+ @keyframes fade-out {
92
+ 0% {
93
+ opacity: 1;
94
+ }
95
+ 100% {
96
+ opacity: 0;
97
+ }
98
+ }
99
+
100
+ #jp-title h1 {
101
+ cursor: pointer;
102
+ font-size: 18px;
103
+ margin: 0;
104
+ font-weight: normal;
105
+ color: var(--jp-ui-font-color0);
106
+ font-family: var(--jp-ui-font-family);
107
+ line-height: calc(1.5 * var(--jp-private-title-panel-height));
108
+ text-overflow: ellipsis;
109
+ overflow: hidden;
110
+ white-space: nowrap;
111
+ }
112
+
113
+ #jp-title h1:hover {
114
+ background: var(--jp-layout-color2);
115
+ }
116
+
117
+ .jp-NotebookCheckpoint {
118
+ font-size: 14px;
119
+ margin-left: 5px;
120
+ margin-right: 5px;
121
+ font-weight: normal;
122
+ color: var(--jp-ui-font-color0);
123
+ font-family: var(--jp-ui-font-family);
124
+ line-height: calc(1.5 * var(--jp-private-title-panel-height));
125
+ text-overflow: ellipsis;
126
+ overflow: hidden;
127
+ white-space: nowrap;
128
+ }
129
+
130
+ /* Mobile View */
131
+
132
+ body[data-format='mobile'] .jp-NotebookCheckpoint {
133
+ display: none;
134
+ }
135
+
136
+ body[data-format='mobile'] .jp-Notebook > *:first-child {
137
+ margin-top: 0;
138
+ }
@@ -0,0 +1 @@
1
+ @import url('./base.css');
package/style/index.js ADDED
@@ -0,0 +1 @@
1
+ import './base.css';
@@ -0,0 +1,6 @@
1
+ :root {
2
+ --jp-notebook-toolbar-margin-bottom: 20px;
3
+ --jp-notebook-padding-offset: 20px;
4
+
5
+ --jp-kernel-status-padding: 5px;
6
+ }