@fails-components/jupyter-applet-view 0.0.1-alpha.10
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/LICENSE +29 -0
- package/README.md +101 -0
- package/lib/appletview.d.ts +7 -0
- package/lib/appletview.js +333 -0
- package/lib/avoutputarea.d.ts +149 -0
- package/lib/avoutputarea.js +751 -0
- package/lib/avtoolbarextension.d.ts +36 -0
- package/lib/avtoolbarextension.js +213 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +35 -0
- package/lib/splitviewnotebookpanel.d.ts +31 -0
- package/lib/splitviewnotebookpanel.js +127 -0
- package/package.json +226 -0
- package/schema/plugin.json +31 -0
- package/schema/toolbar.json +8 -0
- package/src/appletview.ts +395 -0
- package/src/avoutputarea.ts +966 -0
- package/src/avtoolbarextension.ts +280 -0
- package/src/index.ts +47 -0
- package/src/splitviewnotebookpanel.ts +186 -0
- package/style/base.css +5 -0
- package/style/index.css +163 -0
- package/style/index.js +2 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Marten Richter
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# fails_components_jupyter_applet_view
|
|
2
|
+
|
|
3
|
+
[](https://github.com/fails-components/jupyterfails/actions/workflows/build.yml)
|
|
4
|
+
|
|
5
|
+
An extension, that let's you select cell for an applet.
|
|
6
|
+
The applets are show in a splitted notebook window.
|
|
7
|
+
The view can be switched programmatically to an applet mode, where only the selected cells are visible. This is used in fails-components to have jupyter applets in an interactive teaching environment.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- JupyterLab >= 4.0.0
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
To install the extension, execute:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install fails_components_jupyter_applet_view
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Uninstall
|
|
22
|
+
|
|
23
|
+
To remove the extension, execute:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip uninstall fails_components_jupyter_applet_view
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Contributing
|
|
30
|
+
|
|
31
|
+
### Development install
|
|
32
|
+
|
|
33
|
+
Note: You will need NodeJS to build the extension package.
|
|
34
|
+
|
|
35
|
+
The `jlpm` command is JupyterLab's pinned version of
|
|
36
|
+
[yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use
|
|
37
|
+
`yarn` or `npm` in lieu of `jlpm` below.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Clone the repo to your local environment
|
|
41
|
+
# Change directory to the fails_components_jupyter_applet_view directory
|
|
42
|
+
# Install package in development mode
|
|
43
|
+
pip install -e "."
|
|
44
|
+
# Link your development version of the extension with JupyterLab
|
|
45
|
+
jupyter labextension develop . --overwrite
|
|
46
|
+
# Rebuild extension Typescript source after making changes
|
|
47
|
+
jlpm build
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Watch the source directory in one terminal, automatically rebuilding when needed
|
|
54
|
+
jlpm watch
|
|
55
|
+
# Run JupyterLab in another terminal
|
|
56
|
+
jupyter lab
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).
|
|
60
|
+
|
|
61
|
+
By default, the `jlpm build` command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
jupyter lab build --minimize=False
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Development uninstall
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip uninstall fails_components_jupyter_applet_view
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
In development mode, you will also need to remove the symlink created by `jupyter labextension develop`
|
|
74
|
+
command. To find its location, you can run `jupyter labextension list` to figure out where the `labextensions`
|
|
75
|
+
folder is located. Then you can remove the symlink named `@fails-components/jupyter-applet-view` within that folder.
|
|
76
|
+
|
|
77
|
+
### Testing the extension
|
|
78
|
+
|
|
79
|
+
#### Frontend tests
|
|
80
|
+
|
|
81
|
+
This extension is using [Jest](https://jestjs.io/) for JavaScript code testing.
|
|
82
|
+
|
|
83
|
+
To execute them, execute:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
jlpm
|
|
87
|
+
jlpm test
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
<!--
|
|
91
|
+
#### Integration tests
|
|
92
|
+
|
|
93
|
+
This extension uses [Playwright](https://playwright.dev/docs/intro) for the integration tests (aka user level tests).
|
|
94
|
+
More precisely, the JupyterLab helper [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) is used to handle testing the extension in JupyterLab.
|
|
95
|
+
|
|
96
|
+
More information are provided within the [ui-tests](./ui-tests/README.md) README.
|
|
97
|
+
|
|
98
|
+
### Packaging the extension
|
|
99
|
+
|
|
100
|
+
See [RELEASE](RELEASE.md)
|
|
101
|
+
-->
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { JupyterFrontEnd, ILayoutRestorer } from '@jupyterlab/application';
|
|
2
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
3
|
+
import { INotebookTracker } from '@jupyterlab/notebook';
|
|
4
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
5
|
+
import { IFailsLauncherInfo } from '@fails-components/jupyter-launcher';
|
|
6
|
+
import { IFailsInterceptor } from '@fails-components/jupyter-interceptor';
|
|
7
|
+
export declare function activateAppletView(app: JupyterFrontEnd, docManager: IDocumentManager, notebookTracker: INotebookTracker, translator: ITranslator, restorer: ILayoutRestorer | null, failsLauncherInfo: IFailsLauncherInfo | null, failsInterceptor: IFailsInterceptor | null): void;
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { addIcon, moveUpIcon, moveDownIcon, caretUpIcon, caretDownIcon, deleteIcon } from '@jupyterlab/ui-components';
|
|
2
|
+
import { SplitViewNotebookWidgetFactory } from './splitviewnotebookpanel';
|
|
3
|
+
// portions used from Jupyterlab:
|
|
4
|
+
/* -----------------------------------------------------------------------------
|
|
5
|
+
| Copyright (c) Jupyter Development Team.
|
|
6
|
+
| Distributed under the terms of the Modified BSD License.
|
|
7
|
+
|----------------------------------------------------------------------------*/
|
|
8
|
+
// This code contains portions from or is inspired by Jupyter lab's notebook extension, especially the createOutputView part
|
|
9
|
+
// Also a lot is taken from the cell toolbar related parts.
|
|
10
|
+
export function activateAppletView(app, docManager, notebookTracker, translator, restorer, failsLauncherInfo, failsInterceptor) {
|
|
11
|
+
if (app.namespace === 'JupyterLite Server') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
console.log('JupyterLab extension @fails-components/jupyter-applet-view is activated!');
|
|
15
|
+
const trans = translator.load('fails_components_jupyter_applet_view');
|
|
16
|
+
const addToViewID = 'fails-components-jupyter-applet-view:add_to_view';
|
|
17
|
+
const moveViewUpID = 'fails-components-jupyter-applet-view:move_view_up';
|
|
18
|
+
const moveViewDownID = 'fails-components-jupyter-applet-view:move_view_down';
|
|
19
|
+
const moveViewAppUpID = 'fails-components-jupyter-applet-view:move_view_app_up';
|
|
20
|
+
const moveViewAppDownID = 'fails-components-jupyter-applet-view:move_view_app_down';
|
|
21
|
+
const deleteViewID = 'fails-components-jupyter-applet-view:delete_view';
|
|
22
|
+
/*const appletViewOutputs = new WidgetTracker<
|
|
23
|
+
MainAreaWidget<Private.AppletViewOutputArea>
|
|
24
|
+
>({
|
|
25
|
+
namespace: 'cloned-outputs'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (restorer) {
|
|
29
|
+
void restorer.restore(appletViewOutputs, {
|
|
30
|
+
command: commandID,
|
|
31
|
+
args: widget => ({
|
|
32
|
+
path: widget.content.path,
|
|
33
|
+
indices: widget.content.indices
|
|
34
|
+
}),
|
|
35
|
+
name: widget =>
|
|
36
|
+
`${widget.content.path}:${widget.content.indices.join(':')}`,
|
|
37
|
+
when: notebookTracker.restored // After the notebook widgets (but not contents).
|
|
38
|
+
});
|
|
39
|
+
} */
|
|
40
|
+
const { commands, shell /* , serviceManager: services */ } = app;
|
|
41
|
+
const realFactory = app.docRegistry.getWidgetFactory('Notebook');
|
|
42
|
+
const factoryName = 'Notebook'; //'SplitViewNotebook';
|
|
43
|
+
if (realFactory !== undefined) {
|
|
44
|
+
const factory = new SplitViewNotebookWidgetFactory({
|
|
45
|
+
name: factoryName,
|
|
46
|
+
label: trans.__('Notebook'),
|
|
47
|
+
fileTypes: ['notebook'],
|
|
48
|
+
modelName: 'notebook',
|
|
49
|
+
defaultFor: ['notebook'],
|
|
50
|
+
preferKernel: realFactory.preferKernel,
|
|
51
|
+
canStartKernel: true,
|
|
52
|
+
rendermime: realFactory.rendermime,
|
|
53
|
+
contentFactory: realFactory.contentFactory,
|
|
54
|
+
editorConfig: realFactory.editorConfig,
|
|
55
|
+
notebookConfig: realFactory.notebookConfig,
|
|
56
|
+
mimeTypeService: realFactory.mimeTypeService,
|
|
57
|
+
toolbarFactory: realFactory['_toolbarFactory'],
|
|
58
|
+
translator,
|
|
59
|
+
failsLauncherInfo: failsLauncherInfo !== null ? failsLauncherInfo : undefined,
|
|
60
|
+
failsInterceptor: failsInterceptor !== null ? failsInterceptor : undefined
|
|
61
|
+
});
|
|
62
|
+
let id = 0;
|
|
63
|
+
// we need to clone the registration with the tracker from the plugin:
|
|
64
|
+
factory.widgetCreated.connect((sender, widget) => {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
// If the notebook panel does not have an ID, assign it one.
|
|
67
|
+
widget.id = widget.id || `splitviewnotebook-${++id}`;
|
|
68
|
+
const ft = app.docRegistry.getFileType('notebook');
|
|
69
|
+
// Set up the title icon
|
|
70
|
+
widget.title.icon = ft === null || ft === void 0 ? void 0 : ft.icon;
|
|
71
|
+
widget.title.iconClass = (_a = ft === null || ft === void 0 ? void 0 : ft.iconClass) !== null && _a !== void 0 ? _a : '';
|
|
72
|
+
widget.title.iconLabel = (_b = ft === null || ft === void 0 ? void 0 : ft.iconLabel) !== null && _b !== void 0 ? _b : '';
|
|
73
|
+
// Notify the widget tracker if restore data needs to update.
|
|
74
|
+
const tracker = notebookTracker; // dirty hack, does only work as long we do not add anything to the model
|
|
75
|
+
/* widget.context.pathChanged.connect(() => {
|
|
76
|
+
void tracker.save(widget);
|
|
77
|
+
}); // may be we need this */
|
|
78
|
+
// Add the notebook panel to the tracker.
|
|
79
|
+
// void tracker.add(widget);
|
|
80
|
+
widget.context.fileChanged.connect(() => {
|
|
81
|
+
const model = widget.context.model;
|
|
82
|
+
const failsData = model.getMetadata('failsApp');
|
|
83
|
+
const currentSplitView = widget;
|
|
84
|
+
if (currentSplitView.appletViewWidget) {
|
|
85
|
+
if (failsData) {
|
|
86
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
87
|
+
if (outputarea !== undefined) {
|
|
88
|
+
outputarea.loadData(failsData);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
widget.context.saveState.connect((slot, savestate) => {
|
|
94
|
+
if (savestate === 'started') {
|
|
95
|
+
const currentSplitView = widget;
|
|
96
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
97
|
+
if (outputarea !== undefined) {
|
|
98
|
+
const failsData = outputarea.saveData();
|
|
99
|
+
if (failsData) {
|
|
100
|
+
const model = widget.context.model;
|
|
101
|
+
model.setMetadata('failsApp', failsData);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// notebookTracker.inject(widget);
|
|
107
|
+
tracker.add(widget);
|
|
108
|
+
if (!notebookTracker.currentWidget) {
|
|
109
|
+
const pool = tracker['_pool'];
|
|
110
|
+
pool.current = widget;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
// Handle state restoration.
|
|
114
|
+
// No the notebook should do this.
|
|
115
|
+
/* if (restorer) {
|
|
116
|
+
const tracker = notebookTracker as NotebookTracker;
|
|
117
|
+
void restorer.restore(tracker, {
|
|
118
|
+
command: 'docmanager:open',
|
|
119
|
+
args: panel => ({ path: panel.context.path, factory: factoryName }),
|
|
120
|
+
name: panel => panel.context.path,
|
|
121
|
+
when: services.ready
|
|
122
|
+
});
|
|
123
|
+
} */
|
|
124
|
+
// remove from registry, this is bad monkey patching
|
|
125
|
+
if (app.docRegistry['_widgetFactories']['notebook']) {
|
|
126
|
+
delete app.docRegistry['_widgetFactories']['notebook'];
|
|
127
|
+
}
|
|
128
|
+
app.docRegistry.addWidgetFactory(factory);
|
|
129
|
+
app.docRegistry.setDefaultWidgetFactory('notebook',
|
|
130
|
+
/* 'SplitViewNotebook'*/ 'Notebook');
|
|
131
|
+
// we have to register extensions previously added to the system, FIXME: maybe changed after decoupling from jupyter lab
|
|
132
|
+
/* const itExtension = app.docRegistry.widgetExtensions('Notebook');
|
|
133
|
+
for (const extension of itExtension) {
|
|
134
|
+
app.docRegistry.addWidgetExtension(factoryName, extension);
|
|
135
|
+
}*/
|
|
136
|
+
}
|
|
137
|
+
const canBeActivated = () => {
|
|
138
|
+
if (notebookTracker.currentWidget === null ||
|
|
139
|
+
notebookTracker.currentWidget !== shell.currentWidget) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const { content } = notebookTracker.currentWidget;
|
|
143
|
+
const index = content.activeCellIndex;
|
|
144
|
+
// If there are selections that are not the active cell,
|
|
145
|
+
// this command is confusing, so disable it.
|
|
146
|
+
for (let i = 0; i < content.widgets.length; ++i) {
|
|
147
|
+
if (content.isSelected(content.widgets[i]) && i !== index) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// If the cell is already added we deactivate as well
|
|
152
|
+
const currentSplitView = notebookTracker.currentWidget;
|
|
153
|
+
if (currentSplitView.appletViewWidget) {
|
|
154
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
155
|
+
if (outputarea !== undefined && outputarea.firstHasIndex(index)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
161
|
+
commands.addCommand(addToViewID, {
|
|
162
|
+
label: /* trans.__(*/ 'Add Output to first Applet view' /*)*/,
|
|
163
|
+
execute: async (args) => {
|
|
164
|
+
const path = args.path;
|
|
165
|
+
let index = args.index;
|
|
166
|
+
let current;
|
|
167
|
+
let cell;
|
|
168
|
+
// console.log('Add Output for path and index', path, index, args);
|
|
169
|
+
if (path && index !== undefined && index !== null) {
|
|
170
|
+
current = docManager.findWidget(path, 'Notebook' /* may be needs adjustment later*/);
|
|
171
|
+
if (!current) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
current = notebookTracker.currentWidget;
|
|
177
|
+
if (!current) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
cell = current.content.activeCell;
|
|
181
|
+
index = current.content.activeCellIndex;
|
|
182
|
+
}
|
|
183
|
+
// const pathid = current.context.path;
|
|
184
|
+
// console.log('debug current cell index', current, cell, index);
|
|
185
|
+
// TODO: Find area if it already exists, and add content
|
|
186
|
+
const currentSplitView = current;
|
|
187
|
+
if (currentSplitView.appletViewWidget) {
|
|
188
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
189
|
+
if (outputarea !== undefined && !outputarea.firstHasIndex(index)) {
|
|
190
|
+
outputarea.addPart(undefined, { cell, index });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
icon: args => (args.toolbar ? addIcon : undefined),
|
|
195
|
+
isEnabled: canBeActivated,
|
|
196
|
+
isVisible: canBeActivated
|
|
197
|
+
});
|
|
198
|
+
function getCurrentNotebook(args) {
|
|
199
|
+
let current;
|
|
200
|
+
if (typeof args['notebookpath'] !== 'string') {
|
|
201
|
+
current = notebookTracker.currentWidget;
|
|
202
|
+
if (!current) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const path = args['notebookpath'];
|
|
208
|
+
current = docManager.findWidget(path, 'Notebook' /* may be needs adjustment later*/);
|
|
209
|
+
if (!current) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return current;
|
|
214
|
+
}
|
|
215
|
+
function moveWidgets(args, delta) {
|
|
216
|
+
const current = getCurrentNotebook(args);
|
|
217
|
+
if (!current) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const currentSplitView = current;
|
|
221
|
+
if (currentSplitView.appletViewWidget) {
|
|
222
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
223
|
+
const cellid = args.cellid;
|
|
224
|
+
const widgetid = args.widgetid;
|
|
225
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
226
|
+
if (typeof appid !== 'undefined') {
|
|
227
|
+
outputarea.movePart(appid, cellid, delta);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function moveWidgetsApp(args, delta) {
|
|
232
|
+
const current = getCurrentNotebook(args);
|
|
233
|
+
if (!current) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const currentSplitView = current;
|
|
237
|
+
if (currentSplitView.appletViewWidget) {
|
|
238
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
239
|
+
const cellid = args.cellid;
|
|
240
|
+
const widgetid = args.widgetid;
|
|
241
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
242
|
+
if (typeof appid !== 'undefined') {
|
|
243
|
+
outputarea.moveApp(appid, cellid, delta);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/*
|
|
248
|
+
function canMoveWidgetsApp(
|
|
249
|
+
args: ReadonlyPartialJSONObject,
|
|
250
|
+
delta: number
|
|
251
|
+
): boolean {
|
|
252
|
+
const current = getCurrentNotebook(args);
|
|
253
|
+
if (!current) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
const currentSplitView = current as Private.SplitViewNotebookPanel;
|
|
257
|
+
if (currentSplitView.appletViewWidget) {
|
|
258
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
259
|
+
const cellid = args.cellid as string;
|
|
260
|
+
const widgetid = args.widgetid as string;
|
|
261
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
262
|
+
if (typeof appid !== 'undefined') {
|
|
263
|
+
return outputarea.canMoveApp(appid, cellid, delta);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
*/
|
|
269
|
+
commands.addCommand(moveViewUpID, {
|
|
270
|
+
label: /* trans.__(*/ 'Move view up' /*)*/,
|
|
271
|
+
execute: async (args) => {
|
|
272
|
+
moveWidgets(args, -1);
|
|
273
|
+
},
|
|
274
|
+
icon: args => (args.toolbar ? moveUpIcon : undefined),
|
|
275
|
+
isEnabled: () => true,
|
|
276
|
+
isVisible: () => true
|
|
277
|
+
});
|
|
278
|
+
commands.addCommand(moveViewDownID, {
|
|
279
|
+
label: /* trans.__(*/ 'Move view down' /*)*/,
|
|
280
|
+
execute: async (args) => {
|
|
281
|
+
moveWidgets(args, 1);
|
|
282
|
+
},
|
|
283
|
+
icon: args => (args.toolbar ? moveDownIcon : undefined),
|
|
284
|
+
isEnabled: () => true,
|
|
285
|
+
isVisible: () => true
|
|
286
|
+
});
|
|
287
|
+
commands.addCommand(moveViewAppUpID, {
|
|
288
|
+
label: /* trans.__(*/ 'Move view up to other app' /*)*/,
|
|
289
|
+
execute: async (args) => {
|
|
290
|
+
moveWidgetsApp(args, -1);
|
|
291
|
+
},
|
|
292
|
+
icon: args => (args.toolbar ? caretUpIcon : undefined),
|
|
293
|
+
isEnabled: () => true,
|
|
294
|
+
/* isEnabled: args => {
|
|
295
|
+
return canMoveWidgetsApp(args, -1);
|
|
296
|
+
},*/
|
|
297
|
+
isVisible: () => true
|
|
298
|
+
});
|
|
299
|
+
commands.addCommand(moveViewAppDownID, {
|
|
300
|
+
label: /* trans.__(*/ 'Move view down to other app' /*)*/,
|
|
301
|
+
execute: async (args) => {
|
|
302
|
+
moveWidgetsApp(args, 1);
|
|
303
|
+
},
|
|
304
|
+
icon: args => (args.toolbar ? caretDownIcon : undefined),
|
|
305
|
+
isEnabled: () => true,
|
|
306
|
+
/*isEnabled: args => {
|
|
307
|
+
return canMoveWidgetsApp(args, 1);
|
|
308
|
+
},*/
|
|
309
|
+
isVisible: () => true
|
|
310
|
+
});
|
|
311
|
+
commands.addCommand(deleteViewID, {
|
|
312
|
+
label: /* trans.__(*/ 'Delete view' /*)*/,
|
|
313
|
+
execute: async (args) => {
|
|
314
|
+
const current = getCurrentNotebook(args);
|
|
315
|
+
if (!current) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const currentSplitView = current;
|
|
319
|
+
if (currentSplitView.appletViewWidget) {
|
|
320
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
321
|
+
const cellid = args.cellid;
|
|
322
|
+
const widgetid = args.widgetid;
|
|
323
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
324
|
+
if (typeof appid !== 'undefined') {
|
|
325
|
+
outputarea.deletePart(appid, cellid);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
icon: args => (args.toolbar ? deleteIcon : undefined),
|
|
330
|
+
isEnabled: () => true,
|
|
331
|
+
isVisible: () => true
|
|
332
|
+
});
|
|
333
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Cell } from '@jupyterlab/cells';
|
|
2
|
+
import { NotebookPanel } from '@jupyterlab/notebook';
|
|
3
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
4
|
+
import { ISignal } from '@lumino/signaling';
|
|
5
|
+
import { AccordionPanel, Widget, Panel, Title } from '@lumino/widgets';
|
|
6
|
+
import { MainAreaWidget } from '@jupyterlab/apputils';
|
|
7
|
+
import { SplitViewNotebookPanel } from './splitviewnotebookpanel';
|
|
8
|
+
import { IFailsInterceptor } from '@fails-components/jupyter-interceptor';
|
|
9
|
+
import { IScreenShotOpts } from '@fails-components/jupyter-launcher';
|
|
10
|
+
/**
|
|
11
|
+
* A widget hosting applet views
|
|
12
|
+
*/
|
|
13
|
+
export declare class AppletViewOutputArea extends AccordionPanel {
|
|
14
|
+
constructor(options: AppletViewOutputArea.IOptions);
|
|
15
|
+
cloneCell(cell: Cell, cellid: string): Widget;
|
|
16
|
+
getWidgetAppId(widgetid: string): string | undefined;
|
|
17
|
+
addToObserver(appIndex: number, widget: Widget): void;
|
|
18
|
+
removeFromObserver(appIndex: number, widget: Widget): void;
|
|
19
|
+
addCell(appid: string, cell: Cell, cellid: string): Widget;
|
|
20
|
+
insertCell(appid: string, index: number, cell: Cell, cellid: string): Widget | undefined;
|
|
21
|
+
deletePart(appid: string, cellid: string): void;
|
|
22
|
+
movePart(appid: string, cellid: string, delta: number): void;
|
|
23
|
+
moveApp(appid: string, cellid: string, delta: number): false | undefined;
|
|
24
|
+
saveData(): {
|
|
25
|
+
applets: {
|
|
26
|
+
parts: {
|
|
27
|
+
index: number | undefined;
|
|
28
|
+
id: string | undefined;
|
|
29
|
+
}[];
|
|
30
|
+
appid: string;
|
|
31
|
+
appname: string;
|
|
32
|
+
}[];
|
|
33
|
+
};
|
|
34
|
+
loadData(data: any): void;
|
|
35
|
+
addApplet({ appid, appname }: {
|
|
36
|
+
appid: string;
|
|
37
|
+
appname?: string;
|
|
38
|
+
}): Panel;
|
|
39
|
+
addPart(appidOrUndefined: string | undefined, part: AppletViewOutputArea.IAppletPart): void;
|
|
40
|
+
firstHasIndex(index: number): boolean;
|
|
41
|
+
selectApplet(selectedAppid: string): void;
|
|
42
|
+
unselectApplet(): void;
|
|
43
|
+
takeAppScreenshot(opts: IScreenShotOpts): Promise<Blob | undefined>;
|
|
44
|
+
informResize(applet: IViewApplet): void;
|
|
45
|
+
resizeEvent(appid: string, entries: ResizeObserverEntry[], observer: ResizeObserver): void;
|
|
46
|
+
get applets(): IViewApplet[];
|
|
47
|
+
/**
|
|
48
|
+
* The index of the cell in the notebook.
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* The path of the notebook for the cloned output area.
|
|
52
|
+
*/
|
|
53
|
+
get path(): string;
|
|
54
|
+
get viewChanged(): ISignal<this, void>;
|
|
55
|
+
set inLecture(value: boolean);
|
|
56
|
+
handleEvent(event: Event): void;
|
|
57
|
+
private _notebook;
|
|
58
|
+
private _applets;
|
|
59
|
+
private _selectedAppid;
|
|
60
|
+
private _viewChanged;
|
|
61
|
+
private _inLecture;
|
|
62
|
+
private _interceptor;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* AppletViewOutputArea statics.
|
|
66
|
+
*/
|
|
67
|
+
export declare namespace AppletViewOutputArea {
|
|
68
|
+
interface IAppletPart {
|
|
69
|
+
/**
|
|
70
|
+
* The cell for which to clone the output area.
|
|
71
|
+
*/
|
|
72
|
+
cell?: Cell;
|
|
73
|
+
/**
|
|
74
|
+
* The cell id to uniquely identify the cell
|
|
75
|
+
*/
|
|
76
|
+
id?: string;
|
|
77
|
+
/**
|
|
78
|
+
* The cell index if the id is not set yet
|
|
79
|
+
*/
|
|
80
|
+
index?: number;
|
|
81
|
+
}
|
|
82
|
+
interface IApplet {
|
|
83
|
+
appid?: string;
|
|
84
|
+
appname?: string;
|
|
85
|
+
parts: IAppletPart[];
|
|
86
|
+
}
|
|
87
|
+
interface IOptions {
|
|
88
|
+
/**
|
|
89
|
+
* The notebook associated with the cloned output area.
|
|
90
|
+
*/
|
|
91
|
+
notebook: SplitViewNotebookPanel;
|
|
92
|
+
applets?: IApplet[];
|
|
93
|
+
translator?: ITranslator;
|
|
94
|
+
interceptor?: IFailsInterceptor;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export interface IViewPartBase extends AppletViewOutputArea.IAppletPart {
|
|
98
|
+
added?: boolean;
|
|
99
|
+
clone?: Widget;
|
|
100
|
+
}
|
|
101
|
+
export interface IViewPartSize {
|
|
102
|
+
width: number;
|
|
103
|
+
height: number;
|
|
104
|
+
}
|
|
105
|
+
export interface IViewPart extends IViewPartBase {
|
|
106
|
+
added?: boolean;
|
|
107
|
+
clone?: Widget;
|
|
108
|
+
cloned: ISignal<IViewPart, void>;
|
|
109
|
+
sizes?: IViewPartSize;
|
|
110
|
+
}
|
|
111
|
+
export interface IViewApplet {
|
|
112
|
+
appid: string;
|
|
113
|
+
appname: string;
|
|
114
|
+
parts: IViewPart[];
|
|
115
|
+
observer: ResizeObserver;
|
|
116
|
+
}
|
|
117
|
+
export interface IAppletPartOptions extends IViewPartBase {
|
|
118
|
+
notebook: NotebookPanel;
|
|
119
|
+
}
|
|
120
|
+
export interface IAppletViewOutputAreasStore {
|
|
121
|
+
[key: string]: AppletViewOutputArea;
|
|
122
|
+
}
|
|
123
|
+
export interface IAppletViewMainAreaWidgetStore {
|
|
124
|
+
[key: string]: MainAreaWidget<AppletViewOutputArea>;
|
|
125
|
+
}
|
|
126
|
+
export declare class AppletViewOutputAreaPart implements AppletViewOutputArea.IAppletPart {
|
|
127
|
+
constructor(args: IAppletPartOptions);
|
|
128
|
+
/**
|
|
129
|
+
* The index of the cell in the notebook.
|
|
130
|
+
*/
|
|
131
|
+
get index(): number;
|
|
132
|
+
get cell(): Cell | undefined;
|
|
133
|
+
set cell(value: Cell | undefined);
|
|
134
|
+
get added(): boolean;
|
|
135
|
+
get id(): string | undefined;
|
|
136
|
+
set clone(value: Widget | undefined);
|
|
137
|
+
get clone(): Widget | undefined;
|
|
138
|
+
get path(): string;
|
|
139
|
+
get cloned(): ISignal<this, void>;
|
|
140
|
+
private _cell;
|
|
141
|
+
private _id;
|
|
142
|
+
private _index;
|
|
143
|
+
private _notebook;
|
|
144
|
+
private _clone;
|
|
145
|
+
private _cloned;
|
|
146
|
+
}
|
|
147
|
+
export declare class AppletViewRenderer extends AccordionPanel.Renderer {
|
|
148
|
+
createSectionTitle(data: Title<Widget>): HTMLElement;
|
|
149
|
+
}
|