@theia/notebook 1.54.0 → 1.55.0-next.14

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 (65) 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 +5 -1
  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 +26 -3
  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 +1 -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-editor-widget-service.d.ts +4 -0
  24. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
  25. package/lib/browser/service/notebook-editor-widget-service.js +19 -0
  26. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
  27. package/lib/browser/view/notebook-cell-list-view.d.ts +4 -1
  28. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  29. package/lib/browser/view/notebook-cell-list-view.js +36 -13
  30. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  31. package/lib/browser/view/notebook-code-cell-view.d.ts +17 -9
  32. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  33. package/lib/browser/view/notebook-code-cell-view.js +53 -70
  34. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  35. package/lib/browser/view/notebook-markdown-cell-view.d.ts +1 -0
  36. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  37. package/lib/browser/view/notebook-markdown-cell-view.js +9 -2
  38. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  39. package/lib/browser/view-model/notebook-cell-model.d.ts +5 -0
  40. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  41. package/lib/browser/view-model/notebook-cell-model.js +10 -0
  42. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  43. package/lib/browser/view-model/notebook-cell-output-model.d.ts +0 -3
  44. package/lib/browser/view-model/notebook-cell-output-model.d.ts.map +1 -1
  45. package/lib/browser/view-model/notebook-cell-output-model.js +0 -6
  46. package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
  47. package/lib/browser/view-model/notebook-model.d.ts +2 -1
  48. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  49. package/lib/browser/view-model/notebook-model.js +3 -0
  50. package/lib/browser/view-model/notebook-model.js.map +1 -1
  51. package/package.json +7 -7
  52. package/src/browser/contributions/notebook-cell-actions-contribution.ts +4 -1
  53. package/src/browser/contributions/notebook-status-bar-contribution.ts +23 -34
  54. package/src/browser/notebook-editor-widget.tsx +34 -9
  55. package/src/browser/notebook-frontend-module.ts +4 -2
  56. package/src/browser/renderers/cell-output-webview.ts +21 -3
  57. package/src/browser/service/notebook-cell-editor-service.ts +21 -3
  58. package/src/browser/service/notebook-editor-widget-service.ts +20 -0
  59. package/src/browser/style/index.css +35 -9
  60. package/src/browser/view/notebook-cell-list-view.tsx +44 -13
  61. package/src/browser/view/notebook-code-cell-view.tsx +78 -88
  62. package/src/browser/view/notebook-markdown-cell-view.tsx +10 -3
  63. package/src/browser/view-model/notebook-cell-model.ts +13 -0
  64. package/src/browser/view-model/notebook-cell-output-model.ts +0 -8
  65. package/src/browser/view-model/notebook-model.ts +5 -1
@@ -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(() => {
@@ -212,7 +227,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
212
227
 
213
228
  protected override onActivateRequest(msg: Message): void {
214
229
  super.onActivateRequest(msg);
215
- this.node.focus();
230
+ (this.node.getElementsByClassName('theia-notebook-main-container')[0] as HTMLDivElement)?.focus();
216
231
  }
217
232
 
218
233
  getResourceUri(): URI | undefined {
@@ -261,17 +276,20 @@ 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>;
273
291
  } else {
274
- return <div className='theia-notebook-main-container'>
292
+ return <div className='theia-notebook-main-container' tabIndex={-1}>
275
293
  <div className='theia-notebook-main-loading-indicator'></div>
276
294
  </div>;
277
295
  }
@@ -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';
@@ -117,5 +119,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
117
119
  bind(UndoRedoHandler).toService(NotebookUndoRedoHandler);
118
120
 
119
121
  bind(NotebookStatusBarContribution).toSelf().inSingletonScope();
120
- bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution);
122
+ bind(WidgetStatusBarContribution).toService(NotebookStatusBarContribution);
121
123
  });
@@ -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 {
@@ -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;
@@ -26,9 +26,19 @@ import { NotebookContextManager } from '../service/notebook-context-manager';
26
26
 
27
27
  export interface CellRenderer {
28
28
  render(notebookData: NotebookModel, cell: NotebookCellModel, index: number): React.ReactNode
29
+ renderSidebar(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode
29
30
  renderDragImage(cell: NotebookCellModel): HTMLElement
30
31
  }
31
32
 
33
+ export function observeCellHeight(ref: HTMLDivElement | null, cell: NotebookCellModel): void {
34
+ if (ref) {
35
+ cell.cellHeight = ref?.getBoundingClientRect().height ?? 0;
36
+ new ResizeObserver(entries =>
37
+ cell.cellHeight = ref?.getBoundingClientRect().height ?? 0
38
+ ).observe(ref);
39
+ }
40
+ }
41
+
32
42
  interface CellListProps {
33
43
  renderers: Map<CellKind, CellRenderer>;
34
44
  notebookModel: NotebookModel;
@@ -109,7 +119,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
109
119
  }
110
120
 
111
121
  override render(): React.ReactNode {
112
- return <ul className='theia-notebook-cell-list' ref={this.cellListRef}>
122
+ return <ul className='theia-notebook-cell-list' ref={this.cellListRef} onDragStart={e => this.onDragStart(e)}>
113
123
  {this.props.notebookModel.cells
114
124
  .map((cell, index) =>
115
125
  <React.Fragment key={'cell-' + cell.handle}>
@@ -119,13 +129,8 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
119
129
  onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, index)}
120
130
  onDrop={e => this.onDrop(e, index)}
121
131
  onDragOver={e => this.onDragOver(e, cell, 'top')} />
122
- {this.shouldRenderDragOverIndicator(cell, 'top') && <CellDropIndicator />}
132
+ <CellDropIndicator visible={this.shouldRenderDragOverIndicator(cell, 'top')} />
123
133
  <li className={'theia-notebook-cell' + (this.state.selectedCell === cell ? ' focused' : '') + (this.isEnabled() ? ' draggable' : '')}
124
- onClick={e => {
125
- this.setState({ ...this.state, selectedCell: cell });
126
- this.props.notebookModel.setSelectedCell(cell, false);
127
- }}
128
- onDragStart={e => this.onDragStart(e, index, cell)}
129
134
  onDragEnd={e => {
130
135
  NotebookCellListView.dragGhost?.remove();
131
136
  this.setState({ ...this.state, dragOverIndicator: undefined });
@@ -134,6 +139,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
134
139
  onDrop={e => this.onDrop(e, index)}
135
140
  draggable={true}
136
141
  tabIndex={-1}
142
+ data-cell-handle={cell.handle}
137
143
  ref={ref => {
138
144
  if (ref && cell === this.state.selectedCell && this.state.scrollIntoView) {
139
145
  ref.scrollIntoView({ block: 'nearest' });
@@ -141,8 +147,16 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
141
147
  ref.focus();
142
148
  }
143
149
  }
144
- }}>
145
- <div className={'theia-notebook-cell-marker' + (this.state.selectedCell === cell ? ' theia-notebook-cell-marker-selected' : '')}></div>
150
+ }}
151
+ onClick={e => {
152
+ this.setState({ ...this.state, selectedCell: cell });
153
+ this.props.notebookModel.setSelectedCell(cell, false);
154
+ }}
155
+ >
156
+ <div className='theia-notebook-cell-sidebar'>
157
+ <div className={'theia-notebook-cell-marker' + (this.state.selectedCell === cell ? ' theia-notebook-cell-marker-selected' : '')}></div>
158
+ {this.renderCellSidebar(cell)}
159
+ </div>
146
160
  <div className='theia-notebook-cell-content'>
147
161
  {this.renderCellContent(cell, index)}
148
162
  </div>
@@ -152,7 +166,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
152
166
  })
153
167
  }
154
168
  </li>
155
- {this.shouldRenderDragOverIndicator(cell, 'bottom') && <CellDropIndicator />}
169
+ <CellDropIndicator visible={this.shouldRenderDragOverIndicator(cell, 'bottom')} />
156
170
  </React.Fragment>
157
171
  )
158
172
  }
@@ -173,13 +187,30 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
173
187
  return renderer.render(this.props.notebookModel, cell, index);
174
188
  }
175
189
 
176
- protected onDragStart(event: React.DragEvent<HTMLLIElement>, index: number, cell: NotebookCellModel): void {
190
+ renderCellSidebar(cell: NotebookCellModel): React.ReactNode {
191
+ const renderer = this.props.renderers.get(cell.cellKind);
192
+ if (!renderer) {
193
+ throw new Error(`No renderer found for cell type ${cell.cellKind}`);
194
+ }
195
+ return renderer.renderSidebar(this.props.notebookModel, cell);
196
+ }
197
+
198
+ protected onDragStart(event: React.DragEvent<HTMLElement>): void {
177
199
  event.stopPropagation();
178
200
  if (!this.isEnabled()) {
179
201
  event.preventDefault();
180
202
  return;
181
203
  }
182
204
 
205
+ const cellHandle = (event.target as HTMLLIElement).getAttribute('data-cell-handle');
206
+
207
+ if (!cellHandle) {
208
+ throw new Error('Cell handle not found in element for cell drag event');
209
+ }
210
+
211
+ const index = this.props.notebookModel.getCellIndexByHandle(parseInt(cellHandle));
212
+ const cell = this.props.notebookModel.cells[index];
213
+
183
214
  NotebookCellListView.dragGhost = document.createElement('div');
184
215
  NotebookCellListView.dragGhost.classList.add('theia-notebook-drag-ghost-image');
185
216
  NotebookCellListView.dragGhost.appendChild(this.props.renderers.get(cell.cellKind)?.renderDragImage(cell) ?? document.createElement('div'));
@@ -274,6 +305,6 @@ export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOve
274
305
  </li>;
275
306
  }
276
307
 
277
- function CellDropIndicator(): React.JSX.Element {
278
- return <div className='theia-notebook-cell-drop-indicator' />;
308
+ function CellDropIndicator(props: { visible: boolean }): React.JSX.Element {
309
+ return <div className='theia-notebook-cell-drop-indicator' style={{ visibility: props.visible ? 'visible' : 'hidden' }} />;
279
310
  }