@theia/notebook 1.61.0 → 1.62.0
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/browser/contributions/notebook-actions-contribution.d.ts +2 -2
- package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.js +6 -8
- package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +2 -2
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +11 -12
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
- package/lib/browser/notebook-frontend-module.js +2 -0
- package/lib/browser/notebook-frontend-module.js.map +1 -1
- package/lib/browser/service/notebook-cell-editor-service.d.ts +5 -1
- package/lib/browser/service/notebook-cell-editor-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-cell-editor-service.js +12 -0
- package/lib/browser/service/notebook-cell-editor-service.js.map +1 -1
- package/lib/browser/service/notebook-context-manager.d.ts +1 -3
- package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
- package/lib/browser/service/notebook-context-manager.js +0 -10
- package/lib/browser/service/notebook-context-manager.js.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts +2 -2
- package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.js +15 -8
- package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +4 -2
- package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar-factory.js +30 -19
- package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar.d.ts +1 -2
- package/lib/browser/view/notebook-cell-toolbar.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-toolbar.js +2 -4
- package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
- package/lib/browser/view/notebook-main-toolbar.d.ts +3 -4
- package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
- package/lib/browser/view/notebook-main-toolbar.js +22 -45
- package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
- package/package.json +8 -8
- package/src/browser/contributions/notebook-actions-contribution.ts +7 -9
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +14 -14
- package/src/browser/notebook-frontend-module.ts +2 -0
- package/src/browser/service/notebook-cell-editor-service.ts +13 -1
- package/src/browser/service/notebook-context-manager.ts +1 -15
- package/src/browser/view/notebook-cell-list-view.tsx +25 -18
- package/src/browser/view/notebook-cell-toolbar-factory.tsx +35 -23
- package/src/browser/view/notebook-cell-toolbar.tsx +3 -6
- package/src/browser/view/notebook-main-toolbar.tsx +23 -49
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { Command, CommandContribution, CommandHandler, CommandRegistry,
|
|
17
|
+
import { Command, CommandContribution, CommandHandler, CommandRegistry, MenuContribution, MenuModelRegistry, nls } from '@theia/core';
|
|
18
18
|
import { codicon, Key, KeybindingContribution, KeybindingRegistry, KeyCode, KeyModifier } from '@theia/core/lib/browser';
|
|
19
19
|
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
20
20
|
import { NotebookModel } from '../view-model/notebook-model';
|
|
@@ -234,15 +234,17 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
234
234
|
NotebookCellActionContribution.ADDITIONAL_ACTION_MENU,
|
|
235
235
|
nls.localizeByDefault('More'),
|
|
236
236
|
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
order: '30'
|
|
237
|
+
sortString: '30',
|
|
238
|
+
icon: codicon('ellipsis')
|
|
240
239
|
}
|
|
241
240
|
);
|
|
242
241
|
|
|
243
|
-
menus.
|
|
242
|
+
menus.registerSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_ACTION_MENU, '');
|
|
244
243
|
// since contributions are adding to an independent submenu we have to manually add it to the more submenu
|
|
245
|
-
menus.
|
|
244
|
+
menus.linkCompoundMenuNode({
|
|
245
|
+
newParentPath: NotebookCellActionContribution.ADDITIONAL_ACTION_MENU,
|
|
246
|
+
submenuPath: NotebookCellActionContribution.CONTRIBUTED_CELL_ACTION_MENU
|
|
247
|
+
});
|
|
246
248
|
|
|
247
249
|
// code cell sidebar menu
|
|
248
250
|
menus.registerMenuAction(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, {
|
|
@@ -259,19 +261,17 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
259
261
|
});
|
|
260
262
|
|
|
261
263
|
// Notebook Cell extra execution options
|
|
262
|
-
menus.
|
|
264
|
+
menus.registerSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU,
|
|
263
265
|
nls.localizeByDefault('More...'),
|
|
264
|
-
{
|
|
266
|
+
{ icon: codicon('chevron-down') });
|
|
265
267
|
// menus.getMenu(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU).addNode(menus.getMenuNode(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU));
|
|
266
268
|
|
|
267
269
|
// code cell output sidebar menu
|
|
268
270
|
menus.registerSubmenu(
|
|
269
271
|
NotebookCellActionContribution.ADDITIONAL_OUTPUT_SIDEBAR_MENU,
|
|
270
272
|
nls.localizeByDefault('More'),
|
|
271
|
-
{
|
|
272
|
-
|
|
273
|
-
role: CompoundMenuNodeRole.Submenu
|
|
274
|
-
});
|
|
273
|
+
{ icon: codicon('ellipsis') }
|
|
274
|
+
);
|
|
275
275
|
menus.registerMenuAction(NotebookCellActionContribution.ADDITIONAL_OUTPUT_SIDEBAR_MENU, {
|
|
276
276
|
commandId: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id,
|
|
277
277
|
label: nls.localizeByDefault('Clear Cell Outputs'),
|
|
@@ -565,8 +565,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
565
565
|
export namespace NotebookCellActionContribution {
|
|
566
566
|
export const ACTION_MENU = ['notebook-cell-actions-menu'];
|
|
567
567
|
export const ADDITIONAL_ACTION_MENU = [...ACTION_MENU, 'more'];
|
|
568
|
-
export const CONTRIBUTED_CELL_ACTION_MENU = 'notebook/cell/title';
|
|
569
|
-
export const CONTRIBUTED_CELL_EXECUTION_MENU = 'notebook/cell/execute';
|
|
568
|
+
export const CONTRIBUTED_CELL_ACTION_MENU = ['notebook/cell/title'];
|
|
569
|
+
export const CONTRIBUTED_CELL_EXECUTION_MENU = ['notebook/cell/execute'];
|
|
570
570
|
export const CODE_CELL_SIDEBAR_MENU = ['code-cell-sidebar-menu'];
|
|
571
571
|
export const OUTPUT_SIDEBAR_MENU = ['code-cell-output-sidebar-menu'];
|
|
572
572
|
export const ADDITIONAL_OUTPUT_SIDEBAR_MENU = [...OUTPUT_SIDEBAR_MENU, 'more'];
|
|
@@ -53,6 +53,7 @@ import { NotebookStatusBarContribution } from './contributions/notebook-status-b
|
|
|
53
53
|
import { NotebookCellEditorService } from './service/notebook-cell-editor-service';
|
|
54
54
|
import { NotebookCellStatusBarService } from './service/notebook-cell-status-bar-service';
|
|
55
55
|
import { MonacoEditorModelFilter } from '@theia/monaco/lib/browser/monaco-text-model-service';
|
|
56
|
+
import { ActiveMonacoEditorContribution } from '@theia/monaco/lib/browser/monaco-editor-service';
|
|
56
57
|
|
|
57
58
|
export default new ContainerModule(bind => {
|
|
58
59
|
bind(NotebookColorContribution).toSelf().inSingletonScope();
|
|
@@ -76,6 +77,7 @@ export default new ContainerModule(bind => {
|
|
|
76
77
|
bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();
|
|
77
78
|
bind(NotebookClipboardService).toSelf().inSingletonScope();
|
|
78
79
|
bind(NotebookCellEditorService).toSelf().inSingletonScope();
|
|
80
|
+
bind(ActiveMonacoEditorContribution).toService(NotebookCellEditorService);
|
|
79
81
|
bind(NotebookCellStatusBarService).toSelf().inSingletonScope();
|
|
80
82
|
|
|
81
83
|
bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
|
|
@@ -19,13 +19,18 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'
|
|
|
19
19
|
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
|
|
20
20
|
import { NotebookEditorWidgetService } from './notebook-editor-widget-service';
|
|
21
21
|
import { CellUri } from '../../common';
|
|
22
|
+
import { ActiveMonacoEditorContribution, MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
|
|
23
|
+
import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
|
|
22
24
|
|
|
23
25
|
@injectable()
|
|
24
|
-
export class NotebookCellEditorService {
|
|
26
|
+
export class NotebookCellEditorService implements ActiveMonacoEditorContribution {
|
|
25
27
|
|
|
26
28
|
@inject(NotebookEditorWidgetService)
|
|
27
29
|
protected readonly notebookEditorWidgetService: NotebookEditorWidgetService;
|
|
28
30
|
|
|
31
|
+
@inject(MonacoEditorService)
|
|
32
|
+
protected readonly monacoEditorService: MonacoEditorService;
|
|
33
|
+
|
|
29
34
|
protected onDidChangeCellEditorsEmitter = new Emitter<void>();
|
|
30
35
|
readonly onDidChangeCellEditors = this.onDidChangeCellEditorsEmitter.event;
|
|
31
36
|
|
|
@@ -42,6 +47,8 @@ export class NotebookCellEditorService {
|
|
|
42
47
|
// if defocus notebook editor or another notebook editor is focused, clear the active cell
|
|
43
48
|
if (!editor || (this.currentActiveCell && CellUri.parse(this.currentActiveCell.uri)?.notebook.toString() !== editor?.model?.uri.toString())) {
|
|
44
49
|
this.currentActiveCell = undefined;
|
|
50
|
+
// eslint-disable-next-line no-null/no-null
|
|
51
|
+
this.monacoEditorService.setActiveCodeEditor(null);
|
|
45
52
|
this.onDidChangeFocusedCellEditorEmitter.fire(undefined);
|
|
46
53
|
}
|
|
47
54
|
});
|
|
@@ -64,6 +71,7 @@ export class NotebookCellEditorService {
|
|
|
64
71
|
editorFocusChanged(editor?: SimpleMonacoEditor): void {
|
|
65
72
|
if (editor) {
|
|
66
73
|
this.currentActiveCell = editor;
|
|
74
|
+
this.monacoEditorService.setActiveCodeEditor(editor.getControl());
|
|
67
75
|
this.onDidChangeFocusedCellEditorEmitter.fire(editor);
|
|
68
76
|
}
|
|
69
77
|
}
|
|
@@ -71,4 +79,8 @@ export class NotebookCellEditorService {
|
|
|
71
79
|
getActiveCell(): SimpleMonacoEditor | undefined {
|
|
72
80
|
return this.currentActiveCell;
|
|
73
81
|
}
|
|
82
|
+
|
|
83
|
+
getActiveEditor(): ICodeEditor | undefined {
|
|
84
|
+
return this.getActiveCell()?.getControl();
|
|
85
|
+
}
|
|
74
86
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
18
|
import { ContextKeyChangeEvent, ContextKeyService, ContextMatcher, ScopedValueStore } from '@theia/core/lib/browser/context-key-service';
|
|
19
|
-
import { DisposableCollection
|
|
19
|
+
import { DisposableCollection } from '@theia/core';
|
|
20
20
|
import { NotebookKernelService } from './notebook-kernel-service';
|
|
21
21
|
import {
|
|
22
22
|
NOTEBOOK_CELL_EDITABLE,
|
|
@@ -43,9 +43,6 @@ export class NotebookContextManager {
|
|
|
43
43
|
|
|
44
44
|
protected readonly toDispose = new DisposableCollection();
|
|
45
45
|
|
|
46
|
-
protected readonly onDidChangeContextEmitter = new Emitter<ContextKeyChangeEvent>();
|
|
47
|
-
readonly onDidChangeContext = this.onDidChangeContextEmitter.event;
|
|
48
|
-
|
|
49
46
|
protected _context?: HTMLElement;
|
|
50
47
|
|
|
51
48
|
scopedStore: ScopedValueStore;
|
|
@@ -72,14 +69,12 @@ export class NotebookContextManager {
|
|
|
72
69
|
if (e.notebook.toString() === widget?.getResourceUri()?.toString()) {
|
|
73
70
|
this.scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!e.newKernel);
|
|
74
71
|
this.scopedStore.setContext(NOTEBOOK_KERNEL, e.newKernel);
|
|
75
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL]));
|
|
76
72
|
}
|
|
77
73
|
}));
|
|
78
74
|
|
|
79
75
|
widget.model?.onDidChangeContent(events => {
|
|
80
76
|
if (events.some(e => e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Output)) {
|
|
81
77
|
this.scopedStore.setContext(NOTEBOOK_HAS_OUTPUTS, widget.model?.cells.some(cell => cell.outputs.length > 0));
|
|
82
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_HAS_OUTPUTS]));
|
|
83
78
|
}
|
|
84
79
|
});
|
|
85
80
|
|
|
@@ -91,23 +86,18 @@ export class NotebookContextManager {
|
|
|
91
86
|
widget.model?.onDidChangeSelectedCell(e => {
|
|
92
87
|
this.selectedCellChanged(e.cell);
|
|
93
88
|
this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!e);
|
|
94
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_FOCUSED]));
|
|
95
89
|
});
|
|
96
90
|
|
|
97
91
|
this.toDispose.push(this.executionStateService.onDidChangeExecution(e => {
|
|
98
92
|
if (e.notebook.toString() === widget.model?.uri.toString()) {
|
|
99
93
|
this.setCellContext(e.cellHandle, NOTEBOOK_CELL_EXECUTING, !!e.changed);
|
|
100
94
|
this.setCellContext(e.cellHandle, NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle');
|
|
101
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE]));
|
|
102
95
|
}
|
|
103
96
|
}));
|
|
104
97
|
|
|
105
98
|
widget.onDidChangeOutputInputFocus(focus => {
|
|
106
99
|
this.scopedStore.setContext(NOTEBOOK_OUTPUT_INPUT_FOCUSED, focus);
|
|
107
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_OUTPUT_INPUT_FOCUSED]));
|
|
108
100
|
});
|
|
109
|
-
|
|
110
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL]));
|
|
111
101
|
}
|
|
112
102
|
|
|
113
103
|
protected cellDisposables = new DisposableCollection();
|
|
@@ -123,12 +113,8 @@ export class NotebookContextManager {
|
|
|
123
113
|
this.cellDisposables.push(cell.onDidRequestCellEditChange(cellEdit => {
|
|
124
114
|
this.scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit);
|
|
125
115
|
this.scopedStore.setContext(NOTEBOOK_CELL_EDITABLE, cell.cellKind === CellKind.Markup && !cellEdit);
|
|
126
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_MARKDOWN_EDIT_MODE]));
|
|
127
116
|
}));
|
|
128
117
|
}
|
|
129
|
-
|
|
130
|
-
this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_TYPE]));
|
|
131
|
-
|
|
132
118
|
}
|
|
133
119
|
|
|
134
120
|
protected setCellContext(cellHandle: number, key: string, value: unknown): void {
|
|
@@ -19,7 +19,7 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model';
|
|
|
19
19
|
import { NotebookModel } from '../view-model/notebook-model';
|
|
20
20
|
import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory';
|
|
21
21
|
import { animationFrame, onDomEvent } from '@theia/core/lib/browser';
|
|
22
|
-
import { CommandRegistry, DisposableCollection, MenuModelRegistry,
|
|
22
|
+
import { CommandMenu, CommandRegistry, DisposableCollection, MenuModelRegistry, nls } from '@theia/core';
|
|
23
23
|
import { NotebookCommands, NotebookMenus } from '../contributions/notebook-actions-contribution';
|
|
24
24
|
import { NotebookCellActionContribution } from '../contributions/notebook-cell-actions-contribution';
|
|
25
25
|
import { NotebookContextManager } from '../service/notebook-context-manager';
|
|
@@ -126,7 +126,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
126
126
|
<NotebookCellDivider
|
|
127
127
|
menuRegistry={this.props.menuRegistry}
|
|
128
128
|
isVisible={() => this.isEnabled()}
|
|
129
|
-
onAddNewCell={
|
|
129
|
+
onAddNewCell={handler => this.onAddNewCell(handler, index)}
|
|
130
130
|
onDrop={e => this.onDrop(e, index)}
|
|
131
131
|
onDragOver={e => this.onDragOver(e, cell, 'top')} />
|
|
132
132
|
<CellDropIndicator visible={this.shouldRenderDragOverIndicator(cell, 'top')} />
|
|
@@ -173,7 +173,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
173
173
|
<NotebookCellDivider
|
|
174
174
|
menuRegistry={this.props.menuRegistry}
|
|
175
175
|
isVisible={() => this.isEnabled()}
|
|
176
|
-
onAddNewCell={
|
|
176
|
+
onAddNewCell={handler => this.onAddNewCell(handler, this.props.notebookModel.cells.length)}
|
|
177
177
|
onDrop={e => this.onDrop(e, this.props.notebookModel.cells.length - 1)}
|
|
178
178
|
onDragOver={e => this.onDragOver(e, this.props.notebookModel.cells[this.props.notebookModel.cells.length - 1], 'bottom')} />
|
|
179
179
|
</ul>;
|
|
@@ -255,10 +255,10 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
255
255
|
this.setState({ ...this.state, dragOverIndicator: undefined });
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
protected onAddNewCell(
|
|
258
|
+
protected onAddNewCell(handler: (...args: unknown[]) => void, index: number): void {
|
|
259
259
|
if (this.isEnabled()) {
|
|
260
260
|
this.props.commandRegistry.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, index - 1);
|
|
261
|
-
|
|
261
|
+
handler(
|
|
262
262
|
this.props.notebookModel,
|
|
263
263
|
index
|
|
264
264
|
);
|
|
@@ -276,7 +276,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
276
276
|
|
|
277
277
|
export interface NotebookCellDividerProps {
|
|
278
278
|
isVisible: () => boolean;
|
|
279
|
-
onAddNewCell: (
|
|
279
|
+
onAddNewCell: (createCommand: (...args: unknown[]) => void) => void;
|
|
280
280
|
onDrop: (event: React.DragEvent<HTMLLIElement>) => void;
|
|
281
281
|
onDragOver: (event: React.DragEvent<HTMLLIElement>) => void;
|
|
282
282
|
menuRegistry: MenuModelRegistry;
|
|
@@ -286,21 +286,28 @@ export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOve
|
|
|
286
286
|
const [hover, setHover] = React.useState(false);
|
|
287
287
|
|
|
288
288
|
const menuPath = NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP;
|
|
289
|
-
const menuItems = menuRegistry.
|
|
290
|
-
|
|
291
|
-
const renderItem = (item:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
<
|
|
298
|
-
|
|
299
|
-
|
|
289
|
+
const menuItems: CommandMenu[] = menuRegistry.getMenu(menuPath).children.filter(item => CommandMenu.is(item)).map(item => item as CommandMenu);
|
|
290
|
+
|
|
291
|
+
const renderItem = (item: CommandMenu): React.ReactNode => {
|
|
292
|
+
const execute = (...args: unknown[]) => {
|
|
293
|
+
if (CommandMenu.is(item)) {
|
|
294
|
+
item.run([...menuPath, item.id], ...args);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
return <button
|
|
298
|
+
key={item.id}
|
|
299
|
+
className='theia-notebook-add-cell-button'
|
|
300
|
+
onClick={() => onAddNewCell(execute)}
|
|
301
|
+
title={nls.localizeByDefault(`Add ${item.label} Cell`)}
|
|
302
|
+
>
|
|
303
|
+
<div className={item.icon + ' theia-notebook-add-cell-button-icon'} />
|
|
304
|
+
<div className='theia-notebook-add-cell-button-text'>{item.label}</div>
|
|
305
|
+
</button>;
|
|
306
|
+
};
|
|
300
307
|
|
|
301
308
|
return <li className='theia-notebook-cell-divider' onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} onDrop={onDrop} onDragOver={onDragOver}>
|
|
302
309
|
{hover && isVisible() && <div className='theia-notebook-add-cell-buttons'>
|
|
303
|
-
{menuItems.map((item:
|
|
310
|
+
{menuItems.map((item: CommandMenu) => renderItem(item))}
|
|
304
311
|
</div>}
|
|
305
312
|
</li>;
|
|
306
313
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
|
-
import { CommandRegistry,
|
|
18
|
+
import { CommandMenu, CommandRegistry, CompoundMenuNode, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuPath, RenderedMenuNode } from '@theia/core';
|
|
19
19
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
20
20
|
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
21
21
|
import { NotebookCellSidebar, NotebookCellToolbar } from './notebook-cell-toolbar';
|
|
@@ -29,7 +29,6 @@ export interface NotebookCellToolbarItem {
|
|
|
29
29
|
label?: string;
|
|
30
30
|
onClick: (e: React.MouseEvent) => void;
|
|
31
31
|
isVisible: () => boolean;
|
|
32
|
-
contextKeys?: Set<string>
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
export interface toolbarItemOptions {
|
|
@@ -55,48 +54,61 @@ export class NotebookCellToolbarFactory {
|
|
|
55
54
|
@inject(NotebookContextManager)
|
|
56
55
|
protected readonly notebookContextManager: NotebookContextManager;
|
|
57
56
|
|
|
57
|
+
protected readonly onDidChangeContextEmitter = new Emitter<void>;
|
|
58
|
+
readonly onDidChangeContext: Event<void> = this.onDidChangeContextEmitter.event;
|
|
59
|
+
|
|
60
|
+
protected toDisposeOnRender = new DisposableCollection();
|
|
61
|
+
|
|
58
62
|
renderCellToolbar(menuPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): React.ReactNode {
|
|
59
63
|
return <NotebookCellToolbar getMenuItems={() => this.getMenuItems(menuPath, cell, itemOptions)}
|
|
60
|
-
|
|
64
|
+
onContextChanged={this.onDidChangeContext} />;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
renderSidebar(menuPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): React.ReactNode {
|
|
64
68
|
return <NotebookCellSidebar getMenuItems={() => this.getMenuItems(menuPath, cell, itemOptions)}
|
|
65
|
-
|
|
69
|
+
onContextChanged={this.onDidChangeContext} />;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
private getMenuItems(menuItemPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): NotebookCellToolbarItem[] {
|
|
73
|
+
this.toDisposeOnRender.dispose();
|
|
74
|
+
this.toDisposeOnRender = new DisposableCollection();
|
|
69
75
|
const inlineItems: NotebookCellToolbarItem[] = [];
|
|
70
76
|
for (const menuNode of this.menuRegistry.getMenu(menuItemPath).children) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
|
|
78
|
+
const itemPath = [...menuItemPath, menuNode.id];
|
|
79
|
+
if (menuNode.isVisible(itemPath, this.notebookContextManager.getCellContext(cell.handle), this.notebookContextManager.context, itemOptions.commandArgs?.() ?? [])) {
|
|
80
|
+
if (RenderedMenuNode.is(menuNode)) {
|
|
81
|
+
if (menuNode.onDidChange) {
|
|
82
|
+
this.toDisposeOnRender.push(menuNode.onDidChange(() => this.onDidChangeContextEmitter.fire()));
|
|
83
|
+
}
|
|
84
|
+
inlineItems.push(this.createToolbarItem(itemPath, menuNode, itemOptions));
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
88
|
return inlineItems;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
private createToolbarItem(menuNode:
|
|
83
|
-
const menuPath = menuNode.role === CompoundMenuNodeRole.Submenu ? this.menuRegistry.getPath(menuNode) : undefined;
|
|
91
|
+
private createToolbarItem(menuPath: MenuPath, menuNode: RenderedMenuNode, itemOptions: toolbarItemOptions): NotebookCellToolbarItem {
|
|
84
92
|
return {
|
|
85
93
|
id: menuNode.id,
|
|
86
94
|
icon: menuNode.icon,
|
|
87
95
|
label: menuNode.label,
|
|
88
|
-
onClick:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
onClick: e => {
|
|
97
|
+
if (CompoundMenuNode.is(menuNode)) {
|
|
98
|
+
this.contextMenuRenderer.render(
|
|
99
|
+
{
|
|
100
|
+
anchor: e.nativeEvent,
|
|
101
|
+
menuPath: menuPath,
|
|
102
|
+
menu: menuNode,
|
|
103
|
+
includeAnchorArg: false,
|
|
104
|
+
args: itemOptions.contextMenuArgs?.(),
|
|
105
|
+
context: this.notebookContextManager.context || (e.currentTarget as HTMLElement)
|
|
106
|
+
});
|
|
107
|
+
} else if (CommandMenu.is(menuNode)) {
|
|
108
|
+
menuNode.run(menuPath, ...(itemOptions.commandArgs?.() ?? []));
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
isVisible: () => true
|
|
100
112
|
};
|
|
101
113
|
}
|
|
102
114
|
}
|
|
@@ -17,11 +17,10 @@ import * as React from '@theia/core/shared/react';
|
|
|
17
17
|
import { ACTION_ITEM } from '@theia/core/lib/browser';
|
|
18
18
|
import { NotebookCellToolbarItem } from './notebook-cell-toolbar-factory';
|
|
19
19
|
import { DisposableCollection, Event } from '@theia/core';
|
|
20
|
-
import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service';
|
|
21
20
|
|
|
22
21
|
export interface NotebookCellToolbarProps {
|
|
23
22
|
getMenuItems: () => NotebookCellToolbarItem[];
|
|
24
|
-
|
|
23
|
+
onContextChanged: Event<void>;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
interface NotebookCellToolbarState {
|
|
@@ -34,11 +33,9 @@ abstract class NotebookCellActionBar extends React.Component<NotebookCellToolbar
|
|
|
34
33
|
|
|
35
34
|
constructor(props: NotebookCellToolbarProps) {
|
|
36
35
|
super(props);
|
|
37
|
-
this.toDispose.push(props.
|
|
36
|
+
this.toDispose.push(props.onContextChanged(e => {
|
|
38
37
|
const menuItems = this.props.getMenuItems();
|
|
39
|
-
|
|
40
|
-
this.setState({ inlineItems: menuItems });
|
|
41
|
-
}
|
|
38
|
+
this.setState({ inlineItems: menuItems });
|
|
42
39
|
}));
|
|
43
40
|
this.state = { inlineItems: this.props.getMenuItems() };
|
|
44
41
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
|
-
import { ArrayUtils,
|
|
16
|
+
import { ArrayUtils, CommandMenu, CommandRegistry, DisposableCollection, Group, GroupImpl, MenuModelRegistry, MenuNode, MenuPath, nls } from '@theia/core';
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
18
|
import { codicon, ContextMenuRenderer } from '@theia/core/lib/browser';
|
|
19
19
|
import { NotebookCommands, NotebookMenus } from '../contributions/notebook-actions-contribution';
|
|
@@ -21,7 +21,6 @@ import { NotebookModel } from '../view-model/notebook-model';
|
|
|
21
21
|
import { NotebookKernelService } from '../service/notebook-kernel-service';
|
|
22
22
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
23
23
|
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
24
|
-
import { NotebookCommand } from '../../common';
|
|
25
24
|
import { NotebookContextManager } from '../service/notebook-context-manager';
|
|
26
25
|
|
|
27
26
|
export interface NotebookMainToolbarProps {
|
|
@@ -97,19 +96,12 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
97
96
|
}));
|
|
98
97
|
|
|
99
98
|
// TODO maybe we need a mechanism to check for changes in the menu to update this toolbar
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.forceUpdate();
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
props.contextKeyService.onDidChange(e => {
|
|
108
|
-
if (e.affects(contextKeys)) {
|
|
109
|
-
this.forceUpdate();
|
|
99
|
+
const menuItems = this.getMenuItems();
|
|
100
|
+
for (const item of menuItems) {
|
|
101
|
+
if (item.onDidChange) {
|
|
102
|
+
item.onDidChange(() => this.forceUpdate());
|
|
110
103
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
104
|
+
}
|
|
113
105
|
}
|
|
114
106
|
|
|
115
107
|
override componentWillUnmount(): void {
|
|
@@ -137,14 +129,16 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
137
129
|
|
|
138
130
|
protected renderContextMenu(event: MouseEvent, menuItems: readonly MenuNode[]): void {
|
|
139
131
|
const hiddenItems = menuItems.slice(menuItems.length - this.calculateNumberOfHiddenItems(menuItems));
|
|
140
|
-
const contextMenu = this.props.menuRegistry.getMenu([NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU]);
|
|
141
132
|
|
|
142
|
-
|
|
143
|
-
|
|
133
|
+
const menu = new GroupImpl(NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU[0]);
|
|
134
|
+
|
|
135
|
+
hiddenItems.forEach(item => menu.addNode(item));
|
|
144
136
|
|
|
145
137
|
this.props.contextMenuRenderer.render({
|
|
146
138
|
anchor: event,
|
|
147
|
-
menuPath:
|
|
139
|
+
menuPath: NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU,
|
|
140
|
+
menu: menu,
|
|
141
|
+
contextKeyService: this.props.contextKeyService,
|
|
148
142
|
context: this.props.editorNode,
|
|
149
143
|
args: [this.props.notebookModel.uri]
|
|
150
144
|
});
|
|
@@ -153,7 +147,7 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
153
147
|
override render(): React.ReactNode {
|
|
154
148
|
const menuItems = this.getMenuItems();
|
|
155
149
|
return <div className='theia-notebook-main-toolbar' id='notebook-main-toolbar'>
|
|
156
|
-
{menuItems.slice(0, menuItems.length - this.calculateNumberOfHiddenItems(menuItems)).map(item => this.renderMenuItem(item))}
|
|
150
|
+
{menuItems.slice(0, menuItems.length - this.calculateNumberOfHiddenItems(menuItems)).map(item => this.renderMenuItem(NotebookMenus.NOTEBOOK_MAIN_TOOLBAR, item))}
|
|
157
151
|
{
|
|
158
152
|
this.state.numberOfHiddenItems > 0 &&
|
|
159
153
|
<span className={`${codicon('ellipsis')} action-label theia-notebook-main-toolbar-item`} onClick={e => this.renderContextMenu(e.nativeEvent, menuItems)} />
|
|
@@ -180,51 +174,31 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
|
|
|
180
174
|
}
|
|
181
175
|
}
|
|
182
176
|
|
|
183
|
-
protected renderMenuItem(item: MenuNode, submenu?: string): React.ReactNode {
|
|
184
|
-
if (item
|
|
185
|
-
const itemNodes = ArrayUtils.coalesce(item.children?.map(child => this.renderMenuItem(child, item.id)) ?? []);
|
|
177
|
+
protected renderMenuItem<T>(itemPath: MenuPath, item: MenuNode, submenu?: string): React.ReactNode {
|
|
178
|
+
if (Group.is(item)) {
|
|
179
|
+
const itemNodes = ArrayUtils.coalesce(item.children?.map(child => this.renderMenuItem([...itemPath, child.id], child, item.id)) ?? []);
|
|
186
180
|
return <React.Fragment key={item.id}>
|
|
187
181
|
{itemNodes}
|
|
188
182
|
{itemNodes && itemNodes.length > 0 && <span key={`${item.id}-separator`} className='theia-notebook-toolbar-separator'></span>}
|
|
189
183
|
</React.Fragment>;
|
|
190
|
-
} else if ((this.nativeSubmenus.includes(submenu ?? '')) ||
|
|
191
|
-
|
|
192
|
-
if (!visibleCommand) {
|
|
193
|
-
return undefined;
|
|
194
|
-
}
|
|
195
|
-
const command = this.props.commandRegistry.getCommand(item.command ?? '') as NotebookCommand | undefined;
|
|
196
|
-
const label = command?.shortTitle ?? item.label;
|
|
197
|
-
const title = command?.tooltip ?? item.label;
|
|
198
|
-
return <div key={item.id} id={item.id} title={title} className={`theia-notebook-main-toolbar-item action-label${this.getAdditionalClasses(item)}`}
|
|
184
|
+
} else if (CommandMenu.is(item) && ((this.nativeSubmenus.includes(submenu ?? '')) || item.isVisible(itemPath, this.props.contextKeyService, this.props.editorNode))) {
|
|
185
|
+
return <div key={item.id} id={item.id} title={item.label} className={`theia-notebook-main-toolbar-item action-label${this.getAdditionalClasses(itemPath, item)}`}
|
|
199
186
|
onClick={() => {
|
|
200
|
-
|
|
201
|
-
this.props.commandRegistry.executeCommand(item.command, this.props.notebookModel.uri);
|
|
202
|
-
}
|
|
187
|
+
item.run(itemPath, this.props.notebookModel.uri);
|
|
203
188
|
}}>
|
|
204
189
|
<span className={item.icon} />
|
|
205
|
-
<span className='theia-notebook-main-toolbar-item-text'>{label}</span>
|
|
190
|
+
<span className='theia-notebook-main-toolbar-item-text'>{item.label}</span>
|
|
206
191
|
</div>;
|
|
207
192
|
}
|
|
208
193
|
return undefined;
|
|
209
194
|
}
|
|
210
195
|
|
|
211
196
|
protected getMenuItems(): readonly MenuNode[] {
|
|
212
|
-
|
|
213
|
-
const pluginCommands = this.props.menuRegistry.getMenuNode(menuPath).children;
|
|
214
|
-
const theiaCommands = this.props.menuRegistry.getMenu([menuPath]).children;
|
|
215
|
-
return theiaCommands.concat(pluginCommands);
|
|
197
|
+
return this.props.menuRegistry.getMenu(NotebookMenus.NOTEBOOK_MAIN_TOOLBAR).children;
|
|
216
198
|
}
|
|
217
199
|
|
|
218
|
-
protected getAdditionalClasses(item:
|
|
219
|
-
return
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
protected getAllContextKeys(menus: readonly MenuNode[], keySet: Set<string>): void {
|
|
223
|
-
menus.filter(item => item.when)
|
|
224
|
-
.forEach(item => this.props.contextKeyService.parseKeys(item.when!)?.forEach(key => keySet.add(key)));
|
|
225
|
-
|
|
226
|
-
menus.filter(item => item.children && item.children.length > 0)
|
|
227
|
-
.forEach(item => this.getAllContextKeys(item.children!, keySet));
|
|
200
|
+
protected getAdditionalClasses(itemPath: MenuPath, item: CommandMenu): string {
|
|
201
|
+
return item.isEnabled(itemPath, this.props.editorNode) ? '' : ' theia-mod-disabled';
|
|
228
202
|
}
|
|
229
203
|
|
|
230
204
|
protected calculateNumberOfHiddenItems(allMenuItems: readonly MenuNode[]): number {
|