@theia/notebook 1.50.0 → 1.51.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.
Files changed (78) hide show
  1. package/lib/browser/contributions/notebook-actions-contribution.js +2 -2
  2. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  3. package/lib/browser/contributions/notebook-preferences.d.ts +9 -1
  4. package/lib/browser/contributions/notebook-preferences.d.ts.map +1 -1
  5. package/lib/browser/contributions/notebook-preferences.js +53 -3
  6. package/lib/browser/contributions/notebook-preferences.js.map +1 -1
  7. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  8. package/lib/browser/notebook-editor-widget.js +2 -1
  9. package/lib/browser/notebook-editor-widget.js.map +1 -1
  10. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  11. package/lib/browser/notebook-frontend-module.js +2 -0
  12. package/lib/browser/notebook-frontend-module.js.map +1 -1
  13. package/lib/browser/service/notebook-editor-widget-service.d.ts +5 -4
  14. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
  15. package/lib/browser/service/notebook-editor-widget-service.js +3 -0
  16. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
  17. package/lib/browser/service/notebook-execution-service.d.ts +2 -2
  18. package/lib/browser/service/notebook-execution-service.d.ts.map +1 -1
  19. package/lib/browser/service/notebook-execution-service.js.map +1 -1
  20. package/lib/browser/service/notebook-execution-state-service.d.ts +13 -13
  21. package/lib/browser/service/notebook-execution-state-service.d.ts.map +1 -1
  22. package/lib/browser/service/notebook-execution-state-service.js.map +1 -1
  23. package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts +5 -5
  24. package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts.map +1 -1
  25. package/lib/browser/service/notebook-kernel-quick-pick-service.js.map +1 -1
  26. package/lib/browser/service/notebook-kernel-service.d.ts +4 -4
  27. package/lib/browser/service/notebook-kernel-service.d.ts.map +1 -1
  28. package/lib/browser/service/notebook-kernel-service.js.map +1 -1
  29. package/lib/browser/service/notebook-options.d.ts +29 -0
  30. package/lib/browser/service/notebook-options.d.ts.map +1 -0
  31. package/lib/browser/service/notebook-options.js +129 -0
  32. package/lib/browser/service/notebook-options.js.map +1 -0
  33. package/lib/browser/service/notebook-renderer-messaging-service.d.ts +8 -6
  34. package/lib/browser/service/notebook-renderer-messaging-service.d.ts.map +1 -1
  35. package/lib/browser/service/notebook-renderer-messaging-service.js.map +1 -1
  36. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  37. package/lib/browser/view/notebook-cell-editor.js +2 -1
  38. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  39. package/lib/browser/view/notebook-cell-list-view.d.ts +1 -1
  40. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  41. package/lib/browser/view/notebook-cell-list-view.js +10 -9
  42. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  43. package/lib/browser/view/notebook-code-cell-view.d.ts +5 -4
  44. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  45. package/lib/browser/view/notebook-code-cell-view.js +43 -19
  46. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  47. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  48. package/lib/browser/view/notebook-markdown-cell-view.js +5 -2
  49. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  50. package/lib/browser/view-model/notebook-cell-model.js +3 -3
  51. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  52. package/lib/browser/view-model/notebook-cell-output-model.d.ts +1 -1
  53. package/lib/browser/view-model/notebook-cell-output-model.d.ts.map +1 -1
  54. package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
  55. package/lib/browser/view-model/notebook-model.d.ts +2 -0
  56. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  57. package/lib/browser/view-model/notebook-model.js +6 -3
  58. package/lib/browser/view-model/notebook-model.js.map +1 -1
  59. package/package.json +8 -8
  60. package/src/browser/contributions/notebook-actions-contribution.ts +2 -2
  61. package/src/browser/contributions/notebook-preferences.ts +54 -2
  62. package/src/browser/notebook-editor-widget.tsx +3 -1
  63. package/src/browser/notebook-frontend-module.ts +2 -0
  64. package/src/browser/service/notebook-editor-widget-service.ts +7 -4
  65. package/src/browser/service/notebook-execution-service.ts +2 -2
  66. package/src/browser/service/notebook-execution-state-service.ts +12 -12
  67. package/src/browser/service/notebook-kernel-quick-pick-service.ts +5 -5
  68. package/src/browser/service/notebook-kernel-service.ts +4 -4
  69. package/src/browser/service/notebook-options.ts +154 -0
  70. package/src/browser/service/notebook-renderer-messaging-service.ts +6 -6
  71. package/src/browser/style/index.css +11 -2
  72. package/src/browser/view/notebook-cell-editor.tsx +3 -1
  73. package/src/browser/view/notebook-cell-list-view.tsx +10 -9
  74. package/src/browser/view/notebook-code-cell-view.tsx +40 -20
  75. package/src/browser/view/notebook-markdown-cell-view.tsx +4 -2
  76. package/src/browser/view-model/notebook-cell-model.ts +4 -4
  77. package/src/browser/view-model/notebook-cell-output-model.ts +1 -1
  78. package/src/browser/view-model/notebook-model.ts +7 -3
@@ -35,14 +35,14 @@ export class NotebookEditorWidgetService {
35
35
  @inject(ContextKeyService)
36
36
  protected contextKeyService: ContextKeyService;
37
37
 
38
- private readonly notebookEditors = new Map<string, NotebookEditorWidget>();
38
+ protected readonly notebookEditors = new Map<string, NotebookEditorWidget>();
39
39
 
40
- private readonly onNotebookEditorAddEmitter = new Emitter<NotebookEditorWidget>();
41
- private readonly onNotebookEditorRemoveEmitter = new Emitter<NotebookEditorWidget>();
40
+ protected readonly onNotebookEditorAddEmitter = new Emitter<NotebookEditorWidget>();
41
+ protected readonly onNotebookEditorRemoveEmitter = new Emitter<NotebookEditorWidget>();
42
42
  readonly onDidAddNotebookEditor = this.onNotebookEditorAddEmitter.event;
43
43
  readonly onDidRemoveNotebookEditor = this.onNotebookEditorRemoveEmitter.event;
44
44
 
45
- private readonly onDidChangeFocusedEditorEmitter = new Emitter<NotebookEditorWidget | undefined>();
45
+ protected readonly onDidChangeFocusedEditorEmitter = new Emitter<NotebookEditorWidget | undefined>();
46
46
  readonly onDidChangeFocusedEditor = this.onDidChangeFocusedEditorEmitter.event;
47
47
 
48
48
  focusedEditor?: NotebookEditorWidget = undefined;
@@ -62,6 +62,9 @@ export class NotebookEditorWidgetService {
62
62
  }
63
63
  this.notebookEditors.set(editor.id, editor);
64
64
  this.onNotebookEditorAddEmitter.fire(editor);
65
+ if (editor.isVisible) {
66
+ this.notebookEditorFocusChanged(editor, true);
67
+ }
65
68
  }
66
69
 
67
70
  removeNotebookEditor(editor: NotebookEditorWidget): void {
@@ -50,7 +50,7 @@ export class NotebookExecutionService {
50
50
  @inject(NotebookKernelQuickPickService)
51
51
  protected notebookKernelQuickPickService: NotebookKernelQuickPickService;
52
52
 
53
- private readonly cellExecutionParticipants = new Set<CellExecutionParticipant>();
53
+ protected readonly cellExecutionParticipants = new Set<CellExecutionParticipant>();
54
54
 
55
55
  async executeNotebookCells(notebook: NotebookModel, cells: Iterable<NotebookCellModel>): Promise<void> {
56
56
  const cellsArr = Array.from(cells)
@@ -117,7 +117,7 @@ export class NotebookExecutionService {
117
117
  return Disposable.create(() => this.cellExecutionParticipants.delete(participant));
118
118
  }
119
119
 
120
- private async runExecutionParticipants(executions: CellExecution[]): Promise<void> {
120
+ protected async runExecutionParticipants(executions: CellExecution[]): Promise<void> {
121
121
  for (const participant of this.cellExecutionParticipants) {
122
122
  await participant.onWillExecuteCell(executions);
123
123
  }
@@ -69,10 +69,10 @@ export class NotebookExecutionStateService implements Disposable {
69
69
 
70
70
  protected readonly executions = new Map<string, Map<number, CellExecution>>();
71
71
 
72
- private readonly onDidChangeExecutionEmitter = new Emitter<CellExecutionStateChangedEvent>();
72
+ protected readonly onDidChangeExecutionEmitter = new Emitter<CellExecutionStateChangedEvent>();
73
73
  onDidChangeExecution = this.onDidChangeExecutionEmitter.event;
74
74
 
75
- private readonly onDidChangeLastRunFailStateEmitter = new Emitter<NotebookFailStateChangedEvent>();
75
+ protected readonly onDidChangeLastRunFailStateEmitter = new Emitter<NotebookFailStateChangedEvent>();
76
76
  onDidChangeLastRunFailState = this.onDidChangeLastRunFailStateEmitter.event;
77
77
 
78
78
  getOrCreateCellExecution(notebookUri: URI, cellHandle: number): CellExecution {
@@ -98,7 +98,7 @@ export class NotebookExecutionStateService implements Disposable {
98
98
 
99
99
  }
100
100
 
101
- private createNotebookCellExecution(notebook: NotebookModel, cellHandle: number): CellExecution {
101
+ protected createNotebookCellExecution(notebook: NotebookModel, cellHandle: number): CellExecution {
102
102
  const notebookUri = notebook.uri;
103
103
  const execution = new CellExecution(cellHandle, notebook);
104
104
  execution.toDispose.push(execution.onDidUpdate(() => this.onDidChangeExecutionEmitter.fire(new CellExecutionStateChangedEvent(notebookUri, cellHandle, execution))));
@@ -107,7 +107,7 @@ export class NotebookExecutionStateService implements Disposable {
107
107
  return execution;
108
108
  }
109
109
 
110
- private onCellExecutionDidComplete(notebookUri: URI, cellHandle: number, exe: CellExecution, lastRunSuccess?: boolean): void {
110
+ protected onCellExecutionDidComplete(notebookUri: URI, cellHandle: number, exe: CellExecution, lastRunSuccess?: boolean): void {
111
111
  const notebookExecutions = this.executions.get(notebookUri.toString())?.get(cellHandle);
112
112
  if (!notebookExecutions) {
113
113
  throw new Error('Notebook Cell Execution not found while trying to complete it');
@@ -138,15 +138,15 @@ export class NotebookExecutionStateService implements Disposable {
138
138
  }
139
139
 
140
140
  export class CellExecution implements Disposable {
141
- private readonly onDidUpdateEmitter = new Emitter<void>();
141
+ protected readonly onDidUpdateEmitter = new Emitter<void>();
142
142
  readonly onDidUpdate = this.onDidUpdateEmitter.event;
143
143
 
144
- private readonly onDidCompleteEmitter = new Emitter<boolean | undefined>();
144
+ protected readonly onDidCompleteEmitter = new Emitter<boolean | undefined>();
145
145
  readonly onDidComplete = this.onDidCompleteEmitter.event;
146
146
 
147
147
  toDispose = new DisposableCollection();
148
148
 
149
- private _state: NotebookCellExecutionState = NotebookCellExecutionState.Unconfirmed;
149
+ protected _state: NotebookCellExecutionState = NotebookCellExecutionState.Unconfirmed;
150
150
  get state(): NotebookCellExecutionState {
151
151
  return this._state;
152
152
  }
@@ -155,19 +155,19 @@ export class CellExecution implements Disposable {
155
155
  return this.notebook.uri;
156
156
  }
157
157
 
158
- private _didPause = false;
158
+ protected _didPause = false;
159
159
  get didPause(): boolean {
160
160
  return this._didPause;
161
161
  }
162
162
 
163
- private _isPaused = false;
163
+ protected _isPaused = false;
164
164
  get isPaused(): boolean {
165
165
  return this._isPaused;
166
166
  }
167
167
 
168
168
  constructor(
169
169
  readonly cellHandle: number,
170
- private readonly notebook: NotebookModel,
170
+ protected readonly notebook: NotebookModel,
171
171
  ) {
172
172
  console.debug(`CellExecution#ctor ${this.getCellLog()}`);
173
173
  }
@@ -188,7 +188,7 @@ export class CellExecution implements Disposable {
188
188
  this.applyCellExecutionEditsToNotebook([startExecuteEdit]);
189
189
  }
190
190
 
191
- private getCellLog(): string {
191
+ protected getCellLog(): string {
192
192
  return `${this.notebookURI.toString()}, ${this.cellHandle}`;
193
193
  }
194
194
 
@@ -254,7 +254,7 @@ export class CellExecution implements Disposable {
254
254
  this.toDispose.dispose();
255
255
  }
256
256
 
257
- private applyCellExecutionEditsToNotebook(edits: CellEditOperation[]): void {
257
+ protected applyCellExecutionEditsToNotebook(edits: CellEditOperation[]): void {
258
258
  this.notebook.applyEdits(edits, false);
259
259
  }
260
260
  }
@@ -277,7 +277,7 @@ export class NotebookKernelQuickPickService {
277
277
  return true;
278
278
  }
279
279
 
280
- private async displaySelectAnotherQuickPick(editor: NotebookModel, kernelListEmpty: boolean): Promise<boolean> {
280
+ protected async displaySelectAnotherQuickPick(editor: NotebookModel, kernelListEmpty: boolean): Promise<boolean> {
281
281
  const notebook: NotebookModel = editor;
282
282
  const disposables = new DisposableCollection();
283
283
  const quickPick = this.quickInputService.createQuickPick<KernelQuickPickItem>();
@@ -399,11 +399,11 @@ export class NotebookKernelQuickPickService {
399
399
  return false;
400
400
  }
401
401
 
402
- private isUri(value: string): boolean {
402
+ protected isUri(value: string): boolean {
403
403
  return /^(?<scheme>\w[\w\d+.-]*):/.test(value);
404
404
  }
405
405
 
406
- private async calculateKernelSources(editor: NotebookModel): Promise<QuickPickInput<KernelQuickPickItem>[]> {
406
+ protected async calculateKernelSources(editor: NotebookModel): Promise<QuickPickInput<KernelQuickPickItem>[]> {
407
407
  const notebook: NotebookModel = editor;
408
408
 
409
409
  const actions = await this.notebookKernelService.getKernelSourceActionsFromProviders(notebook);
@@ -448,7 +448,7 @@ export class NotebookKernelQuickPickService {
448
448
  return quickPickItems;
449
449
  }
450
450
 
451
- private async selectOneKernel(notebook: NotebookModel, source: string, kernels: NotebookKernel[]): Promise<void> {
451
+ protected async selectOneKernel(notebook: NotebookModel, source: string, kernels: NotebookKernel[]): Promise<void> {
452
452
  const quickPickItems: QuickPickInput<KernelPick>[] = kernels.map(kernel => toKernelQuickPick(kernel, undefined));
453
453
  const quickPick = this.quickInputService.createQuickPick<KernelQuickPickItem>();
454
454
  quickPick.items = quickPickItems;
@@ -472,7 +472,7 @@ export class NotebookKernelQuickPickService {
472
472
  quickPick.show();
473
473
  }
474
474
 
475
- private async executeCommand<T>(notebook: NotebookModel, command: NotebookCommand): Promise<T | undefined | void> {
475
+ protected async executeCommand<T>(notebook: NotebookModel, command: NotebookCommand): Promise<T | undefined | void> {
476
476
  const args = (command.arguments || []).concat([NotebookModelResource.create(notebook.uri)]);
477
477
  return this.commandService.executeCommand(command.id, ...args);
478
478
  }
@@ -84,7 +84,7 @@ export interface NotebookTextModelLike { uri: URI; viewType: string }
84
84
 
85
85
  class KernelInfo {
86
86
 
87
- private static instanceCounter = 0;
87
+ protected static instanceCounter = 0;
88
88
 
89
89
  score: number;
90
90
  readonly kernel: NotebookKernel;
@@ -130,7 +130,7 @@ export class SourceCommand implements Disposable {
130
130
  this.onDidChangeStateEmitter.fire();
131
131
  }
132
132
 
133
- private async runCommand(commandService: CommandService): Promise<void> {
133
+ protected async runCommand(commandService: CommandService): Promise<void> {
134
134
  try {
135
135
  await commandService.executeCommand(this.command.id, {
136
136
  uri: this.model.uri,
@@ -278,7 +278,7 @@ export class NotebookKernelService {
278
278
  return this.kernels.get(id)?.kernel;
279
279
  }
280
280
 
281
- private static score(kernel: NotebookKernel, notebook: NotebookTextModelLike): number {
281
+ protected static score(kernel: NotebookKernel, notebook: NotebookTextModelLike): number {
282
282
  if (kernel.viewType === notebook.viewType) {
283
283
  return 10;
284
284
  } else if (kernel.viewType === '*') {
@@ -288,7 +288,7 @@ export class NotebookKernelService {
288
288
  }
289
289
  }
290
290
 
291
- private tryAutoBindNotebook(notebook: NotebookModel, onlyThisKernel?: NotebookKernel): void {
291
+ protected tryAutoBindNotebook(notebook: NotebookModel, onlyThisKernel?: NotebookKernel): void {
292
292
 
293
293
  const id = this.notebookBindings[`${notebook.viewType}/${notebook.uri}`];
294
294
  if (!id) {
@@ -0,0 +1,154 @@
1
+
2
+ // *****************************************************************************
3
+ // Copyright (C) 2024 TypeFox and others.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+
18
+ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
19
+ import { PreferenceService } from '@theia/core/lib/browser';
20
+ import { Emitter } from '@theia/core';
21
+ import { NotebookPreferences, notebookPreferenceSchema } from '../contributions/notebook-preferences';
22
+ import { EditorPreferences } from '@theia/editor/lib/browser';
23
+ import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
24
+ import { PixelRatio } from '@theia/monaco-editor-core/esm/vs/base/browser/browser';
25
+
26
+ const notebookOutputOptionsRelevantPreferences = [
27
+ 'editor.fontSize',
28
+ 'editor.fontFamily',
29
+ NotebookPreferences.NOTEBOOK_LINE_NUMBERS,
30
+ NotebookPreferences.OUTPUT_LINE_HEIGHT,
31
+ NotebookPreferences.OUTPUT_FONT_SIZE,
32
+ NotebookPreferences.OUTPUT_FONT_FAMILY,
33
+ NotebookPreferences.OUTPUT_SCROLLING,
34
+ NotebookPreferences.OUTPUT_WORD_WRAP,
35
+ NotebookPreferences.OUTPUT_LINE_LIMIT
36
+ ];
37
+
38
+ export interface NotebookOutputOptions {
39
+ // readonly outputNodePadding: number;
40
+ // readonly outputNodeLeftPadding: number;
41
+ // readonly previewNodePadding: number;
42
+ // readonly markdownLeftMargin: number;
43
+ // readonly leftMargin: number;
44
+ // readonly rightMargin: number;
45
+ // readonly runGutter: number;
46
+ // readonly dragAndDropEnabled: boolean;
47
+ readonly fontSize: number;
48
+ readonly outputFontSize?: number;
49
+ readonly fontFamily: string;
50
+ readonly outputFontFamily?: string;
51
+ // readonly markupFontSize: number;
52
+ // readonly markdownLineHeight: number;
53
+ readonly outputLineHeight: number;
54
+ readonly outputScrolling: boolean;
55
+ readonly outputWordWrap: boolean;
56
+ readonly outputLineLimit: number;
57
+ // readonly outputLinkifyFilePaths: boolean;
58
+ // readonly minimalError: boolean;
59
+
60
+ }
61
+
62
+ @injectable()
63
+ export class NotebookOptionsService {
64
+
65
+ @inject(PreferenceService)
66
+ protected readonly preferenceService: PreferenceService;
67
+
68
+ @inject(EditorPreferences)
69
+ protected readonly editorPreferences: EditorPreferences;
70
+
71
+ protected outputOptionsChangedEmitter = new Emitter<NotebookOutputOptions>();
72
+ onDidChangeOutputOptions = this.outputOptionsChangedEmitter.event;
73
+
74
+ protected fontInfo?: BareFontInfo;
75
+ get editorFontInfo(): BareFontInfo {
76
+ return this.getOrCreateMonacoFontInfo();
77
+ }
78
+
79
+ @postConstruct()
80
+ protected init(): void {
81
+ this.preferenceService.onPreferencesChanged(async preferenceChanges => {
82
+ if (notebookOutputOptionsRelevantPreferences.some(p => p in preferenceChanges)) {
83
+ this.outputOptionsChangedEmitter.fire(this.computeOutputOptions());
84
+ }
85
+ });
86
+ }
87
+
88
+ computeOutputOptions(): NotebookOutputOptions {
89
+ const outputLineHeight = this.getNotebookPreferenceWithDefault<number>(NotebookPreferences.OUTPUT_LINE_HEIGHT);
90
+
91
+ const fontSize = this.preferenceService.get<number>('editor.fontSize')!;
92
+ const outputFontSize = this.getNotebookPreferenceWithDefault<number>(NotebookPreferences.OUTPUT_FONT_SIZE);
93
+
94
+ return {
95
+ fontSize,
96
+ outputFontSize: outputFontSize,
97
+ fontFamily: this.preferenceService.get<string>('editor.fontFamily')!,
98
+ outputFontFamily: this.getNotebookPreferenceWithDefault<string>(NotebookPreferences.OUTPUT_FONT_FAMILY),
99
+ outputLineHeight: this.computeOutputLineHeight(outputLineHeight, outputFontSize ?? fontSize),
100
+ outputScrolling: this.getNotebookPreferenceWithDefault<boolean>(NotebookPreferences.OUTPUT_SCROLLING)!,
101
+ outputWordWrap: this.getNotebookPreferenceWithDefault<boolean>(NotebookPreferences.OUTPUT_WORD_WRAP)!,
102
+ outputLineLimit: this.getNotebookPreferenceWithDefault<number>(NotebookPreferences.OUTPUT_LINE_LIMIT)!
103
+ };
104
+ }
105
+
106
+ protected getNotebookPreferenceWithDefault<T>(key: string): T {
107
+ return this.preferenceService.get<T>(key, notebookPreferenceSchema.properties?.[key]?.default as T);
108
+ }
109
+
110
+ protected computeOutputLineHeight(lineHeight: number, outputFontSize: number): number {
111
+ const minimumLineHeight = 9;
112
+
113
+ if (lineHeight === 0) {
114
+ // use editor line height
115
+ lineHeight = this.editorFontInfo.lineHeight;
116
+ } else if (lineHeight < minimumLineHeight) {
117
+ // Values too small to be line heights in pixels are in ems.
118
+ let fontSize = outputFontSize;
119
+ if (fontSize === 0) {
120
+ fontSize = this.preferenceService.get<number>('editor.fontSize')!;
121
+ }
122
+
123
+ lineHeight = lineHeight * fontSize;
124
+ }
125
+
126
+ // Enforce integer, minimum constraints
127
+ lineHeight = Math.round(lineHeight);
128
+ if (lineHeight < minimumLineHeight) {
129
+ lineHeight = minimumLineHeight;
130
+ }
131
+
132
+ return lineHeight;
133
+ }
134
+
135
+ protected getOrCreateMonacoFontInfo(): BareFontInfo {
136
+ if (!this.fontInfo) {
137
+ this.fontInfo = this.createFontInfo();
138
+ this.editorPreferences.onPreferenceChanged(e => this.fontInfo = this.createFontInfo());
139
+ }
140
+ return this.fontInfo;
141
+ }
142
+
143
+ protected createFontInfo(): BareFontInfo {
144
+ return BareFontInfo.createFromRawSettings({
145
+ fontFamily: this.editorPreferences['editor.fontFamily'],
146
+ fontWeight: String(this.editorPreferences['editor.fontWeight']),
147
+ fontSize: this.editorPreferences['editor.fontSize'],
148
+ fontLigatures: this.editorPreferences['editor.fontLigatures'],
149
+ lineHeight: this.editorPreferences['editor.lineHeight'],
150
+ letterSpacing: this.editorPreferences['editor.letterSpacing'],
151
+ }, PixelRatio.value);
152
+ }
153
+
154
+ }
@@ -45,17 +45,17 @@ export interface RendererMessaging extends Disposable {
45
45
  @injectable()
46
46
  export class NotebookRendererMessagingService implements Disposable {
47
47
 
48
- private readonly postMessageEmitter = new Emitter<RendererMessage>();
48
+ protected readonly postMessageEmitter = new Emitter<RendererMessage>();
49
49
  readonly onPostMessage = this.postMessageEmitter.event;
50
50
 
51
- private readonly willActivateRendererEmitter = new Emitter<string>();
51
+ protected readonly willActivateRendererEmitter = new Emitter<string>();
52
52
  readonly onWillActivateRenderer = this.willActivateRendererEmitter.event;
53
53
 
54
54
  @inject(NotebookEditorWidgetService)
55
- private readonly editorWidgetService: NotebookEditorWidgetService;
55
+ protected readonly editorWidgetService: NotebookEditorWidgetService;
56
56
 
57
- private readonly activations = new Map<string /* rendererId */, undefined | RendererMessage[]>();
58
- private readonly scopedMessaging = new Map<string /* editorId */, RendererMessaging>();
57
+ protected readonly activations = new Map<string /* rendererId */, undefined | RendererMessage[]>();
58
+ protected readonly scopedMessaging = new Map<string /* editorId */, RendererMessaging>();
59
59
 
60
60
  receiveMessage(editorId: string | undefined, rendererId: string, message: unknown): Promise<boolean> {
61
61
  if (editorId === undefined) {
@@ -101,7 +101,7 @@ export class NotebookRendererMessagingService implements Disposable {
101
101
  return messaging;
102
102
  }
103
103
 
104
- private postMessage(editorId: string, rendererId: string, message: unknown): void {
104
+ protected postMessage(editorId: string, rendererId: string, message: unknown): void {
105
105
  if (!this.activations.has(rendererId)) {
106
106
  this.prepare(rendererId);
107
107
  }
@@ -187,6 +187,14 @@
187
187
  overflow: hidden;
188
188
  }
189
189
 
190
+ .theia-notebook-main-container .theia-notebook-main-loading-indicator {
191
+ /* `progress-animation` is defined in `packages/core/src/browser/style/progress-bar.css` */
192
+ animation: progress-animation 1.8s 0s infinite
193
+ cubic-bezier(0.645, 0.045, 0.355, 1);
194
+ background-color: var(--theia-progressBar-background);
195
+ height: 2px;
196
+ }
197
+
190
198
  .theia-notebook-viewport {
191
199
  display: flex;
192
200
  overflow: hidden;
@@ -286,6 +294,7 @@
286
294
  position: absolute;
287
295
  top: -99999px;
288
296
  left: -99999px;
289
- height: 500px;
290
- width: 500px;
297
+ max-height: 500px;
298
+ min-height: 100px;
299
+ background-color: var(--theia-editor-background);
291
300
  }
@@ -26,6 +26,7 @@ import { DisposableCollection, OS } from '@theia/core';
26
26
  import { NotebookViewportService } from './notebook-viewport-service';
27
27
  import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
28
28
  import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE } from '../contributions/notebook-context-keys';
29
+ import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorExtensions';
29
30
 
30
31
  interface CellEditorProps {
31
32
  notebookModel: NotebookModel,
@@ -117,7 +118,8 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
117
118
  editorNode,
118
119
  monacoServices,
119
120
  { ...DEFAULT_EDITOR_OPTIONS, ...cell.editorOptions },
120
- [[IContextKeyService, this.props.notebookContextManager.scopedStore]]);
121
+ [[IContextKeyService, this.props.notebookContextManager.scopedStore]],
122
+ { contributions: EditorExtensionsRegistry.getEditorContributions().filter(c => c.id !== 'editor.contrib.findController') });
121
123
  this.toDispose.push(this.editor);
122
124
  this.editor.setLanguage(cell.language);
123
125
  this.toDispose.push(this.editor.getControl().onDidContentSizeChange(() => {
@@ -45,7 +45,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
45
45
 
46
46
  protected toDispose = new DisposableCollection();
47
47
 
48
- protected dragGhost: HTMLElement | undefined;
48
+ protected static dragGhost: HTMLElement | undefined;
49
49
 
50
50
  constructor(props: CellListProps) {
51
51
  super(props);
@@ -96,6 +96,10 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
96
96
  this.props.notebookModel.setSelectedCell(cell, false);
97
97
  }}
98
98
  onDragStart={e => this.onDragStart(e, index, cell)}
99
+ onDragEnd={e => {
100
+ NotebookCellListView.dragGhost?.remove();
101
+ this.setState({ ...this.state, dragOverIndicator: undefined });
102
+ }}
99
103
  onDragOver={e => this.onDragOver(e, cell)}
100
104
  onDrop={e => this.onDrop(e, index)}
101
105
  draggable={true}
@@ -137,14 +141,11 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
137
141
  return;
138
142
  }
139
143
 
140
- if (this.dragGhost) {
141
- this.dragGhost.remove();
142
- }
143
- this.dragGhost = document.createElement('div');
144
- this.dragGhost.classList.add('theia-notebook-drag-ghost-image');
145
- this.dragGhost.appendChild(this.props.renderers.get(cell.cellKind)?.renderDragImage(cell) ?? document.createElement('div'));
146
- document.body.appendChild(this.dragGhost);
147
- event.dataTransfer.setDragImage(this.dragGhost, -10, 0);
144
+ NotebookCellListView.dragGhost = document.createElement('div');
145
+ NotebookCellListView.dragGhost.classList.add('theia-notebook-drag-ghost-image');
146
+ NotebookCellListView.dragGhost.appendChild(this.props.renderers.get(cell.cellKind)?.renderDragImage(cell) ?? document.createElement('div'));
147
+ document.body.appendChild(NotebookCellListView.dragGhost);
148
+ event.dataTransfer.setDragImage(NotebookCellListView.dragGhost, -10, 0);
148
149
 
149
150
  event.dataTransfer.setData('text/theia-notebook-cell-index', index.toString());
150
151
  event.dataTransfer.setData('text/plain', this.props.notebookModel.cells[index].source);
@@ -32,8 +32,9 @@ import { CommandRegistry, DisposableCollection, nls } from '@theia/core';
32
32
  import { NotebookContextManager } from '../service/notebook-context-manager';
33
33
  import { NotebookViewportService } from './notebook-viewport-service';
34
34
  import { EditorPreferences } from '@theia/editor/lib/browser';
35
- import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
36
- import { PixelRatio } from '@theia/monaco-editor-core/esm/vs/base/browser/browser';
35
+ import { NotebookOptionsService } from '../service/notebook-options';
36
+ import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
37
+ import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent';
37
38
 
38
39
  @injectable()
39
40
  export class NotebookCodeCellRenderer implements CellRenderer {
@@ -64,7 +65,11 @@ export class NotebookCodeCellRenderer implements CellRenderer {
64
65
  @inject(CommandRegistry)
65
66
  protected readonly commandRegistry: CommandRegistry;
66
67
 
67
- protected fontInfo: BareFontInfo | undefined;
68
+ @inject(NotebookOptionsService)
69
+ protected readonly notebookOptionsService: NotebookOptionsService;
70
+
71
+ @inject(MarkdownRenderer)
72
+ protected readonly markdownRenderer: MarkdownRenderer;
68
73
 
69
74
  render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode {
70
75
  return <div>
@@ -81,7 +86,7 @@ export class NotebookCodeCellRenderer implements CellRenderer {
81
86
  monacoServices={this.monacoServices}
82
87
  notebookContextManager={this.notebookContextManager}
83
88
  notebookViewportService={this.notebookViewportService}
84
- fontInfo={this.getOrCreateMonacoFontInfo()} />
89
+ fontInfo={this.notebookOptionsService.editorFontInfo} />
85
90
  <NotebookCodeCellStatus cell={cell} notebook={notebookModel}
86
91
  commandRegistry={this.commandRegistry}
87
92
  executionStateService={this.executionStateService}
@@ -102,28 +107,43 @@ export class NotebookCodeCellRenderer implements CellRenderer {
102
107
  renderDragImage(cell: NotebookCellModel): HTMLElement {
103
108
  const dragImage = document.createElement('div');
104
109
  dragImage.className = 'theia-notebook-drag-image';
105
- dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/codeText', 'Code cell selected');
110
+ dragImage.style.width = this.notebookContextManager.context?.clientWidth + 'px';
111
+ dragImage.style.height = '100px';
112
+ dragImage.style.display = 'flex';
113
+
114
+ const fakeRunButton = document.createElement('span');
115
+ fakeRunButton.className = `${codicon('play')} theia-notebook-cell-status-item`;
116
+ dragImage.appendChild(fakeRunButton);
117
+
118
+ const fakeEditor = document.createElement('div');
119
+ dragImage.appendChild(fakeEditor);
120
+ const lines = cell.source.split('\n').slice(0, 5).join('\n');
121
+ const codeSequence = this.getMarkdownCodeSequence(lines);
122
+ const firstLine = new MarkdownString(`${codeSequence}${cell.language}\n${lines}\n${codeSequence}`, { supportHtml: true, isTrusted: false });
123
+ fakeEditor.appendChild(this.markdownRenderer.render(firstLine).element);
124
+ fakeEditor.classList.add('theia-notebook-cell-editor-container');
125
+ fakeEditor.style.padding = '10px';
106
126
  return dragImage;
107
127
  }
108
128
 
109
- protected getOrCreateMonacoFontInfo(): BareFontInfo {
110
- if (!this.fontInfo) {
111
- this.fontInfo = this.createFontInfo();
112
- this.editorPreferences.onPreferenceChanged(e => this.fontInfo = this.createFontInfo());
129
+ protected getMarkdownCodeSequence(input: string): string {
130
+ // We need a minimum of 3 backticks to start a code block.
131
+ let longest = 2;
132
+ let current = 0;
133
+ for (let i = 0; i < input.length; i++) {
134
+ const char = input.charAt(i);
135
+ if (char === '`') {
136
+ current++;
137
+ if (current > longest) {
138
+ longest = current;
139
+ }
140
+ } else {
141
+ current = 0;
142
+ }
113
143
  }
114
- return this.fontInfo;
144
+ return Array(longest + 1).fill('`').join('');
115
145
  }
116
146
 
117
- protected createFontInfo(): BareFontInfo {
118
- return BareFontInfo.createFromRawSettings({
119
- fontFamily: this.editorPreferences['editor.fontFamily'],
120
- fontWeight: String(this.editorPreferences['editor.fontWeight']),
121
- fontSize: this.editorPreferences['editor.fontSize'],
122
- fontLigatures: this.editorPreferences['editor.fontLigatures'],
123
- lineHeight: this.editorPreferences['editor.lineHeight'],
124
- letterSpacing: this.editorPreferences['editor.letterSpacing'],
125
- }, PixelRatio.value);
126
- }
127
147
  }
128
148
 
129
149
  export interface NotebookCodeCellStatusProps {
@@ -44,8 +44,10 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
44
44
 
45
45
  renderDragImage(cell: NotebookCellModel): HTMLElement {
46
46
  const dragImage = document.createElement('div');
47
- dragImage.className = 'theia-notebook-drag-image';
48
- dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/markdownText', 'Mardown cell selected');
47
+ dragImage.style.width = this.notebookContextManager.context?.clientWidth + 'px';
48
+ const markdownString = new MarkdownStringImpl(cell.source, { supportHtml: true, isTrusted: true });
49
+ const markdownElement = this.markdownRenderer.render(markdownString).element;
50
+ dragImage.appendChild(markdownElement);
49
51
  return dragImage;
50
52
  }
51
53
  }
@@ -30,7 +30,7 @@ import { NotebookCellOutputsSplice } from '../notebook-types';
30
30
  import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service';
31
31
  import { NotebookCellOutputModel } from './notebook-cell-output-model';
32
32
  import { PreferenceService } from '@theia/core/lib/browser';
33
- import { NOTEBOOK_LINE_NUMBERS } from '../contributions/notebook-preferences';
33
+ import { NotebookPreferences } from '../contributions/notebook-preferences';
34
34
  import { LanguageService } from '@theia/core/lib/browser/language-service';
35
35
 
36
36
  export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
@@ -245,13 +245,13 @@ export class NotebookCellModel implements NotebookCell, Disposable {
245
245
  this._internalMetadata = this.props.internalMetadata ?? {};
246
246
 
247
247
  this.editorOptions = {
248
- lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS)
248
+ lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS)
249
249
  };
250
250
  this.toDispose.push(this.preferenceService.onPreferenceChanged(e => {
251
- if (e.preferenceName === NOTEBOOK_LINE_NUMBERS) {
251
+ if (e.preferenceName === NotebookPreferences.NOTEBOOK_LINE_NUMBERS) {
252
252
  this.editorOptions = {
253
253
  ...this.editorOptions,
254
- lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS)
254
+ lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS)
255
255
  };
256
256
  }
257
257
  }));
@@ -38,7 +38,7 @@ export class NotebookCellOutputModel implements Disposable {
38
38
  return this.rawOutput.metadata;
39
39
  }
40
40
 
41
- constructor(private rawOutput: CellOutput) { }
41
+ constructor(protected rawOutput: CellOutput) { }
42
42
 
43
43
  replaceData(rawData: CellOutput): void {
44
44
  this.rawOutput = rawData;