UNPKG

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