Skip to content

Commit 7fe19d4

Browse files
committed
Fix toolbar, save interaction, block markdown cell unrendering
1 parent 44c7baa commit 7fe19d4

File tree

4 files changed

+60
-7
lines changed

4 files changed

+60
-7
lines changed

schema/plugin.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"title": "jupytereverywhere",
44
"description": "jupytereverywhere settings.",
55
"type": "object",
6-
"properties": {},
76
"additionalProperties": false,
87
"jupyter.lab.toolbars": {
98
"ViewOnlyNotebook": [
@@ -158,5 +157,13 @@
158157
"disabled": true
159158
}
160159
]
160+
},
161+
"jupyter.lab.transform": true,
162+
"properties": {
163+
"toolbar": {
164+
"title": "View-only notebook panel toolbar items",
165+
"type": "array",
166+
"default": []
167+
}
161168
}
162169
}

src/pages/notebook.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ export const notebookPlugin: JupyterFrontEndPlugin<void> = {
4747

4848
const { content }: { content: INotebookContent } = notebookResponse;
4949

50+
// We make all cells read-only by setting editable: false.
51+
// This is still required with a custom widget factory as
52+
// it is not trivial to coerce the cells to respect the `readOnly`
53+
// property otherwise (Mike tried swapping `Notebook.ContentFactory`
54+
// and it does not work without further hacks).
5055
if (content.cells) {
5156
content.cells.forEach(cell => {
5257
cell.metadata = {
@@ -70,7 +75,12 @@ export const notebookPlugin: JupyterFrontEndPlugin<void> = {
7075
await contents.save(filename, {
7176
content,
7277
format: 'json',
73-
type: 'notebook'
78+
type: 'notebook',
79+
// Even though we have a custom view-only factory, we still
80+
// want to indicate that notebook is read-only to avoid
81+
// error on Ctrl + S and instead get a nice notification that
82+
// the notebook cannot be saved unless using save-as.
83+
writable: false
7484
});
7585

7686
await commands.execute('docmanager:open', {

src/view-only.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application';
22
import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
33
import { WidgetTracker, IWidgetTracker } from '@jupyterlab/apputils';
4+
import { ReactiveToolbar, Toolbar } from '@jupyterlab/ui-components';
45
import { IEditorServices } from '@jupyterlab/codeeditor';
56
import { ABCWidgetFactory, DocumentRegistry, DocumentWidget } from '@jupyterlab/docregistry';
67
import { ISettingRegistry } from '@jupyterlab/settingregistry';
78
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
89
import { ITranslator } from '@jupyterlab/translation';
9-
import { INotebookModel, NotebookPanel, Notebook, StaticNotebook } from '@jupyterlab/notebook';
10+
import { INotebookModel, Notebook, StaticNotebook } from '@jupyterlab/notebook';
1011
import { createToolbarFactory, IToolbarWidgetRegistry } from '@jupyterlab/apputils';
1112
import { Widget } from '@lumino/widgets';
1213
import { Token } from '@lumino/coreutils';
14+
import { MarkdownCell } from '@jupyterlab/cells';
1315

1416
export const VIEW_ONLY_NOTEBOOK_FACTORY = 'ViewOnlyNotebook';
1517

@@ -39,6 +41,27 @@ class ViewOnlyHeader extends Widget {
3941
}
4042
}
4143

44+
namespace FilteredToolbar {
45+
export interface IOptions extends Toolbar.IOptions {
46+
itemsToFilterOut: Set<string>;
47+
}
48+
}
49+
50+
class FilteredToolbar extends ReactiveToolbar {
51+
constructor(options: FilteredToolbar.IOptions) {
52+
super(options);
53+
this._itemsToFilterOut = options.itemsToFilterOut;
54+
}
55+
insertItem(index: number, name: string, widget: Widget): boolean {
56+
if (this._itemsToFilterOut?.has(name)) {
57+
return false;
58+
}
59+
return super.insertItem(index, name, widget);
60+
}
61+
// This can be undefined during the super() call in constructor
62+
private _itemsToFilterOut: Set<string> | undefined;
63+
}
64+
4265
class ViewOnlyNotebook extends StaticNotebook {
4366
// Add any customization for view-only notebook here if needed
4467
}
@@ -48,7 +71,12 @@ class ViewOnlyNotebookPanel extends DocumentWidget<ViewOnlyNotebook, INotebookMo
4871
* Construct a new view-only notebook panel.
4972
*/
5073
constructor(options: DocumentWidget.IOptions<ViewOnlyNotebook, INotebookModel>) {
51-
super(options);
74+
super({
75+
...options,
76+
toolbar: new FilteredToolbar({
77+
itemsToFilterOut: new Set(['read-only-indicator'])
78+
})
79+
});
5280

5381
this.addClass(NOTEBOOK_PANEL_CLASS);
5482
this.toolbar.addClass(NOTEBOOK_PANEL_TOOLBAR_CLASS);
@@ -114,10 +142,17 @@ class ViewOnlyNotebookWidgetFactory extends ABCWidgetFactory<
114142
}
115143
}
116144

145+
class ViewOnlyContentFactory extends Notebook.ContentFactory {
146+
createMarkdownCell(options: MarkdownCell.IOptions): MarkdownCell {
147+
const cell = super.createMarkdownCell(options);
148+
cell.showEditorForReadOnly = false;
149+
return cell;
150+
}
151+
}
152+
117153
export const viewOnlyNotebookFactoryPlugin: JupyterFrontEndPlugin<IViewOnlyNotebookTracker> = {
118154
id: 'jupytereverywhere:view-only-notebook',
119155
requires: [
120-
NotebookPanel.IContentFactory,
121156
IEditorServices,
122157
IRenderMimeRegistry,
123158
IToolbarWidgetRegistry,
@@ -128,13 +163,13 @@ export const viewOnlyNotebookFactoryPlugin: JupyterFrontEndPlugin<IViewOnlyNoteb
128163
autoStart: true,
129164
activate: (
130165
app: JupyterFrontEnd,
131-
contentFactory: NotebookPanel.IContentFactory,
132166
editorServices: IEditorServices,
133167
rendermime: IRenderMimeRegistry,
134168
toolbarRegistry: IToolbarWidgetRegistry,
135169
settingRegistry: ISettingRegistry,
136170
translator: ITranslator
137171
) => {
172+
// This needs to have a `toolbar` property with an array
138173
const PANEL_SETTINGS = 'jupytereverywhere:plugin';
139174

140175
const toolbarFactory = createToolbarFactory(
@@ -146,6 +181,7 @@ export const viewOnlyNotebookFactoryPlugin: JupyterFrontEndPlugin<IViewOnlyNoteb
146181
);
147182

148183
const trans = translator.load('jupyterlab');
184+
const editorFactory = editorServices.factoryService.newInlineEditor;
149185

150186
const factory = new ViewOnlyNotebookWidgetFactory({
151187
name: VIEW_ONLY_NOTEBOOK_FACTORY,
@@ -155,7 +191,7 @@ export const viewOnlyNotebookFactoryPlugin: JupyterFrontEndPlugin<IViewOnlyNoteb
155191
preferKernel: false,
156192
canStartKernel: false,
157193
rendermime,
158-
contentFactory,
194+
contentFactory: new ViewOnlyContentFactory({ editorFactory }),
159195
editorConfig: StaticNotebook.defaultEditorConfig,
160196
notebookConfig: StaticNotebook.defaultNotebookConfig,
161197
mimeTypeService: editorServices.mimeTypeService,
2.29 KB
Loading

0 commit comments

Comments
 (0)