@theia/notebook 1.55.0-next.4 → 1.55.0-next.67

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.
Files changed (81) hide show
  1. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  2. package/lib/browser/contributions/notebook-cell-actions-contribution.js +55 -10
  3. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  4. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts +7 -9
  5. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts.map +1 -1
  6. package/lib/browser/contributions/notebook-status-bar-contribution.js +21 -33
  7. package/lib/browser/contributions/notebook-status-bar-contribution.js.map +1 -1
  8. package/lib/browser/notebook-editor-widget.d.ts +4 -0
  9. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  10. package/lib/browser/notebook-editor-widget.js +23 -1
  11. package/lib/browser/notebook-editor-widget.js.map +1 -1
  12. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  13. package/lib/browser/notebook-frontend-module.js +3 -1
  14. package/lib/browser/notebook-frontend-module.js.map +1 -1
  15. package/lib/browser/renderers/cell-output-webview.d.ts +17 -3
  16. package/lib/browser/renderers/cell-output-webview.d.ts.map +1 -1
  17. package/lib/browser/renderers/cell-output-webview.js +2 -1
  18. package/lib/browser/renderers/cell-output-webview.js.map +1 -1
  19. package/lib/browser/service/notebook-cell-editor-service.d.ts +3 -0
  20. package/lib/browser/service/notebook-cell-editor-service.d.ts.map +1 -1
  21. package/lib/browser/service/notebook-cell-editor-service.js +26 -2
  22. package/lib/browser/service/notebook-cell-editor-service.js.map +1 -1
  23. package/lib/browser/service/notebook-cell-status-bar-service.d.ts +39 -0
  24. package/lib/browser/service/notebook-cell-status-bar-service.d.ts.map +1 -0
  25. package/lib/browser/service/notebook-cell-status-bar-service.js +69 -0
  26. package/lib/browser/service/notebook-cell-status-bar-service.js.map +1 -0
  27. package/lib/browser/service/notebook-editor-widget-service.d.ts +4 -0
  28. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
  29. package/lib/browser/service/notebook-editor-widget-service.js +19 -0
  30. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
  31. package/lib/browser/view/notebook-cell-editor.d.ts +1 -0
  32. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  33. package/lib/browser/view/notebook-cell-editor.js +19 -13
  34. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  35. package/lib/browser/view/notebook-cell-list-view.d.ts +4 -1
  36. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  37. package/lib/browser/view/notebook-cell-list-view.js +36 -13
  38. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  39. package/lib/browser/view/notebook-cell-toolbar.js +1 -1
  40. package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
  41. package/lib/browser/view/notebook-code-cell-view.d.ts +30 -12
  42. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  43. package/lib/browser/view/notebook-code-cell-view.js +99 -71
  44. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  45. package/lib/browser/view/notebook-main-toolbar.js +4 -4
  46. package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
  47. package/lib/browser/view/notebook-markdown-cell-view.d.ts +5 -0
  48. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  49. package/lib/browser/view/notebook-markdown-cell-view.js +22 -5
  50. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  51. package/lib/browser/view-model/notebook-cell-model.d.ts +12 -0
  52. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  53. package/lib/browser/view-model/notebook-cell-model.js +19 -0
  54. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  55. package/lib/browser/view-model/notebook-cell-output-model.d.ts +0 -3
  56. package/lib/browser/view-model/notebook-cell-output-model.d.ts.map +1 -1
  57. package/lib/browser/view-model/notebook-cell-output-model.js +0 -6
  58. package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
  59. package/lib/browser/view-model/notebook-model.d.ts +2 -1
  60. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  61. package/lib/browser/view-model/notebook-model.js +3 -0
  62. package/lib/browser/view-model/notebook-model.js.map +1 -1
  63. package/package.json +8 -8
  64. package/src/browser/contributions/notebook-cell-actions-contribution.ts +61 -11
  65. package/src/browser/contributions/notebook-status-bar-contribution.ts +23 -34
  66. package/src/browser/notebook-editor-widget.tsx +32 -7
  67. package/src/browser/notebook-frontend-module.ts +6 -2
  68. package/src/browser/renderers/cell-output-webview.ts +21 -3
  69. package/src/browser/service/notebook-cell-editor-service.ts +21 -3
  70. package/src/browser/service/notebook-cell-status-bar-service.ts +94 -0
  71. package/src/browser/service/notebook-editor-widget-service.ts +20 -0
  72. package/src/browser/style/index.css +54 -9
  73. package/src/browser/view/notebook-cell-editor.tsx +18 -11
  74. package/src/browser/view/notebook-cell-list-view.tsx +44 -13
  75. package/src/browser/view/notebook-cell-toolbar.tsx +1 -1
  76. package/src/browser/view/notebook-code-cell-view.tsx +143 -92
  77. package/src/browser/view/notebook-main-toolbar.tsx +4 -4
  78. package/src/browser/view/notebook-markdown-cell-view.tsx +30 -6
  79. package/src/browser/view-model/notebook-cell-model.ts +29 -0
  80. package/src/browser/view-model/notebook-cell-output-model.ts +0 -8
  81. package/src/browser/view-model/notebook-model.ts +5 -1
@@ -14,10 +14,9 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { inject, injectable } from '@theia/core/shared/inversify';
18
- import { FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser';
17
+ import { injectable } from '@theia/core/shared/inversify';
18
+ import { StatusBar, StatusBarAlignment, Widget, WidgetStatusBarContribution } from '@theia/core/lib/browser';
19
19
  import { Disposable } from '@theia/core/lib/common';
20
- import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
21
20
  import { NotebookEditorWidget } from '../notebook-editor-widget';
22
21
  import { nls } from '@theia/core';
23
22
  import { NotebookCommands } from './notebook-actions-contribution';
@@ -25,53 +24,43 @@ import { NotebookCommands } from './notebook-actions-contribution';
25
24
  export const NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID = 'notebook-cell-selection-position';
26
25
 
27
26
  @injectable()
28
- export class NotebookStatusBarContribution implements FrontendApplicationContribution {
27
+ export class NotebookStatusBarContribution implements WidgetStatusBarContribution<NotebookEditorWidget> {
29
28
 
30
- @inject(StatusBar) protected readonly statusBar: StatusBar;
31
- @inject(NotebookEditorWidgetService) protected readonly editorWidgetService: NotebookEditorWidgetService;
29
+ protected onDeactivate: Disposable | undefined;
32
30
 
33
- protected currentCellSelectionListener: Disposable | undefined;
34
- protected lastFocusedEditor: NotebookEditorWidget | undefined;
31
+ canHandle(widget: Widget): widget is NotebookEditorWidget {
32
+ return widget instanceof NotebookEditorWidget;
33
+ }
35
34
 
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();
35
+ activate(statusBar: StatusBar, widget: NotebookEditorWidget): void {
36
+ widget.ready.then(model => {
37
+ this.onDeactivate = model.onDidChangeSelectedCell(() => {
38
+ this.updateStatusbar(statusBar, widget);
45
39
  });
46
- this.updateStatusbar(editor);
47
- this.lastFocusedEditor = editor;
48
40
  });
49
- if (this.editorWidgetService.focusedEditor) {
50
- this.updateStatusbar();
51
- }
41
+ this.updateStatusbar(statusBar, widget);
52
42
  }
53
43
 
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
- }
44
+ deactivate(statusBar: StatusBar): void {
45
+ this.onDeactivate?.dispose();
46
+ this.updateStatusbar(statusBar);
47
+ }
59
48
 
60
- await editor?.ready;
61
- if (!editor?.model) {
49
+ protected async updateStatusbar(statusBar: StatusBar, editor?: NotebookEditorWidget): Promise<void> {
50
+ const model = await editor?.ready;
51
+ if (!model || model.cells.length === 0 || !model.selectedCell) {
52
+ statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID);
62
53
  return;
63
54
  }
64
55
 
65
- const selectedCellIndex = editor.model.selectedCell ? editor.model.cells.indexOf(editor.model.selectedCell) + 1 : '';
56
+ const selectedCellIndex = model.cells.indexOf(model.selectedCell) + 1;
66
57
 
67
- this.statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, {
68
- text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, editor.model.cells.length),
58
+ statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, {
59
+ text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, model.cells.length),
69
60
  alignment: StatusBarAlignment.RIGHT,
70
61
  priority: 100,
71
62
  command: NotebookCommands.CENTER_ACTIVE_CELL.id,
72
63
  arguments: [editor]
73
64
  });
74
-
75
65
  }
76
-
77
66
  }
@@ -18,7 +18,7 @@ import * as React from '@theia/core/shared/react';
18
18
  import { CommandRegistry, MenuModelRegistry, URI } from '@theia/core';
19
19
  import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable, lock, unlock, animationFrame } from '@theia/core/lib/browser';
20
20
  import { ReactNode } from '@theia/core/shared/react';
21
- import { CellKind } from '../common';
21
+ import { CellKind, NotebookCellsChangeType } from '../common';
22
22
  import { CellRenderer as CellRenderer, NotebookCellListView } from './view/notebook-cell-list-view';
23
23
  import { NotebookCodeCellRenderer } from './view/notebook-code-cell-view';
24
24
  import { NotebookMarkdownCellRenderer } from './view/notebook-markdown-cell-view';
@@ -35,6 +35,8 @@ import { NotebookViewportService } from './view/notebook-viewport-service';
35
35
  import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution';
36
36
  import { NotebookFindWidget } from './view/notebook-find-widget';
37
37
  import debounce = require('lodash/debounce');
38
+ import { CellOutputWebview, CellOutputWebviewFactory } from './renderers/cell-output-webview';
39
+ import { NotebookCellOutputModel } from './view-model/notebook-cell-output-model';
38
40
  const PerfectScrollbar = require('react-perfect-scrollbar');
39
41
 
40
42
  export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory');
@@ -44,6 +46,9 @@ export function createNotebookEditorWidgetContainer(parent: interfaces.Container
44
46
 
45
47
  child.bind(NotebookEditorProps).toConstantValue(props);
46
48
 
49
+ const cellOutputWebviewFactory: CellOutputWebviewFactory = parent.get(CellOutputWebviewFactory);
50
+ child.bind(CellOutputWebview).toConstantValue(cellOutputWebviewFactory());
51
+
47
52
  child.bind(NotebookContextManager).toSelf().inSingletonScope();
48
53
  child.bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope();
49
54
  child.bind(NotebookCellToolbarFactory).toSelf().inSingletonScope();
@@ -104,6 +109,9 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
104
109
  @inject(NotebookViewportService)
105
110
  protected readonly viewportService: NotebookViewportService;
106
111
 
112
+ @inject(CellOutputWebview)
113
+ protected readonly cellOutputWebview: CellOutputWebview;
114
+
107
115
  protected readonly onDidChangeModelEmitter = new Emitter<void>();
108
116
  readonly onDidChangeModel = this.onDidChangeModelEmitter.event;
109
117
 
@@ -173,11 +181,18 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
173
181
  this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]);
174
182
  model.setSelectedCell(model.cells[0]);
175
183
  }
184
+ model.onDidChangeContent(changeEvents => {
185
+ const cellEvent = changeEvents.filter(event => event.kind === NotebookCellsChangeType.Move || event.kind === NotebookCellsChangeType.ModelChange);
186
+ if (cellEvent.length > 0) {
187
+ this.cellOutputWebview.cellsChanged(cellEvent);
188
+ }
189
+ });
176
190
  });
177
191
  }
178
192
 
179
193
  protected async waitForData(): Promise<NotebookModel> {
180
194
  this._model = await this.props.notebookData;
195
+ this.cellOutputWebview.init(this._model, this);
181
196
  this.saveable.delegate = this._model;
182
197
  this.toDispose.push(this._model);
183
198
  this.toDispose.push(this._model.onDidChangeContent(() => {
@@ -261,12 +276,15 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
261
276
  <PerfectScrollbar className='theia-notebook-scroll-container'
262
277
  ref={this.scrollBarRef}
263
278
  onScrollY={(e: HTMLDivElement) => this.viewportService.onScroll(e)}>
264
- <NotebookCellListView renderers={this.renderers}
265
- notebookModel={this._model}
266
- notebookContext={this.notebookContextManager}
267
- toolbarRenderer={this.cellToolbarFactory}
268
- commandRegistry={this.commandRegistry}
269
- menuRegistry={this.menuRegistry} />
279
+ <div className='theia-notebook-scroll-area'>
280
+ {this.cellOutputWebview.render()}
281
+ <NotebookCellListView renderers={this.renderers}
282
+ notebookModel={this._model}
283
+ notebookContext={this.notebookContextManager}
284
+ toolbarRenderer={this.cellToolbarFactory}
285
+ commandRegistry={this.commandRegistry}
286
+ menuRegistry={this.menuRegistry} />
287
+ </div>
270
288
  </PerfectScrollbar>
271
289
  </div>
272
290
  </div>;
@@ -282,6 +300,12 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
282
300
  this.notebookEditorService.removeNotebookEditor(this);
283
301
  }
284
302
 
303
+ requestOuputPresentationChange(cellHandle: number, output?: NotebookCellOutputModel): void {
304
+ if (output) {
305
+ this.cellOutputWebview.requestOutputPresentationUpdate(cellHandle, output);
306
+ }
307
+ }
308
+
285
309
  postKernelMessage(message: unknown): void {
286
310
  this.onDidPostKernelMessageEmitter.fire(message);
287
311
  }
@@ -307,6 +331,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
307
331
  }
308
332
 
309
333
  override dispose(): void {
334
+ this.cellOutputWebview.dispose();
310
335
  this.notebookContextManager.dispose();
311
336
  this.onDidChangeModelEmitter.dispose();
312
337
  this.onDidPostKernelMessageEmitter.dispose();
@@ -16,7 +16,9 @@
16
16
  import '../../src/browser/style/index.css';
17
17
 
18
18
  import { ContainerModule } from '@theia/core/shared/inversify';
19
- import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory } from '@theia/core/lib/browser';
19
+ import {
20
+ FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory, WidgetStatusBarContribution
21
+ } from '@theia/core/lib/browser';
20
22
  import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
21
23
  import { NotebookOpenHandler } from './notebook-open-handler';
22
24
  import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core';
@@ -49,6 +51,7 @@ import { NotebookOptionsService } from './service/notebook-options';
49
51
  import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler';
50
52
  import { NotebookStatusBarContribution } from './contributions/notebook-status-bar-contribution';
51
53
  import { NotebookCellEditorService } from './service/notebook-cell-editor-service';
54
+ import { NotebookCellStatusBarService } from './service/notebook-cell-status-bar-service';
52
55
 
53
56
  export default new ContainerModule((bind, unbind, isBound, rebind) => {
54
57
  bind(NotebookColorContribution).toSelf().inSingletonScope();
@@ -72,6 +75,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
72
75
  bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();
73
76
  bind(NotebookClipboardService).toSelf().inSingletonScope();
74
77
  bind(NotebookCellEditorService).toSelf().inSingletonScope();
78
+ bind(NotebookCellStatusBarService).toSelf().inSingletonScope();
75
79
 
76
80
  bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
77
81
  bind(ResourceResolver).toService(NotebookCellResourceResolver);
@@ -117,5 +121,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
117
121
  bind(UndoRedoHandler).toService(NotebookUndoRedoHandler);
118
122
 
119
123
  bind(NotebookStatusBarContribution).toSelf().inSingletonScope();
120
- bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution);
124
+ bind(WidgetStatusBarContribution).toService(NotebookStatusBarContribution);
121
125
  });
@@ -14,20 +14,38 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { Disposable } from '@theia/core';
18
- import { NotebookCellModel } from '../view-model/notebook-cell-model';
17
+ import { Disposable, Event } from '@theia/core';
19
18
  import { NotebookModel } from '../view-model/notebook-model';
19
+ import { NotebookEditorWidget } from '../notebook-editor-widget';
20
+ import { NotebookContentChangedEvent } from '../notebook-types';
21
+ import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model';
22
+ import { NotebookCellModel } from '../view-model/notebook-cell-model';
20
23
 
21
24
  export const CellOutputWebviewFactory = Symbol('outputWebviewFactory');
25
+ export const CellOutputWebview = Symbol('outputWebview');
22
26
 
23
- export type CellOutputWebviewFactory = (cell: NotebookCellModel, notebook: NotebookModel) => Promise<CellOutputWebview>;
27
+ export type CellOutputWebviewFactory = () => Promise<CellOutputWebview>;
28
+
29
+ export interface OutputRenderEvent {
30
+ cellHandle: number;
31
+ outputId: string;
32
+ outputHeight: number;
33
+ }
24
34
 
25
35
  export interface CellOutputWebview extends Disposable {
26
36
 
27
37
  readonly id: string;
28
38
 
39
+ init(notebook: NotebookModel, editor: NotebookEditorWidget): void;
40
+
29
41
  render(): React.ReactNode;
30
42
 
43
+ setCellHeight(cell: NotebookCellModel, height: number): void;
44
+ cellsChanged(cellEvent: NotebookContentChangedEvent[]): void;
45
+ onDidRenderOutput: Event<OutputRenderEvent>
46
+
47
+ requestOutputPresentationUpdate(cellHandle: number, output: NotebookCellOutputModel): void;
48
+
31
49
  attachWebview(): void;
32
50
  isAttached(): boolean
33
51
  }
@@ -15,12 +15,17 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { Emitter, URI } from '@theia/core';
18
- import { injectable } from '@theia/core/shared/inversify';
18
+ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
19
19
  import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
20
+ import { NotebookEditorWidgetService } from './notebook-editor-widget-service';
21
+ import { CellUri } from '../../common';
20
22
 
21
23
  @injectable()
22
24
  export class NotebookCellEditorService {
23
25
 
26
+ @inject(NotebookEditorWidgetService)
27
+ protected readonly notebookEditorWidgetService: NotebookEditorWidgetService;
28
+
24
29
  protected onDidChangeCellEditorsEmitter = new Emitter<void>();
25
30
  readonly onDidChangeCellEditors = this.onDidChangeCellEditorsEmitter.event;
26
31
 
@@ -31,6 +36,17 @@ export class NotebookCellEditorService {
31
36
 
32
37
  protected currentCellEditors: Map<string, SimpleMonacoEditor> = new Map();
33
38
 
39
+ @postConstruct()
40
+ protected init(): void {
41
+ this.notebookEditorWidgetService.onDidChangeCurrentEditor(editor => {
42
+ // if defocus notebook editor or another notebook editor is focused, clear the active cell
43
+ if (!editor || (this.currentActiveCell && CellUri.parse(this.currentActiveCell.uri)?.notebook.toString() !== editor?.model?.uri.toString())) {
44
+ this.currentActiveCell = undefined;
45
+ this.onDidChangeFocusedCellEditorEmitter.fire(undefined);
46
+ }
47
+ });
48
+ }
49
+
34
50
  get allCellEditors(): SimpleMonacoEditor[] {
35
51
  return Array.from(this.currentCellEditors.values());
36
52
  }
@@ -46,8 +62,10 @@ export class NotebookCellEditorService {
46
62
  }
47
63
 
48
64
  editorFocusChanged(editor?: SimpleMonacoEditor): void {
49
- this.currentActiveCell = editor;
50
- this.onDidChangeFocusedCellEditorEmitter.fire(editor);
65
+ if (editor) {
66
+ this.currentActiveCell = editor;
67
+ this.onDidChangeFocusedCellEditorEmitter.fire(editor);
68
+ }
51
69
  }
52
70
 
53
71
  getActiveCell(): SimpleMonacoEditor | undefined {
@@ -0,0 +1,94 @@
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
+ * Copyright (c) Microsoft Corporation. All rights reserved.
18
+ * Licensed under the MIT License. See License.txt in the project root for license information.
19
+ *--------------------------------------------------------------------------------------------*/
20
+
21
+ import { CancellationToken, Command, Disposable, Emitter, Event, URI } from '@theia/core';
22
+ import { CellStatusbarAlignment } from '../../common';
23
+ import { ThemeColor } from '@theia/core/lib/common/theme';
24
+ import { AccessibilityInformation } from '@theia/core/lib/common/accessibility';
25
+ import { injectable } from '@theia/core/shared/inversify';
26
+ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
27
+
28
+ export interface NotebookCellStatusBarItem {
29
+ readonly alignment: CellStatusbarAlignment;
30
+ readonly priority?: number;
31
+ readonly text: string;
32
+ readonly color?: string | ThemeColor;
33
+ readonly backgroundColor?: string | ThemeColor;
34
+ readonly tooltip?: string | MarkdownString;
35
+ readonly command?: string | (Command & { arguments?: unknown[] });
36
+ readonly accessibilityInformation?: AccessibilityInformation;
37
+ readonly opacity?: string;
38
+ readonly onlyShowWhenActive?: boolean;
39
+ }
40
+ export interface NotebookCellStatusBarItemList {
41
+ items: NotebookCellStatusBarItem[];
42
+ dispose?(): void;
43
+ }
44
+
45
+ export interface NotebookCellStatusBarItemProvider {
46
+ viewType: string;
47
+ onDidChangeStatusBarItems?: Event<void>;
48
+ provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise<NotebookCellStatusBarItemList | undefined>;
49
+ }
50
+
51
+ @injectable()
52
+ export class NotebookCellStatusBarService implements Disposable {
53
+
54
+ protected readonly onDidChangeProvidersEmitter = new Emitter<void>();
55
+ readonly onDidChangeProviders: Event<void> = this.onDidChangeProvidersEmitter.event;
56
+
57
+ protected readonly onDidChangeItemsEmitter = new Emitter<void>();
58
+ readonly onDidChangeItems: Event<void> = this.onDidChangeItemsEmitter.event;
59
+
60
+ protected readonly providers: NotebookCellStatusBarItemProvider[] = [];
61
+
62
+ registerCellStatusBarItemProvider(provider: NotebookCellStatusBarItemProvider): Disposable {
63
+ this.providers.push(provider);
64
+ let changeListener: Disposable | undefined;
65
+ if (provider.onDidChangeStatusBarItems) {
66
+ changeListener = provider.onDidChangeStatusBarItems(() => this.onDidChangeItemsEmitter.fire());
67
+ }
68
+
69
+ this.onDidChangeProvidersEmitter.fire();
70
+
71
+ return Disposable.create(() => {
72
+ changeListener?.dispose();
73
+ const idx = this.providers.findIndex(p => p === provider);
74
+ this.providers.splice(idx, 1);
75
+ });
76
+ }
77
+
78
+ async getStatusBarItemsForCell(notebookUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise<NotebookCellStatusBarItemList[]> {
79
+ const providers = this.providers.filter(p => p.viewType === viewType || p.viewType === '*');
80
+ return Promise.all(providers.map(async p => {
81
+ try {
82
+ return await p.provideCellStatusBarItems(notebookUri, cellIndex, token) ?? { items: [] };
83
+ } catch (e) {
84
+ console.error(e);
85
+ return { items: [] };
86
+ }
87
+ }));
88
+ }
89
+
90
+ dispose(): void {
91
+ this.onDidChangeItemsEmitter.dispose();
92
+ this.onDidChangeProvidersEmitter.dispose();
93
+ }
94
+ }
@@ -45,13 +45,23 @@ export class NotebookEditorWidgetService {
45
45
  protected readonly onDidChangeFocusedEditorEmitter = new Emitter<NotebookEditorWidget | undefined>();
46
46
  readonly onDidChangeFocusedEditor = this.onDidChangeFocusedEditorEmitter.event;
47
47
 
48
+ protected readonly onDidChangeCurrentEditorEmitter = new Emitter<NotebookEditorWidget | undefined>();
49
+ readonly onDidChangeCurrentEditor = this.onDidChangeCurrentEditorEmitter.event;
50
+
48
51
  focusedEditor?: NotebookEditorWidget = undefined;
49
52
 
53
+ currentEditor?: NotebookEditorWidget = undefined;
54
+
50
55
  @postConstruct()
51
56
  protected init(): void {
52
57
  this.applicationShell.onDidChangeActiveWidget(event => {
53
58
  this.notebookEditorFocusChanged(event.newValue as NotebookEditorWidget, event.newValue instanceof NotebookEditorWidget);
54
59
  });
60
+ this.applicationShell.onDidChangeCurrentWidget(event => {
61
+ if (event.newValue instanceof NotebookEditorWidget || event.oldValue instanceof NotebookEditorWidget) {
62
+ this.currentNotebookEditorChanged(event.newValue);
63
+ }
64
+ });
55
65
  }
56
66
 
57
67
  // --- editor management
@@ -98,4 +108,14 @@ export class NotebookEditorWidgetService {
98
108
  }
99
109
  }
100
110
 
111
+ currentNotebookEditorChanged(newEditor: unknown): void {
112
+ if (newEditor instanceof NotebookEditorWidget) {
113
+ this.currentEditor = newEditor;
114
+ this.onDidChangeCurrentEditorEmitter.fire(newEditor);
115
+ } else if (this.currentEditor?.isDisposed || !this.currentEditor?.isVisible) {
116
+ this.currentEditor = undefined;
117
+ this.onDidChangeCurrentEditorEmitter.fire(undefined);
118
+ }
119
+ }
120
+
101
121
  }
@@ -20,10 +20,24 @@
20
20
  }
21
21
 
22
22
  .theia-notebook-cell-list {
23
+ position: absolute;
24
+ top: 0;
25
+ width: 100%;
23
26
  overflow-y: auto;
24
27
  list-style: none;
25
28
  padding-left: 0px;
26
29
  background-color: var(--theia-notebook-editorBackground);
30
+ z-index: 0;
31
+ pointer-events: none;
32
+ }
33
+
34
+
35
+ .theia-notebook-cell-output-webview {
36
+ padding: 5px 0px;
37
+ margin: 0px 15px 0px 50px;
38
+ width: calc(100% - 60px);
39
+ position: absolute;
40
+ z-index: 0;
27
41
  }
28
42
 
29
43
  .theia-notebook-cell {
@@ -69,7 +83,8 @@
69
83
  /* Rendered Markdown Content */
70
84
 
71
85
  .theia-notebook-markdown-content {
72
- padding: 8px 16px 8px 36px;
86
+ pointer-events: all;
87
+ padding: 8px 16px 8px 0px;
73
88
  font-size: var(--theia-notebook-markdown-size);
74
89
  }
75
90
 
@@ -87,9 +102,13 @@
87
102
  padding-bottom: 0;
88
103
  }
89
104
 
105
+ .theia-notebook-markdown-sidebar {
106
+ width: 35px;
107
+ }
108
+
90
109
  /* Markdown cell edit mode */
91
110
  .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
92
- margin-left: 36px;
111
+ pointer-events: all;
93
112
  margin-right: var(--theia-notebook-cell-editor-margin-right);
94
113
  outline: 1px solid var(--theia-notebook-cellBorderColor);
95
114
  }
@@ -108,6 +127,7 @@
108
127
  }
109
128
 
110
129
  .theia-notebook-cell-editor-container {
130
+ pointer-events: all;
111
131
  width: calc(100% - 46px);
112
132
  flex: 1;
113
133
  outline: 1px solid var(--theia-notebook-cellBorderColor);
@@ -149,6 +169,7 @@
149
169
  }
150
170
 
151
171
  .theia-notebook-cell-toolbar {
172
+ pointer-events: all;
152
173
  border: 1px solid var(--theia-notebook-cellToolbarSeparator);
153
174
  display: flex;
154
175
  position: absolute;
@@ -161,11 +182,15 @@
161
182
  display: flex;
162
183
  flex-direction: column;
163
184
  padding: 2px;
164
- background-color: var(--theia-editor-background);
165
185
  flex-grow: 1;
166
186
  }
167
187
 
168
188
  .theia-notebook-cell-sidebar {
189
+ pointer-events: all;
190
+ display: flex;
191
+ }
192
+
193
+ .theia-notebook-cell-sidebar-actions {
169
194
  display: flex;
170
195
  flex-direction: column;
171
196
  }
@@ -194,6 +219,7 @@
194
219
  }
195
220
 
196
221
  .theia-notebook-cell-divider {
222
+ pointer-events: all;
197
223
  height: 25px;
198
224
  width: 100%;
199
225
  }
@@ -303,19 +329,19 @@
303
329
  margin: 1px 0 0 4px;
304
330
  }
305
331
 
306
- .theia-notebook-cell-output-webview {
307
- padding: 5px 0px;
308
- margin: 0px 15px 0px 9px;
309
- width: 100%;
310
- }
311
-
312
332
  .theia-notebook-cell-drop-indicator {
313
333
  height: 2px;
314
334
  background-color: var(--theia-notebook-focusedCellBorder);
315
335
  width: 100%;
316
336
  }
317
337
 
338
+ .theia-notebook-collapsed-output-container {
339
+ width: 0;
340
+ overflow: visible;
341
+ }
342
+
318
343
  .theia-notebook-collapsed-output {
344
+ text-wrap: nowrap;
319
345
  padding: 4px 8px;
320
346
  color: var(--theia-foreground);
321
347
  margin-left: 30px;
@@ -482,3 +508,22 @@ mark.theia-find-match.theia-find-match-selected {
482
508
  color: var(--theia-editor-findMatchForeground);
483
509
  background-color: var(--theia-editor-findMatchBackground);
484
510
  }
511
+
512
+ .cell-status-bar-item {
513
+ align-items: center;
514
+ display: flex;
515
+ height: 16px;
516
+ margin: 0 3px;
517
+ overflow: hidden;
518
+ padding: 0 3px;
519
+ text-overflow: clip;
520
+ white-space: pre;
521
+ }
522
+
523
+ .cell-status-item-has-command {
524
+ cursor: pointer;
525
+ }
526
+
527
+ .cell-status-item-has-command:hover {
528
+ background-color: var(--theia-toolbar-hoverBackground);
529
+ }
@@ -103,15 +103,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
103
103
  this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount);
104
104
  }));
105
105
 
106
- this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => {
107
- let parent = this.container?.parentElement;
108
- while (parent && !parent.classList.contains('theia-notebook-cell')) {
109
- parent = parent.parentElement;
110
- }
111
- if (parent) {
112
- parent.focus();
113
- }
114
- }));
106
+ this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => this.blurEditor()));
115
107
 
116
108
  this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => {
117
109
  this.editor?.getControl().updateOptions(options);
@@ -130,7 +122,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
130
122
 
131
123
  this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
132
124
  if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
133
- this.props.notebookContextManager.context?.focus();
125
+ this.blurEditor();
134
126
  }
135
127
  }));
136
128
  if (!this.props.notebookViewportService || (this.container && this.props.notebookViewportService.isElementInViewport(this.container))) {
@@ -212,6 +204,11 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
212
204
 
213
205
  this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => {
214
206
  const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection);
207
+ // TODO handle secondary selections
208
+ this.props.cell.selection = {
209
+ start: { line: e.selection.startLineNumber - 1, character: e.selection.startColumn - 1 },
210
+ end: { line: e.selection.endLineNumber - 1, character: e.selection.endColumn - 1 }
211
+ };
215
212
  this.props.notebookModel.selectedText = selectedText;
216
213
  }));
217
214
  this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => {
@@ -226,7 +223,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
226
223
  }));
227
224
  this.props.notebookCellEditorService.editorCreated(uri, this.editor);
228
225
  this.setMatches();
229
- if (cell.editing && notebookModel.selectedCell === cell) {
226
+ if (notebookModel.selectedCell === cell) {
230
227
  this.editor.getControl().focus();
231
228
  }
232
229
  }
@@ -273,4 +270,14 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
273
270
  </div >;
274
271
  }
275
272
 
273
+ protected blurEditor(): void {
274
+ let parent = this.container?.parentElement;
275
+ while (parent && !parent.classList.contains('theia-notebook-cell')) {
276
+ parent = parent.parentElement;
277
+ }
278
+ if (parent) {
279
+ parent.focus();
280
+ }
281
+ }
282
+
276
283
  }