@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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jupyter.lab.shortcuts": [],
|
|
3
|
+
"title": "Applet view for Fails",
|
|
4
|
+
"description": "@fails-components/jupyter-applet-view settings.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {},
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"jupyter.lab.menus": {
|
|
9
|
+
"context": [
|
|
10
|
+
{
|
|
11
|
+
"type": "separator",
|
|
12
|
+
"selector": ".jp-Notebook .jp-Cell",
|
|
13
|
+
"rank": 70
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"command": "fails-components-jupyter-applet-view:add_to_view",
|
|
17
|
+
"selector": ".jp-Notebook .jp-Cell",
|
|
18
|
+
"rank": 71
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"jupyter.lab.toolbars": {
|
|
23
|
+
"Cell": [
|
|
24
|
+
{
|
|
25
|
+
"name": "add_to_view",
|
|
26
|
+
"command": "fails-components-jupyter-applet-view:add_to_view",
|
|
27
|
+
"rank": 10
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { JupyterFrontEnd, ILayoutRestorer } from '@jupyterlab/application';
|
|
2
|
+
import { Cell } from '@jupyterlab/cells';
|
|
3
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
4
|
+
import {
|
|
5
|
+
INotebookTracker,
|
|
6
|
+
NotebookWidgetFactory,
|
|
7
|
+
NotebookTracker,
|
|
8
|
+
NotebookPanel
|
|
9
|
+
} from '@jupyterlab/notebook';
|
|
10
|
+
import { RestorablePool } from '@jupyterlab/statedb';
|
|
11
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
12
|
+
import {
|
|
13
|
+
addIcon,
|
|
14
|
+
moveUpIcon,
|
|
15
|
+
moveDownIcon,
|
|
16
|
+
caretUpIcon,
|
|
17
|
+
caretDownIcon,
|
|
18
|
+
deleteIcon
|
|
19
|
+
} from '@jupyterlab/ui-components';
|
|
20
|
+
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
|
|
21
|
+
import { SplitViewNotebookWidgetFactory } from './splitviewnotebookpanel';
|
|
22
|
+
import { SplitViewNotebookPanel } from './splitviewnotebookpanel';
|
|
23
|
+
import { IFailsLauncherInfo } from '@fails-components/jupyter-launcher';
|
|
24
|
+
import { IFailsInterceptor } from '@fails-components/jupyter-interceptor';
|
|
25
|
+
|
|
26
|
+
// portions used from Jupyterlab:
|
|
27
|
+
/* -----------------------------------------------------------------------------
|
|
28
|
+
| Copyright (c) Jupyter Development Team.
|
|
29
|
+
| Distributed under the terms of the Modified BSD License.
|
|
30
|
+
|----------------------------------------------------------------------------*/
|
|
31
|
+
// This code contains portions from or is inspired by Jupyter lab's notebook extension, especially the createOutputView part
|
|
32
|
+
// Also a lot is taken from the cell toolbar related parts.
|
|
33
|
+
export function activateAppletView(
|
|
34
|
+
app: JupyterFrontEnd,
|
|
35
|
+
docManager: IDocumentManager,
|
|
36
|
+
notebookTracker: INotebookTracker,
|
|
37
|
+
translator: ITranslator,
|
|
38
|
+
restorer: ILayoutRestorer | null,
|
|
39
|
+
failsLauncherInfo: IFailsLauncherInfo | null,
|
|
40
|
+
failsInterceptor: IFailsInterceptor | null
|
|
41
|
+
): void {
|
|
42
|
+
if (app.namespace === 'JupyterLite Server') {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
console.log(
|
|
46
|
+
'JupyterLab extension @fails-components/jupyter-applet-view is activated!'
|
|
47
|
+
);
|
|
48
|
+
const trans = translator.load('fails_components_jupyter_applet_view');
|
|
49
|
+
const addToViewID = 'fails-components-jupyter-applet-view:add_to_view';
|
|
50
|
+
const moveViewUpID = 'fails-components-jupyter-applet-view:move_view_up';
|
|
51
|
+
const moveViewDownID = 'fails-components-jupyter-applet-view:move_view_down';
|
|
52
|
+
const moveViewAppUpID =
|
|
53
|
+
'fails-components-jupyter-applet-view:move_view_app_up';
|
|
54
|
+
const moveViewAppDownID =
|
|
55
|
+
'fails-components-jupyter-applet-view:move_view_app_down';
|
|
56
|
+
const deleteViewID = 'fails-components-jupyter-applet-view:delete_view';
|
|
57
|
+
/*const appletViewOutputs = new WidgetTracker<
|
|
58
|
+
MainAreaWidget<Private.AppletViewOutputArea>
|
|
59
|
+
>({
|
|
60
|
+
namespace: 'cloned-outputs'
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (restorer) {
|
|
64
|
+
void restorer.restore(appletViewOutputs, {
|
|
65
|
+
command: commandID,
|
|
66
|
+
args: widget => ({
|
|
67
|
+
path: widget.content.path,
|
|
68
|
+
indices: widget.content.indices
|
|
69
|
+
}),
|
|
70
|
+
name: widget =>
|
|
71
|
+
`${widget.content.path}:${widget.content.indices.join(':')}`,
|
|
72
|
+
when: notebookTracker.restored // After the notebook widgets (but not contents).
|
|
73
|
+
});
|
|
74
|
+
} */
|
|
75
|
+
const { commands, shell /* , serviceManager: services */ } = app;
|
|
76
|
+
|
|
77
|
+
const realFactory: NotebookWidgetFactory | undefined =
|
|
78
|
+
app.docRegistry.getWidgetFactory(
|
|
79
|
+
'Notebook'
|
|
80
|
+
) as unknown as NotebookWidgetFactory;
|
|
81
|
+
const factoryName = 'Notebook'; //'SplitViewNotebook';
|
|
82
|
+
if (realFactory !== undefined) {
|
|
83
|
+
const factory = new SplitViewNotebookWidgetFactory({
|
|
84
|
+
name: factoryName,
|
|
85
|
+
label: trans.__('Notebook'),
|
|
86
|
+
fileTypes: ['notebook'],
|
|
87
|
+
modelName: 'notebook',
|
|
88
|
+
defaultFor: ['notebook'],
|
|
89
|
+
preferKernel: realFactory.preferKernel,
|
|
90
|
+
canStartKernel: true,
|
|
91
|
+
rendermime: realFactory.rendermime,
|
|
92
|
+
contentFactory: realFactory.contentFactory,
|
|
93
|
+
editorConfig: realFactory.editorConfig,
|
|
94
|
+
notebookConfig: realFactory.notebookConfig,
|
|
95
|
+
mimeTypeService: realFactory.mimeTypeService,
|
|
96
|
+
toolbarFactory: realFactory['_toolbarFactory'],
|
|
97
|
+
translator,
|
|
98
|
+
failsLauncherInfo:
|
|
99
|
+
failsLauncherInfo !== null ? failsLauncherInfo : undefined,
|
|
100
|
+
failsInterceptor: failsInterceptor !== null ? failsInterceptor : undefined
|
|
101
|
+
});
|
|
102
|
+
let id = 0;
|
|
103
|
+
// we need to clone the registration with the tracker from the plugin:
|
|
104
|
+
factory.widgetCreated.connect((sender, widget) => {
|
|
105
|
+
// If the notebook panel does not have an ID, assign it one.
|
|
106
|
+
widget.id = widget.id || `splitviewnotebook-${++id}`;
|
|
107
|
+
const ft = app.docRegistry.getFileType('notebook');
|
|
108
|
+
// Set up the title icon
|
|
109
|
+
widget.title.icon = ft?.icon;
|
|
110
|
+
widget.title.iconClass = ft?.iconClass ?? '';
|
|
111
|
+
widget.title.iconLabel = ft?.iconLabel ?? '';
|
|
112
|
+
|
|
113
|
+
// Notify the widget tracker if restore data needs to update.
|
|
114
|
+
const tracker = notebookTracker as NotebookTracker; // dirty hack, does only work as long we do not add anything to the model
|
|
115
|
+
|
|
116
|
+
/* widget.context.pathChanged.connect(() => {
|
|
117
|
+
void tracker.save(widget);
|
|
118
|
+
}); // may be we need this */
|
|
119
|
+
// Add the notebook panel to the tracker.
|
|
120
|
+
// void tracker.add(widget);
|
|
121
|
+
widget.context.fileChanged.connect(() => {
|
|
122
|
+
const model = widget.context.model;
|
|
123
|
+
const failsData = model.getMetadata('failsApp');
|
|
124
|
+
const currentSplitView = widget as SplitViewNotebookPanel;
|
|
125
|
+
if (currentSplitView.appletViewWidget) {
|
|
126
|
+
if (failsData) {
|
|
127
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
128
|
+
if (outputarea !== undefined) {
|
|
129
|
+
outputarea.loadData(failsData);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
widget.context.saveState.connect((slot, savestate) => {
|
|
135
|
+
if (savestate === 'started') {
|
|
136
|
+
const currentSplitView = widget as SplitViewNotebookPanel;
|
|
137
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
138
|
+
if (outputarea !== undefined) {
|
|
139
|
+
const failsData = outputarea.saveData();
|
|
140
|
+
if (failsData) {
|
|
141
|
+
const model = widget.context.model;
|
|
142
|
+
model.setMetadata('failsApp', failsData);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// notebookTracker.inject(widget);
|
|
149
|
+
tracker.add(widget);
|
|
150
|
+
if (!notebookTracker.currentWidget) {
|
|
151
|
+
const pool = tracker['_pool'] as RestorablePool;
|
|
152
|
+
pool.current = widget;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
// Handle state restoration.
|
|
156
|
+
// No the notebook should do this.
|
|
157
|
+
/* if (restorer) {
|
|
158
|
+
const tracker = notebookTracker as NotebookTracker;
|
|
159
|
+
void restorer.restore(tracker, {
|
|
160
|
+
command: 'docmanager:open',
|
|
161
|
+
args: panel => ({ path: panel.context.path, factory: factoryName }),
|
|
162
|
+
name: panel => panel.context.path,
|
|
163
|
+
when: services.ready
|
|
164
|
+
});
|
|
165
|
+
} */
|
|
166
|
+
// remove from registry, this is bad monkey patching
|
|
167
|
+
if (app.docRegistry['_widgetFactories']['notebook']) {
|
|
168
|
+
delete app.docRegistry['_widgetFactories']['notebook'];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
app.docRegistry.addWidgetFactory(factory);
|
|
172
|
+
app.docRegistry.setDefaultWidgetFactory(
|
|
173
|
+
'notebook',
|
|
174
|
+
/* 'SplitViewNotebook'*/ 'Notebook'
|
|
175
|
+
);
|
|
176
|
+
// we have to register extensions previously added to the system, FIXME: maybe changed after decoupling from jupyter lab
|
|
177
|
+
/* const itExtension = app.docRegistry.widgetExtensions('Notebook');
|
|
178
|
+
for (const extension of itExtension) {
|
|
179
|
+
app.docRegistry.addWidgetExtension(factoryName, extension);
|
|
180
|
+
}*/
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const canBeActivated = (): boolean => {
|
|
184
|
+
if (
|
|
185
|
+
notebookTracker.currentWidget === null ||
|
|
186
|
+
notebookTracker.currentWidget !== shell.currentWidget
|
|
187
|
+
) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
const { content } = notebookTracker.currentWidget!;
|
|
191
|
+
const index = content.activeCellIndex;
|
|
192
|
+
// If there are selections that are not the active cell,
|
|
193
|
+
// this command is confusing, so disable it.
|
|
194
|
+
for (let i = 0; i < content.widgets.length; ++i) {
|
|
195
|
+
if (content.isSelected(content.widgets[i]) && i !== index) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// If the cell is already added we deactivate as well
|
|
200
|
+
const currentSplitView =
|
|
201
|
+
notebookTracker.currentWidget as SplitViewNotebookPanel;
|
|
202
|
+
if (currentSplitView.appletViewWidget) {
|
|
203
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
204
|
+
if (outputarea !== undefined && outputarea.firstHasIndex(index)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
commands.addCommand(addToViewID, {
|
|
212
|
+
label: /* trans.__(*/ 'Add Output to first Applet view' /*)*/,
|
|
213
|
+
execute: async args => {
|
|
214
|
+
const path = args.path as string | undefined | null;
|
|
215
|
+
let index = args.index as number | undefined | null;
|
|
216
|
+
let current: NotebookPanel | undefined | null;
|
|
217
|
+
let cell: Cell | undefined;
|
|
218
|
+
|
|
219
|
+
// console.log('Add Output for path and index', path, index, args);
|
|
220
|
+
if (path && index !== undefined && index !== null) {
|
|
221
|
+
current = docManager.findWidget(
|
|
222
|
+
path,
|
|
223
|
+
'Notebook' /* may be needs adjustment later*/
|
|
224
|
+
) as unknown as NotebookPanel;
|
|
225
|
+
if (!current) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
current = notebookTracker.currentWidget;
|
|
230
|
+
if (!current) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
cell = current.content.activeCell as Cell;
|
|
234
|
+
index = current.content.activeCellIndex;
|
|
235
|
+
}
|
|
236
|
+
// const pathid = current.context.path;
|
|
237
|
+
// console.log('debug current cell index', current, cell, index);
|
|
238
|
+
// TODO: Find area if it already exists, and add content
|
|
239
|
+
const currentSplitView = current as SplitViewNotebookPanel;
|
|
240
|
+
if (currentSplitView.appletViewWidget) {
|
|
241
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
242
|
+
if (outputarea !== undefined && !outputarea.firstHasIndex(index)) {
|
|
243
|
+
outputarea.addPart(undefined, { cell, index });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
icon: args => (args.toolbar ? addIcon : undefined),
|
|
248
|
+
isEnabled: canBeActivated,
|
|
249
|
+
isVisible: canBeActivated
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
function getCurrentNotebook(
|
|
253
|
+
args: ReadonlyPartialJSONObject
|
|
254
|
+
): NotebookPanel | undefined | null {
|
|
255
|
+
let current: NotebookPanel | undefined | null;
|
|
256
|
+
if (typeof args['notebookpath'] !== 'string') {
|
|
257
|
+
current = notebookTracker.currentWidget;
|
|
258
|
+
if (!current) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
const path: string = args['notebookpath'];
|
|
263
|
+
current = docManager.findWidget(
|
|
264
|
+
path,
|
|
265
|
+
'Notebook' /* may be needs adjustment later*/
|
|
266
|
+
) as unknown as NotebookPanel;
|
|
267
|
+
if (!current) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return current;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function moveWidgets(args: ReadonlyPartialJSONObject, delta: number) {
|
|
275
|
+
const current = getCurrentNotebook(args);
|
|
276
|
+
if (!current) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const currentSplitView = current as SplitViewNotebookPanel;
|
|
280
|
+
if (currentSplitView.appletViewWidget) {
|
|
281
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
282
|
+
const cellid = args.cellid as string;
|
|
283
|
+
const widgetid = args.widgetid as string;
|
|
284
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
285
|
+
if (typeof appid !== 'undefined') {
|
|
286
|
+
outputarea.movePart(appid, cellid, delta);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function moveWidgetsApp(args: ReadonlyPartialJSONObject, delta: number) {
|
|
292
|
+
const current = getCurrentNotebook(args);
|
|
293
|
+
if (!current) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const currentSplitView = current as SplitViewNotebookPanel;
|
|
297
|
+
if (currentSplitView.appletViewWidget) {
|
|
298
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
299
|
+
const cellid = args.cellid as string;
|
|
300
|
+
const widgetid = args.widgetid as string;
|
|
301
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
302
|
+
if (typeof appid !== 'undefined') {
|
|
303
|
+
outputarea.moveApp(appid, cellid, delta);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
function canMoveWidgetsApp(
|
|
310
|
+
args: ReadonlyPartialJSONObject,
|
|
311
|
+
delta: number
|
|
312
|
+
): boolean {
|
|
313
|
+
const current = getCurrentNotebook(args);
|
|
314
|
+
if (!current) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
const currentSplitView = current as Private.SplitViewNotebookPanel;
|
|
318
|
+
if (currentSplitView.appletViewWidget) {
|
|
319
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
320
|
+
const cellid = args.cellid as string;
|
|
321
|
+
const widgetid = args.widgetid as string;
|
|
322
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
323
|
+
if (typeof appid !== 'undefined') {
|
|
324
|
+
return outputarea.canMoveApp(appid, cellid, delta);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
*/
|
|
330
|
+
commands.addCommand(moveViewUpID, {
|
|
331
|
+
label: /* trans.__(*/ 'Move view up' /*)*/,
|
|
332
|
+
execute: async args => {
|
|
333
|
+
moveWidgets(args, -1);
|
|
334
|
+
},
|
|
335
|
+
icon: args => (args.toolbar ? moveUpIcon : undefined),
|
|
336
|
+
isEnabled: () => true,
|
|
337
|
+
isVisible: () => true
|
|
338
|
+
});
|
|
339
|
+
commands.addCommand(moveViewDownID, {
|
|
340
|
+
label: /* trans.__(*/ 'Move view down' /*)*/,
|
|
341
|
+
execute: async args => {
|
|
342
|
+
moveWidgets(args, 1);
|
|
343
|
+
},
|
|
344
|
+
icon: args => (args.toolbar ? moveDownIcon : undefined),
|
|
345
|
+
isEnabled: () => true,
|
|
346
|
+
isVisible: () => true
|
|
347
|
+
});
|
|
348
|
+
commands.addCommand(moveViewAppUpID, {
|
|
349
|
+
label: /* trans.__(*/ 'Move view up to other app' /*)*/,
|
|
350
|
+
execute: async args => {
|
|
351
|
+
moveWidgetsApp(args, -1);
|
|
352
|
+
},
|
|
353
|
+
icon: args => (args.toolbar ? caretUpIcon : undefined),
|
|
354
|
+
isEnabled: () => true,
|
|
355
|
+
/* isEnabled: args => {
|
|
356
|
+
return canMoveWidgetsApp(args, -1);
|
|
357
|
+
},*/
|
|
358
|
+
isVisible: () => true
|
|
359
|
+
});
|
|
360
|
+
commands.addCommand(moveViewAppDownID, {
|
|
361
|
+
label: /* trans.__(*/ 'Move view down to other app' /*)*/,
|
|
362
|
+
execute: async args => {
|
|
363
|
+
moveWidgetsApp(args, 1);
|
|
364
|
+
},
|
|
365
|
+
icon: args => (args.toolbar ? caretDownIcon : undefined),
|
|
366
|
+
isEnabled: () => true,
|
|
367
|
+
/*isEnabled: args => {
|
|
368
|
+
return canMoveWidgetsApp(args, 1);
|
|
369
|
+
},*/
|
|
370
|
+
isVisible: () => true
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
commands.addCommand(deleteViewID, {
|
|
374
|
+
label: /* trans.__(*/ 'Delete view' /*)*/,
|
|
375
|
+
execute: async args => {
|
|
376
|
+
const current = getCurrentNotebook(args);
|
|
377
|
+
if (!current) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const currentSplitView = current as SplitViewNotebookPanel;
|
|
381
|
+
if (currentSplitView.appletViewWidget) {
|
|
382
|
+
const outputarea = currentSplitView.appletViewWidget;
|
|
383
|
+
const cellid = args.cellid as string;
|
|
384
|
+
const widgetid = args.widgetid as string;
|
|
385
|
+
const appid = outputarea.getWidgetAppId(widgetid);
|
|
386
|
+
if (typeof appid !== 'undefined') {
|
|
387
|
+
outputarea.deletePart(appid, cellid);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
icon: args => (args.toolbar ? deleteIcon : undefined),
|
|
392
|
+
isEnabled: () => true,
|
|
393
|
+
isVisible: () => true
|
|
394
|
+
});
|
|
395
|
+
}
|