@jupyterlab/notebook-extension 4.0.0-alpha.20 → 4.0.0-alpha.21
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.js +268 -96
- package/lib/index.js.map +1 -1
- package/lib/tool-widgets/activeCellToolWidget.d.ts +35 -0
- package/lib/tool-widgets/activeCellToolWidget.js +78 -0
- package/lib/tool-widgets/activeCellToolWidget.js.map +1 -0
- package/lib/tool-widgets/metadataEditorFields.d.ts +56 -0
- package/lib/tool-widgets/metadataEditorFields.js +76 -0
- package/lib/tool-widgets/metadataEditorFields.js.map +1 -0
- package/package.json +44 -39
- package/schema/tools.json +143 -0
- package/schema/tracker.json +3 -3
- package/src/index.ts +390 -118
- package/src/tool-widgets/activeCellToolWidget.tsx +119 -0
- package/src/tool-widgets/metadataEditorFields.tsx +128 -0
- package/style/index.css +1 -0
- package/style/index.js +1 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { FieldProps } from '@rjsf/utils';
|
|
8
|
+
import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
|
|
9
|
+
import { INotebookTracker, NotebookTools } from '@jupyterlab/notebook';
|
|
10
|
+
import { ISharedText } from '@jupyter/ydoc';
|
|
11
|
+
import { PanelLayout, Widget } from '@lumino/widgets';
|
|
12
|
+
import { CodeCellModel, ICellModel, InputPrompt } from '@jupyterlab/cells';
|
|
13
|
+
import { Debouncer } from '@lumino/polling';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The class name added to the ActiveCellTool.
|
|
17
|
+
*/
|
|
18
|
+
const ACTIVE_CELL_TOOL_CLASS = 'jp-ActiveCellTool';
|
|
19
|
+
/**
|
|
20
|
+
* The class name added to the ActiveCellTool content.
|
|
21
|
+
*/
|
|
22
|
+
const ACTIVE_CELL_TOOL_CONTENT_CLASS = 'jp-ActiveCellTool-Content';
|
|
23
|
+
/**
|
|
24
|
+
* The class name added to the ActiveCellTool cell content.
|
|
25
|
+
*/
|
|
26
|
+
const ACTIVE_CELL_TOOL_CELL_CONTENT_CLASS = 'jp-ActiveCellTool-CellContent';
|
|
27
|
+
|
|
28
|
+
namespace Private {
|
|
29
|
+
/**
|
|
30
|
+
* Custom active cell field options.
|
|
31
|
+
*/
|
|
32
|
+
export interface IOptions {
|
|
33
|
+
/**
|
|
34
|
+
* The tracker to the notebook panel.
|
|
35
|
+
*/
|
|
36
|
+
tracker: INotebookTracker;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Editor languages registry
|
|
40
|
+
*/
|
|
41
|
+
languages: IEditorLanguageRegistry;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The active cell field, displaying the first line and execution count of the active cell.
|
|
47
|
+
*
|
|
48
|
+
* ## Note
|
|
49
|
+
* This field does not work as other metadata form fields, as it does not update metadata.
|
|
50
|
+
*/
|
|
51
|
+
export class ActiveCellTool extends NotebookTools.Tool {
|
|
52
|
+
constructor(options: Private.IOptions) {
|
|
53
|
+
super();
|
|
54
|
+
const { languages } = options;
|
|
55
|
+
this._tracker = options.tracker;
|
|
56
|
+
|
|
57
|
+
this.addClass(ACTIVE_CELL_TOOL_CLASS);
|
|
58
|
+
this.layout = new PanelLayout();
|
|
59
|
+
|
|
60
|
+
this._inputPrompt = new InputPrompt();
|
|
61
|
+
(this.layout as PanelLayout).addWidget(this._inputPrompt);
|
|
62
|
+
|
|
63
|
+
// First code line container
|
|
64
|
+
const node = document.createElement('div');
|
|
65
|
+
node.classList.add(ACTIVE_CELL_TOOL_CONTENT_CLASS);
|
|
66
|
+
const container = node.appendChild(document.createElement('div'));
|
|
67
|
+
const editor = container.appendChild(document.createElement('pre'));
|
|
68
|
+
container.className = ACTIVE_CELL_TOOL_CELL_CONTENT_CLASS;
|
|
69
|
+
this._editorEl = editor;
|
|
70
|
+
(this.layout as PanelLayout).addWidget(new Widget({ node }));
|
|
71
|
+
|
|
72
|
+
const update = async () => {
|
|
73
|
+
this._editorEl.innerHTML = '';
|
|
74
|
+
if (this._cellModel?.type === 'code') {
|
|
75
|
+
this._inputPrompt.executionCount = `${
|
|
76
|
+
(this._cellModel as CodeCellModel).executionCount ?? ''
|
|
77
|
+
}`;
|
|
78
|
+
this._inputPrompt.show();
|
|
79
|
+
} else {
|
|
80
|
+
this._inputPrompt.executionCount = null;
|
|
81
|
+
this._inputPrompt.hide();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this._cellModel) {
|
|
85
|
+
await languages.highlight(
|
|
86
|
+
this._cellModel.sharedModel.getSource().split('\n')[0],
|
|
87
|
+
languages.findByMIME(this._cellModel.mimeType),
|
|
88
|
+
this._editorEl
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
this._refreshDebouncer = new Debouncer(update, 150);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
render(props: FieldProps): JSX.Element {
|
|
97
|
+
const activeCell = this._tracker.activeCell;
|
|
98
|
+
if (activeCell) this._cellModel = activeCell?.model || null;
|
|
99
|
+
(this._cellModel?.sharedModel as ISharedText).changed.connect(
|
|
100
|
+
this.refresh,
|
|
101
|
+
this
|
|
102
|
+
);
|
|
103
|
+
this._cellModel?.mimeTypeChanged.connect(this.refresh, this);
|
|
104
|
+
this.refresh()
|
|
105
|
+
.then(() => undefined)
|
|
106
|
+
.catch(() => undefined);
|
|
107
|
+
return <div ref={ref => ref?.appendChild(this.node)}></div>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async refresh(): Promise<void> {
|
|
111
|
+
await this._refreshDebouncer.invoke();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private _tracker: INotebookTracker;
|
|
115
|
+
private _cellModel: ICellModel | null;
|
|
116
|
+
private _refreshDebouncer: Debouncer<void, void, null[]>;
|
|
117
|
+
private _editorEl: HTMLPreElement;
|
|
118
|
+
private _inputPrompt: InputPrompt;
|
|
119
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { FieldProps } from '@rjsf/utils';
|
|
8
|
+
import { INotebookTracker, NotebookTools } from '@jupyterlab/notebook';
|
|
9
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
10
|
+
import { CodeEditor } from '@jupyterlab/codeeditor';
|
|
11
|
+
import { ObservableJSON } from '@jupyterlab/observables';
|
|
12
|
+
import { JSONObject } from '@lumino/coreutils';
|
|
13
|
+
|
|
14
|
+
const CELL_METADATA_EDITOR_CLASS = 'jp-CellMetadataEditor';
|
|
15
|
+
const NOTEBOOK_METADATA_EDITOR_CLASS = 'jp-NotebookMetadataEditor';
|
|
16
|
+
|
|
17
|
+
namespace Private {
|
|
18
|
+
/**
|
|
19
|
+
* Custom metadata field options.
|
|
20
|
+
*/
|
|
21
|
+
export interface IOptions {
|
|
22
|
+
/**
|
|
23
|
+
* The editor factory used by the tool.
|
|
24
|
+
*/
|
|
25
|
+
editorFactory: CodeEditor.Factory;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The tracker to the notebook panel.
|
|
29
|
+
*/
|
|
30
|
+
tracker: INotebookTracker;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The label of the JSON editor.
|
|
34
|
+
*/
|
|
35
|
+
label?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Language translator.
|
|
39
|
+
*/
|
|
40
|
+
translator?: ITranslator;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The cell metadata field.
|
|
46
|
+
*
|
|
47
|
+
* ## Note
|
|
48
|
+
* This field does not work as other metadata form fields, as it does not use RJSF to update metadata.
|
|
49
|
+
* It extends the MetadataEditorTool which updates itself the metadata.
|
|
50
|
+
* It only renders the node of MetadataEditorTool in a React element instead of displaying a RJSF field.
|
|
51
|
+
*/
|
|
52
|
+
export class CellMetadataField extends NotebookTools.MetadataEditorTool {
|
|
53
|
+
constructor(options: Private.IOptions) {
|
|
54
|
+
super(options);
|
|
55
|
+
this._tracker = options.tracker;
|
|
56
|
+
|
|
57
|
+
this.editor.editorHostNode.addEventListener('blur', this.editor, true);
|
|
58
|
+
this.editor.editorHostNode.addEventListener('click', this.editor, true);
|
|
59
|
+
this.editor.headerNode.addEventListener('click', this.editor);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private _onSourceChanged() {
|
|
63
|
+
if (this.editor.source) {
|
|
64
|
+
this._tracker.activeCell?.model.sharedModel.setMetadata(
|
|
65
|
+
this.editor.source.toJSON()
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
render(props: FieldProps): JSX.Element {
|
|
71
|
+
const cell = this._tracker.activeCell;
|
|
72
|
+
this.editor.source = cell
|
|
73
|
+
? new ObservableJSON({ values: cell.model.metadata as JSONObject })
|
|
74
|
+
: null;
|
|
75
|
+
this.editor.source?.changed.connect(this._onSourceChanged, this);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={CELL_METADATA_EDITOR_CLASS}>
|
|
79
|
+
<div ref={ref => ref?.appendChild(this.node)}></div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private _tracker: INotebookTracker;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The notebook metadata field.
|
|
89
|
+
*
|
|
90
|
+
* ## Note
|
|
91
|
+
* This field does not work as other metadata form fields, as it does not use RJSF to update metadata.
|
|
92
|
+
* It extends the MetadataEditorTool which updates itself the metadata.
|
|
93
|
+
* It only renders the node of MetadataEditorTool in a React element instead of displaying a RJSF field.
|
|
94
|
+
*/
|
|
95
|
+
export class NotebookMetadataField extends NotebookTools.MetadataEditorTool {
|
|
96
|
+
constructor(options: Private.IOptions) {
|
|
97
|
+
super(options);
|
|
98
|
+
this._tracker = options.tracker;
|
|
99
|
+
|
|
100
|
+
this.editor.editorHostNode.addEventListener('blur', this.editor, true);
|
|
101
|
+
this.editor.editorHostNode.addEventListener('click', this.editor, true);
|
|
102
|
+
this.editor.headerNode.addEventListener('click', this.editor);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private _onSourceChanged() {
|
|
106
|
+
if (this.editor.source) {
|
|
107
|
+
this._tracker.currentWidget?.model?.sharedModel.setMetadata(
|
|
108
|
+
this.editor.source.toJSON()
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
render(props: FieldProps): JSX.Element {
|
|
114
|
+
const notebook = this._tracker.currentWidget;
|
|
115
|
+
this.editor.source = notebook
|
|
116
|
+
? new ObservableJSON({ values: notebook.model?.metadata as JSONObject })
|
|
117
|
+
: null;
|
|
118
|
+
this.editor.source?.changed.connect(this._onSourceChanged, this);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div className={NOTEBOOK_METADATA_EDITOR_CLASS}>
|
|
122
|
+
<div ref={ref => ref?.appendChild(this.node)}></div>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private _tracker: INotebookTracker;
|
|
128
|
+
}
|
package/style/index.css
CHANGED
|
@@ -25,4 +25,5 @@
|
|
|
25
25
|
@import url('~@jupyterlab/lsp/style/index.css');
|
|
26
26
|
@import url('~@jupyterlab/mainmenu/style/index.css');
|
|
27
27
|
@import url('~@jupyterlab/notebook/style/index.css');
|
|
28
|
+
@import url('~@jupyterlab/metadataform/style/index.css');
|
|
28
29
|
@import url('~@jupyterlab/property-inspector/style/index.css');
|
package/style/index.js
CHANGED
|
@@ -25,4 +25,5 @@ import '@jupyterlab/logconsole/style/index.js';
|
|
|
25
25
|
import '@jupyterlab/lsp/style/index.js';
|
|
26
26
|
import '@jupyterlab/mainmenu/style/index.js';
|
|
27
27
|
import '@jupyterlab/notebook/style/index.js';
|
|
28
|
+
import '@jupyterlab/metadataform/style/index.js';
|
|
28
29
|
import '@jupyterlab/property-inspector/style/index.js';
|