UNPKG

8.35 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { Dialog, Printing, showDialog } from '@jupyterlab/apputils';
4import { isMarkdownCellModel } from '@jupyterlab/cells';
5import { PageConfig } from '@jupyterlab/coreutils';
6import { DocumentWidget } from '@jupyterlab/docregistry';
7import { nullTranslator } from '@jupyterlab/translation';
8import { each } from '@lumino/algorithm';
9import { Token } from '@lumino/coreutils';
10import { Notebook } from './widget';
11/**
12 * The class name added to notebook panels.
13 */
14const NOTEBOOK_PANEL_CLASS = 'jp-NotebookPanel';
15const NOTEBOOK_PANEL_TOOLBAR_CLASS = 'jp-NotebookPanel-toolbar';
16const NOTEBOOK_PANEL_NOTEBOOK_CLASS = 'jp-NotebookPanel-notebook';
17/**
18 * The class name to add when the document is loaded for the search box.
19 */
20const SEARCH_DOCUMENT_LOADED_CLASS = 'jp-DocumentSearch-document-loaded';
21/**
22 * A widget that hosts a notebook toolbar and content area.
23 *
24 * #### Notes
25 * The widget keeps the document metadata in sync with the current
26 * kernel on the context.
27 */
28export class NotebookPanel extends DocumentWidget {
29 /**
30 * Construct a new notebook panel.
31 */
32 constructor(options) {
33 super(options);
34 /**
35 * Whether we are currently in a series of autorestarts we have already
36 * notified the user about.
37 */
38 this._autorestarting = false;
39 this.translator = options.translator || nullTranslator;
40 this._trans = this.translator.load('jupyterlab');
41 // Set up CSS classes
42 this.addClass(NOTEBOOK_PANEL_CLASS);
43 this.toolbar.addClass(NOTEBOOK_PANEL_TOOLBAR_CLASS);
44 this.content.addClass(NOTEBOOK_PANEL_NOTEBOOK_CLASS);
45 // Set up things related to the context
46 this.content.model = this.context.model;
47 this.context.sessionContext.kernelChanged.connect(this._onKernelChanged, this);
48 this.context.sessionContext.statusChanged.connect(this._onSessionStatusChanged, this);
49 this.content.fullyRendered.connect(this._onFullyRendered, this);
50 this.context.saveState.connect(this._onSave, this);
51 void this.revealed.then(() => {
52 if (this.isDisposed) {
53 // this widget has already been disposed, bail
54 return;
55 }
56 // Set the document edit mode on initial open if it looks like a new document.
57 if (this.content.widgets.length === 1) {
58 const cellModel = this.content.widgets[0].model;
59 if (cellModel.type === 'code' && cellModel.value.text === '') {
60 this.content.mode = 'edit';
61 }
62 }
63 });
64 }
65 _onSave(sender, state) {
66 if (state === 'started' && this.model) {
67 // Find markdown cells
68 const { cells } = this.model;
69 each(cells, cell => {
70 if (isMarkdownCellModel(cell)) {
71 for (const key of cell.attachments.keys) {
72 if (!cell.value.text.includes(key)) {
73 cell.attachments.remove(key);
74 }
75 }
76 }
77 });
78 }
79 }
80 /**
81 * The session context used by the panel.
82 */
83 get sessionContext() {
84 return this.context.sessionContext;
85 }
86 /**
87 * The model for the widget.
88 */
89 get model() {
90 return this.content.model;
91 }
92 /**
93 * Update the options for the current notebook panel.
94 *
95 * @param config new options to set
96 */
97 setConfig(config) {
98 this.content.editorConfig = config.editorConfig;
99 this.content.notebookConfig = config.notebookConfig;
100 // Update kernel shutdown behavior
101 const kernelPreference = this.context.sessionContext.kernelPreference;
102 this.context.sessionContext.kernelPreference = Object.assign(Object.assign({}, kernelPreference), { shutdownOnDispose: config.kernelShutdown });
103 }
104 /**
105 * Set URI fragment identifier.
106 */
107 setFragment(fragment) {
108 void this.context.ready.then(() => {
109 this.content.setFragment(fragment);
110 });
111 }
112 /**
113 * Dispose of the resources used by the widget.
114 */
115 dispose() {
116 this.content.dispose();
117 super.dispose();
118 }
119 /**
120 * Prints the notebook by converting to HTML with nbconvert.
121 */
122 [Printing.symbol]() {
123 return async () => {
124 // Save before generating HTML
125 if (this.context.model.dirty && !this.context.model.readOnly) {
126 await this.context.save();
127 }
128 await Printing.printURL(PageConfig.getNBConvertURL({
129 format: 'html',
130 download: false,
131 path: this.context.path
132 }));
133 };
134 }
135 /**
136 * Handle a fully rendered signal notebook.
137 */
138 _onFullyRendered(notebook, fullyRendered) {
139 fullyRendered
140 ? this.removeClass(SEARCH_DOCUMENT_LOADED_CLASS)
141 : this.addClass(SEARCH_DOCUMENT_LOADED_CLASS);
142 }
143 /**
144 * Handle a change in the kernel by updating the document metadata.
145 */
146 _onKernelChanged(sender, args) {
147 if (!this.model || !args.newValue) {
148 return;
149 }
150 const { newValue } = args;
151 void newValue.info.then(info => {
152 var _a;
153 if (this.model &&
154 ((_a = this.context.sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel) === newValue) {
155 this._updateLanguage(info.language_info);
156 }
157 });
158 void this._updateSpec(newValue);
159 }
160 _onSessionStatusChanged(sender, status) {
161 var _a;
162 // If the status is autorestarting, and we aren't already in a series of
163 // autorestarts, show the dialog.
164 if (status === 'autorestarting' && !this._autorestarting) {
165 // The kernel died and the server is restarting it. We notify the user so
166 // they know why their kernel state is gone.
167 void showDialog({
168 title: this._trans.__('Kernel Restarting'),
169 body: this._trans.__('The kernel for %1 appears to have died. It will restart automatically.', (_a = this.sessionContext.session) === null || _a === void 0 ? void 0 : _a.path),
170 buttons: [Dialog.okButton({ label: this._trans.__('Ok') })]
171 });
172 this._autorestarting = true;
173 }
174 else if (status === 'restarting') {
175 // Another autorestart attempt will first change the status to
176 // restarting, then to autorestarting again, so we don't reset the
177 // autorestarting status if the status is 'restarting'.
178 /* no-op */
179 }
180 else {
181 this._autorestarting = false;
182 }
183 }
184 /**
185 * Update the kernel language.
186 */
187 _updateLanguage(language) {
188 this.model.metadata.set('language_info', language);
189 }
190 /**
191 * Update the kernel spec.
192 */
193 async _updateSpec(kernel) {
194 const spec = await kernel.spec;
195 if (this.isDisposed) {
196 return;
197 }
198 this.model.metadata.set('kernelspec', {
199 name: kernel.name,
200 display_name: spec === null || spec === void 0 ? void 0 : spec.display_name,
201 language: spec === null || spec === void 0 ? void 0 : spec.language
202 });
203 }
204}
205/**
206 * A namespace for `NotebookPanel` statics.
207 */
208(function (NotebookPanel) {
209 /**
210 * The default implementation of an `IContentFactory`.
211 */
212 class ContentFactory extends Notebook.ContentFactory {
213 /**
214 * Create a new content area for the panel.
215 */
216 createNotebook(options) {
217 return new Notebook(options);
218 }
219 }
220 NotebookPanel.ContentFactory = ContentFactory;
221 /**
222 * Default content factory for the notebook panel.
223 */
224 NotebookPanel.defaultContentFactory = new ContentFactory();
225 /* tslint:disable */
226 /**
227 * The notebook renderer token.
228 */
229 NotebookPanel.IContentFactory = new Token('@jupyterlab/notebook:IContentFactory');
230 /* tslint:enable */
231})(NotebookPanel || (NotebookPanel = {}));
232//# sourceMappingURL=panel.js.map
\No newline at end of file