@theia/notebook 1.53.0-next.5 → 1.53.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 +1 -0
- package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.js +26 -3
- package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +2 -0
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +36 -2
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-status-bar-contribution.d.ts +14 -0
- package/lib/browser/contributions/notebook-status-bar-contribution.d.ts.map +1 -0
- package/lib/browser/contributions/notebook-status-bar-contribution.js +75 -0
- package/lib/browser/contributions/notebook-status-bar-contribution.js.map +1 -0
- package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget.js +2 -4
- package/lib/browser/notebook-editor-widget.js.map +1 -1
- package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
- package/lib/browser/notebook-frontend-module.js +3 -0
- package/lib/browser/notebook-frontend-module.js.map +1 -1
- package/lib/browser/view/notebook-cell-editor.d.ts +1 -0
- package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-editor.js +30 -16
- package/lib/browser/view/notebook-cell-editor.js.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts +6 -4
- package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.js +20 -13
- package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.d.ts +3 -0
- package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.js +5 -0
- package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
- package/package.json +8 -8
- package/src/browser/contributions/notebook-actions-contribution.ts +28 -2
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +40 -2
- package/src/browser/contributions/notebook-status-bar-contribution.ts +77 -0
- package/src/browser/notebook-editor-widget.tsx +3 -4
- package/src/browser/notebook-frontend-module.ts +4 -0
- package/src/browser/style/index.css +11 -3
- package/src/browser/view/notebook-cell-editor.tsx +30 -14
- package/src/browser/view/notebook-cell-list-view.tsx +37 -20
- package/src/browser/view-model/notebook-cell-model.ts +7 -0
|
@@ -39,11 +39,13 @@ export namespace NotebookCellCommands {
|
|
|
39
39
|
/** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */
|
|
40
40
|
export const EDIT_COMMAND = Command.toDefaultLocalizedCommand({
|
|
41
41
|
id: 'notebook.cell.edit',
|
|
42
|
+
category: 'Notebook',
|
|
42
43
|
iconClass: codicon('edit')
|
|
43
44
|
});
|
|
44
45
|
/** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */
|
|
45
46
|
export const STOP_EDIT_COMMAND = Command.toDefaultLocalizedCommand({
|
|
46
47
|
id: 'notebook.cell.stop-edit',
|
|
48
|
+
category: 'Notebook',
|
|
47
49
|
iconClass: codicon('check')
|
|
48
50
|
});
|
|
49
51
|
/** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
|
|
@@ -59,11 +61,21 @@ export namespace NotebookCellCommands {
|
|
|
59
61
|
/** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
|
|
60
62
|
export const EXECUTE_SINGLE_CELL_COMMAND = Command.toDefaultLocalizedCommand({
|
|
61
63
|
id: 'notebook.cell.execute-cell',
|
|
64
|
+
category: 'Notebook',
|
|
65
|
+
label: nls.localizeByDefault('Execute Cell'),
|
|
62
66
|
iconClass: codicon('play'),
|
|
63
67
|
});
|
|
64
68
|
/** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
|
|
65
69
|
export const EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND = Command.toDefaultLocalizedCommand({
|
|
66
70
|
id: 'notebook.cell.execute-cell-and-focus-next',
|
|
71
|
+
label: nls.localizeByDefault('Execute Notebook Cell and Select Below'),
|
|
72
|
+
category: 'Notebook',
|
|
73
|
+
});
|
|
74
|
+
/** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
|
|
75
|
+
export const EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND = Command.toDefaultLocalizedCommand({
|
|
76
|
+
id: 'notebook.cell.execute-cell-and-insert-below',
|
|
77
|
+
label: nls.localizeByDefault('Execute Notebook Cell and Insert Below'),
|
|
78
|
+
category: 'Notebook',
|
|
67
79
|
});
|
|
68
80
|
|
|
69
81
|
export const EXECUTE_ABOVE_CELLS_COMMAND = Command.toDefaultLocalizedCommand({
|
|
@@ -85,11 +97,13 @@ export namespace NotebookCellCommands {
|
|
|
85
97
|
/** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */
|
|
86
98
|
export const CLEAR_OUTPUTS_COMMAND = Command.toDefaultLocalizedCommand({
|
|
87
99
|
id: 'notebook.cell.clear-outputs',
|
|
100
|
+
category: 'Notebook',
|
|
88
101
|
label: 'Clear Cell Outputs',
|
|
89
102
|
});
|
|
90
103
|
/** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel | undefined, output: NotebookCellOutputModel */
|
|
91
104
|
export const CHANGE_OUTPUT_PRESENTATION_COMMAND = Command.toDefaultLocalizedCommand({
|
|
92
105
|
id: 'notebook.cell.change-presentation',
|
|
106
|
+
category: 'Notebook',
|
|
93
107
|
label: 'Change Presentation',
|
|
94
108
|
});
|
|
95
109
|
|
|
@@ -114,11 +128,13 @@ export namespace NotebookCellCommands {
|
|
|
114
128
|
|
|
115
129
|
export const TO_CODE_CELL_COMMAND = Command.toLocalizedCommand({
|
|
116
130
|
id: 'notebook.cell.changeToCode',
|
|
131
|
+
category: 'Notebook',
|
|
117
132
|
label: 'Change Cell to Code'
|
|
118
133
|
});
|
|
119
134
|
|
|
120
135
|
export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({
|
|
121
136
|
id: 'notebook.cell.changeToMarkdown',
|
|
137
|
+
category: 'Notebook',
|
|
122
138
|
label: 'Change Cell to Markdown'
|
|
123
139
|
});
|
|
124
140
|
|
|
@@ -302,6 +318,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
302
318
|
}
|
|
303
319
|
})
|
|
304
320
|
);
|
|
321
|
+
commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND, this.editableCellCommandHandler(
|
|
322
|
+
async (notebookModel, cell) => {
|
|
323
|
+
if (cell.cellKind === CellKind.Code) {
|
|
324
|
+
await commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell);
|
|
325
|
+
}
|
|
326
|
+
await commands.executeCommand(NotebookCellCommands.STOP_EDIT_COMMAND.id, notebookModel, cell);
|
|
327
|
+
|
|
328
|
+
if (cell.cellKind === CellKind.Code) {
|
|
329
|
+
await commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id);
|
|
330
|
+
} else {
|
|
331
|
+
await commands.executeCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND.id);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const index = notebookModel.cells.indexOf(cell);
|
|
335
|
+
notebookModel.setSelectedCell(notebookModel.cells[index + 1]);
|
|
336
|
+
})
|
|
337
|
+
);
|
|
305
338
|
|
|
306
339
|
commands.registerCommand(NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND, this.editableCellCommandHandler(
|
|
307
340
|
(notebookModel, cell) => {
|
|
@@ -432,8 +465,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
432
465
|
},
|
|
433
466
|
{
|
|
434
467
|
command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
|
|
435
|
-
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
|
|
436
|
-
when: `editorTextFocus &&
|
|
468
|
+
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt, KeyModifier.CtrlCmd] }).toString(),
|
|
469
|
+
when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`,
|
|
437
470
|
},
|
|
438
471
|
{
|
|
439
472
|
command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
|
|
@@ -450,6 +483,11 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
450
483
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(),
|
|
451
484
|
when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
452
485
|
},
|
|
486
|
+
{
|
|
487
|
+
command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND.id,
|
|
488
|
+
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
|
|
489
|
+
when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
490
|
+
},
|
|
453
491
|
{
|
|
454
492
|
command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id,
|
|
455
493
|
keybinding: KeyCode.createKeyCode({ first: Key.KEY_O, modifiers: [KeyModifier.Alt] }).toString(),
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser';
|
|
19
|
+
import { Disposable } from '@theia/core/lib/common';
|
|
20
|
+
import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
|
|
21
|
+
import { NotebookEditorWidget } from '../notebook-editor-widget';
|
|
22
|
+
import { nls } from '@theia/core';
|
|
23
|
+
import { NotebookCommands } from './notebook-actions-contribution';
|
|
24
|
+
|
|
25
|
+
export const NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID = 'notebook-cell-selection-position';
|
|
26
|
+
|
|
27
|
+
@injectable()
|
|
28
|
+
export class NotebookStatusBarContribution implements FrontendApplicationContribution {
|
|
29
|
+
|
|
30
|
+
@inject(StatusBar) protected readonly statusBar: StatusBar;
|
|
31
|
+
@inject(NotebookEditorWidgetService) protected readonly editorWidgetService: NotebookEditorWidgetService;
|
|
32
|
+
|
|
33
|
+
protected currentCellSelectionListener: Disposable | undefined;
|
|
34
|
+
protected lastFocusedEditor: NotebookEditorWidget | undefined;
|
|
35
|
+
|
|
36
|
+
onStart(): void {
|
|
37
|
+
this.editorWidgetService.onDidChangeFocusedEditor(editor => {
|
|
38
|
+
this.currentCellSelectionListener?.dispose();
|
|
39
|
+
this.currentCellSelectionListener = editor?.model?.onDidChangeSelectedCell(() =>
|
|
40
|
+
this.updateStatusbar(editor)
|
|
41
|
+
);
|
|
42
|
+
editor?.onDidDispose(() => {
|
|
43
|
+
this.lastFocusedEditor = undefined;
|
|
44
|
+
this.updateStatusbar();
|
|
45
|
+
});
|
|
46
|
+
this.updateStatusbar(editor);
|
|
47
|
+
this.lastFocusedEditor = editor;
|
|
48
|
+
});
|
|
49
|
+
if (this.editorWidgetService.focusedEditor) {
|
|
50
|
+
this.updateStatusbar();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
protected async updateStatusbar(editor?: NotebookEditorWidget): Promise<void> {
|
|
55
|
+
if ((!editor && !this.lastFocusedEditor?.isVisible) || editor?.model?.cells.length === 0) {
|
|
56
|
+
this.statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await editor?.ready;
|
|
61
|
+
if (!editor?.model) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const selectedCellIndex = editor.model.selectedCell ? editor.model.cells.indexOf(editor.model.selectedCell) + 1 : '';
|
|
66
|
+
|
|
67
|
+
this.statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, {
|
|
68
|
+
text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, editor.model.cells.length),
|
|
69
|
+
alignment: StatusBarAlignment.RIGHT,
|
|
70
|
+
priority: 100,
|
|
71
|
+
command: NotebookCommands.CENTER_ACTIVE_CELL.id,
|
|
72
|
+
arguments: [editor]
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
@@ -154,7 +154,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
154
154
|
@postConstruct()
|
|
155
155
|
protected init(): void {
|
|
156
156
|
this.id = NOTEBOOK_EDITOR_ID_PREFIX + this.props.uri.toString();
|
|
157
|
-
this.node.tabIndex = -1;
|
|
158
157
|
|
|
159
158
|
this.scrollOptions = {
|
|
160
159
|
suppressScrollY: true
|
|
@@ -174,8 +173,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
174
173
|
this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]);
|
|
175
174
|
model.setSelectedCell(model.cells[0]);
|
|
176
175
|
}
|
|
177
|
-
model.cells.forEach(cell => cell.onWillBlurCellEditor(() => this.node.focus()));
|
|
178
|
-
model.onDidAddOrRemoveCell(e => e.newCellIds?.forEach(cellId => model.cells.find(cell => cell.handle === cellId)?.onWillBlurCellEditor(() => this.node.focus())));
|
|
179
176
|
});
|
|
180
177
|
}
|
|
181
178
|
|
|
@@ -207,6 +204,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
207
204
|
}
|
|
208
205
|
// Ensure that the model is loaded before adding the editor
|
|
209
206
|
this.notebookEditorService.addNotebookEditor(this);
|
|
207
|
+
this._model.selectedCell = this._model.cells[0];
|
|
210
208
|
this.update();
|
|
211
209
|
this.notebookContextManager.init(this);
|
|
212
210
|
return this._model;
|
|
@@ -267,7 +265,8 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
267
265
|
notebookModel={this._model}
|
|
268
266
|
notebookContext={this.notebookContextManager}
|
|
269
267
|
toolbarRenderer={this.cellToolbarFactory}
|
|
270
|
-
commandRegistry={this.commandRegistry}
|
|
268
|
+
commandRegistry={this.commandRegistry}
|
|
269
|
+
menuRegistry={this.menuRegistry} />
|
|
271
270
|
</PerfectScrollbar>
|
|
272
271
|
</div>
|
|
273
272
|
</div>;
|
|
@@ -47,6 +47,7 @@ import { NotebookClipboardService } from './service/notebook-clipboard-service';
|
|
|
47
47
|
import { bindNotebookPreferences } from './contributions/notebook-preferences';
|
|
48
48
|
import { NotebookOptionsService } from './service/notebook-options';
|
|
49
49
|
import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler';
|
|
50
|
+
import { NotebookStatusBarContribution } from './contributions/notebook-status-bar-contribution';
|
|
50
51
|
|
|
51
52
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
52
53
|
bind(NotebookColorContribution).toSelf().inSingletonScope();
|
|
@@ -112,4 +113,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
|
112
113
|
|
|
113
114
|
bind(NotebookUndoRedoHandler).toSelf().inSingletonScope();
|
|
114
115
|
bind(UndoRedoHandler).toService(NotebookUndoRedoHandler);
|
|
116
|
+
|
|
117
|
+
bind(NotebookStatusBarContribution).toSelf().inSingletonScope();
|
|
118
|
+
bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution);
|
|
115
119
|
});
|
|
@@ -272,10 +272,10 @@
|
|
|
272
272
|
border: 1px solid var(--theia-notebook-cellToolbarSeparator);
|
|
273
273
|
background-color: var(--theia-editor-background);
|
|
274
274
|
color: var(--theia-foreground);
|
|
275
|
-
|
|
276
|
-
text-align: center;
|
|
275
|
+
display: flex;
|
|
277
276
|
height: 24px;
|
|
278
277
|
margin: 0 8px;
|
|
278
|
+
padding: 2px 4px;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
.theia-notebook-add-cell-button:hover {
|
|
@@ -286,10 +286,18 @@
|
|
|
286
286
|
background-color: var(--theia-toolbar-active);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
.theia-notebook-add-cell-button
|
|
289
|
+
.theia-notebook-add-cell-button>* {
|
|
290
290
|
vertical-align: middle;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
.theia-notebook-add-cell-button-icon::before {
|
|
294
|
+
font: normal normal normal 14px/1 codicon;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.theia-notebook-add-cell-button-text {
|
|
298
|
+
margin: 1px 0 0 4px;
|
|
299
|
+
}
|
|
300
|
+
|
|
293
301
|
.theia-notebook-cell-output-webview {
|
|
294
302
|
padding: 5px 0px;
|
|
295
303
|
margin: 0px 15px 0px 9px;
|
|
@@ -101,6 +101,16 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
101
101
|
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount);
|
|
102
102
|
}));
|
|
103
103
|
|
|
104
|
+
this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => {
|
|
105
|
+
let parent = this.container?.parentElement;
|
|
106
|
+
while (parent && !parent.classList.contains('theia-notebook-cell')) {
|
|
107
|
+
parent = parent.parentElement;
|
|
108
|
+
}
|
|
109
|
+
if (parent) {
|
|
110
|
+
parent.focus();
|
|
111
|
+
}
|
|
112
|
+
}));
|
|
113
|
+
|
|
104
114
|
this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => {
|
|
105
115
|
this.editor?.getControl().updateOptions(options);
|
|
106
116
|
}));
|
|
@@ -114,20 +124,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
114
124
|
animationFrame().then(() => this.setMatches());
|
|
115
125
|
}));
|
|
116
126
|
|
|
117
|
-
this.toDispose.push(this.props.cell.onDidSelectFindMatch(match =>
|
|
118
|
-
const editorDomNode = this.editor?.getControl().getDomNode();
|
|
119
|
-
if (editorDomNode) {
|
|
120
|
-
editorDomNode.scrollIntoView({
|
|
121
|
-
behavior: 'instant',
|
|
122
|
-
block: 'center'
|
|
123
|
-
});
|
|
124
|
-
} else {
|
|
125
|
-
this.container?.scrollIntoView({
|
|
126
|
-
behavior: 'instant',
|
|
127
|
-
block: 'center'
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}));
|
|
127
|
+
this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => this.centerEditorInView()));
|
|
131
128
|
|
|
132
129
|
this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
|
|
133
130
|
if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
|
|
@@ -145,6 +142,10 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
145
142
|
});
|
|
146
143
|
this.toDispose.push(disposable);
|
|
147
144
|
}
|
|
145
|
+
|
|
146
|
+
this.toDispose.push(this.props.cell.onDidRequestCenterEditor(() => {
|
|
147
|
+
this.centerEditorInView();
|
|
148
|
+
}));
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
override componentWillUnmount(): void {
|
|
@@ -156,6 +157,21 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
156
157
|
this.toDispose = new DisposableCollection();
|
|
157
158
|
}
|
|
158
159
|
|
|
160
|
+
protected centerEditorInView(): void {
|
|
161
|
+
const editorDomNode = this.editor?.getControl().getDomNode();
|
|
162
|
+
if (editorDomNode) {
|
|
163
|
+
editorDomNode.scrollIntoView({
|
|
164
|
+
behavior: 'instant',
|
|
165
|
+
block: 'center'
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
this.container?.scrollIntoView({
|
|
169
|
+
behavior: 'instant',
|
|
170
|
+
block: 'center'
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
159
175
|
protected async initEditor(): Promise<void> {
|
|
160
176
|
const { cell, notebookModel, monacoServices } = this.props;
|
|
161
177
|
if (this.container) {
|
|
@@ -18,9 +18,9 @@ import { CellEditType, CellKind, NotebookCellsChangeType } from '../../common';
|
|
|
18
18
|
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
|
-
import { animationFrame,
|
|
22
|
-
import { CommandRegistry, DisposableCollection, nls } from '@theia/core';
|
|
23
|
-
import { NotebookCommands } from '../contributions/notebook-actions-contribution';
|
|
21
|
+
import { animationFrame, onDomEvent } from '@theia/core/lib/browser';
|
|
22
|
+
import { CommandRegistry, DisposableCollection, MenuModelRegistry, MenuNode, nls } from '@theia/core';
|
|
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';
|
|
26
26
|
|
|
@@ -34,7 +34,8 @@ interface CellListProps {
|
|
|
34
34
|
notebookModel: NotebookModel;
|
|
35
35
|
notebookContext: NotebookContextManager;
|
|
36
36
|
toolbarRenderer: NotebookCellToolbarFactory;
|
|
37
|
-
commandRegistry: CommandRegistry
|
|
37
|
+
commandRegistry: CommandRegistry;
|
|
38
|
+
menuRegistry: MenuModelRegistry;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
interface NotebookCellListState {
|
|
@@ -113,8 +114,9 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
113
114
|
.map((cell, index) =>
|
|
114
115
|
<React.Fragment key={'cell-' + cell.handle}>
|
|
115
116
|
<NotebookCellDivider
|
|
117
|
+
menuRegistry={this.props.menuRegistry}
|
|
116
118
|
isVisible={() => this.isEnabled()}
|
|
117
|
-
onAddNewCell={(
|
|
119
|
+
onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, index)}
|
|
118
120
|
onDrop={e => this.onDrop(e, index)}
|
|
119
121
|
onDragOver={e => this.onDragOver(e, cell, 'top')} />
|
|
120
122
|
{this.shouldRenderDragOverIndicator(cell, 'top') && <CellDropIndicator />}
|
|
@@ -132,7 +134,14 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
132
134
|
onDrop={e => this.onDrop(e, index)}
|
|
133
135
|
draggable={true}
|
|
134
136
|
tabIndex={-1}
|
|
135
|
-
ref={ref =>
|
|
137
|
+
ref={ref => {
|
|
138
|
+
if (ref && cell === this.state.selectedCell && this.state.scrollIntoView) {
|
|
139
|
+
ref.scrollIntoView({ block: 'nearest' });
|
|
140
|
+
if (cell.cellKind === CellKind.Markup && !cell.editing) {
|
|
141
|
+
ref.focus();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}}>
|
|
136
145
|
<div className={'theia-notebook-cell-marker' + (this.state.selectedCell === cell ? ' theia-notebook-cell-marker-selected' : '')}></div>
|
|
137
146
|
<div className='theia-notebook-cell-content'>
|
|
138
147
|
{this.renderCellContent(cell, index)}
|
|
@@ -148,8 +157,9 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
148
157
|
)
|
|
149
158
|
}
|
|
150
159
|
<NotebookCellDivider
|
|
160
|
+
menuRegistry={this.props.menuRegistry}
|
|
151
161
|
isVisible={() => this.isEnabled()}
|
|
152
|
-
onAddNewCell={(
|
|
162
|
+
onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, this.props.notebookModel.cells.length)}
|
|
153
163
|
onDrop={e => this.onDrop(e, this.props.notebookModel.cells.length - 1)}
|
|
154
164
|
onDragOver={e => this.onDragOver(e, this.props.notebookModel.cells[this.props.notebookModel.cells.length - 1], 'bottom')} />
|
|
155
165
|
</ul>;
|
|
@@ -214,11 +224,11 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
214
224
|
this.setState({ ...this.state, dragOverIndicator: undefined });
|
|
215
225
|
}
|
|
216
226
|
|
|
217
|
-
protected onAddNewCell(
|
|
227
|
+
protected onAddNewCell(commandId: string, index: number): void {
|
|
218
228
|
if (this.isEnabled()) {
|
|
219
|
-
this.props.commandRegistry.executeCommand(NotebookCommands.
|
|
229
|
+
this.props.commandRegistry.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, index - 1);
|
|
230
|
+
this.props.commandRegistry.executeCommand(commandId,
|
|
220
231
|
this.props.notebookModel,
|
|
221
|
-
kind,
|
|
222
232
|
index
|
|
223
233
|
);
|
|
224
234
|
}
|
|
@@ -235,24 +245,31 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
235
245
|
|
|
236
246
|
export interface NotebookCellDividerProps {
|
|
237
247
|
isVisible: () => boolean;
|
|
238
|
-
onAddNewCell: (
|
|
248
|
+
onAddNewCell: (commandId: string) => void;
|
|
239
249
|
onDrop: (event: React.DragEvent<HTMLLIElement>) => void;
|
|
240
250
|
onDragOver: (event: React.DragEvent<HTMLLIElement>) => void;
|
|
251
|
+
menuRegistry: MenuModelRegistry;
|
|
241
252
|
}
|
|
242
253
|
|
|
243
|
-
export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver }: NotebookCellDividerProps): React.JSX.Element {
|
|
254
|
+
export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver, menuRegistry }: NotebookCellDividerProps): React.JSX.Element {
|
|
244
255
|
const [hover, setHover] = React.useState(false);
|
|
245
256
|
|
|
257
|
+
const menuPath = NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP;
|
|
258
|
+
const menuItems = menuRegistry.getMenuNode(menuPath).children;
|
|
259
|
+
|
|
260
|
+
const renderItem = (item: MenuNode): React.ReactNode => <button
|
|
261
|
+
key={item.id}
|
|
262
|
+
className='theia-notebook-add-cell-button'
|
|
263
|
+
onClick={() => onAddNewCell(item.command || '')}
|
|
264
|
+
title={nls.localizeByDefault(`Add ${item.label} Cell`)}
|
|
265
|
+
>
|
|
266
|
+
<div className={item.icon + ' theia-notebook-add-cell-button-icon'} />
|
|
267
|
+
<div className='theia-notebook-add-cell-button-text'>{item.label}</div>
|
|
268
|
+
</button>;
|
|
269
|
+
|
|
246
270
|
return <li className='theia-notebook-cell-divider' onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} onDrop={onDrop} onDragOver={onDragOver}>
|
|
247
271
|
{hover && isVisible() && <div className='theia-notebook-add-cell-buttons'>
|
|
248
|
-
|
|
249
|
-
<div className={codicon('add') + ' theia-notebook-add-cell-button-icon'} />
|
|
250
|
-
{nls.localizeByDefault('Code')}
|
|
251
|
-
</button>
|
|
252
|
-
<button className='theia-notebook-add-cell-button' onClick={() => onAddNewCell(CellKind.Markup)} title={nls.localizeByDefault('Add Markdown Cell')}>
|
|
253
|
-
<div className={codicon('add') + ' theia-notebook-add-cell-button-icon'} />
|
|
254
|
-
{nls.localizeByDefault('Markdown')}
|
|
255
|
-
</button>
|
|
272
|
+
{menuItems.map((item: MenuNode) => renderItem(item))}
|
|
256
273
|
</div>}
|
|
257
274
|
</li>;
|
|
258
275
|
}
|
|
@@ -126,6 +126,9 @@ export class NotebookCellModel implements NotebookCell, Disposable {
|
|
|
126
126
|
protected readonly onDidSelectFindMatchEmitter = new Emitter<NotebookCodeEditorFindMatch>();
|
|
127
127
|
readonly onDidSelectFindMatch: Event<NotebookCodeEditorFindMatch> = this.onDidSelectFindMatchEmitter.event;
|
|
128
128
|
|
|
129
|
+
protected onDidRequestCenterEditorEmitter = new Emitter<void>();
|
|
130
|
+
readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event;
|
|
131
|
+
|
|
129
132
|
@inject(NotebookCellModelProps)
|
|
130
133
|
protected readonly props: NotebookCellModelProps;
|
|
131
134
|
|
|
@@ -299,6 +302,10 @@ export class NotebookCellModel implements NotebookCell, Disposable {
|
|
|
299
302
|
this.onWillBlurCellEditorEmitter.fire();
|
|
300
303
|
}
|
|
301
304
|
|
|
305
|
+
requestCenterEditor(): void {
|
|
306
|
+
this.onDidRequestCenterEditorEmitter.fire();
|
|
307
|
+
}
|
|
308
|
+
|
|
302
309
|
spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
|
|
303
310
|
if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
|
|
304
311
|
const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);
|