@theia/notebook 1.48.2 → 1.49.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 (124) hide show
  1. package/lib/browser/contributions/notebook-actions-contribution.d.ts +7 -1
  2. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  3. package/lib/browser/contributions/notebook-actions-contribution.js +81 -7
  4. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  5. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +5 -0
  6. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  7. package/lib/browser/contributions/notebook-cell-actions-contribution.js +86 -9
  8. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  9. package/lib/browser/contributions/notebook-label-provider-contribution.d.ts +16 -0
  10. package/lib/browser/contributions/notebook-label-provider-contribution.d.ts.map +1 -0
  11. package/lib/browser/contributions/notebook-label-provider-contribution.js +65 -0
  12. package/lib/browser/contributions/notebook-label-provider-contribution.js.map +1 -0
  13. package/lib/browser/contributions/notebook-outline-contribution.d.ts +30 -0
  14. package/lib/browser/contributions/notebook-outline-contribution.d.ts.map +1 -0
  15. package/lib/browser/contributions/notebook-outline-contribution.js +109 -0
  16. package/lib/browser/contributions/notebook-outline-contribution.js.map +1 -0
  17. package/lib/browser/contributions/notebook-output-action-contribution.d.ts +16 -0
  18. package/lib/browser/contributions/notebook-output-action-contribution.d.ts.map +1 -0
  19. package/lib/browser/contributions/notebook-output-action-contribution.js +85 -0
  20. package/lib/browser/contributions/notebook-output-action-contribution.js.map +1 -0
  21. package/lib/browser/contributions/notebook-preferences.d.ts +4 -0
  22. package/lib/browser/contributions/notebook-preferences.d.ts.map +1 -0
  23. package/lib/browser/contributions/notebook-preferences.js +31 -0
  24. package/lib/browser/contributions/notebook-preferences.js.map +1 -0
  25. package/lib/browser/notebook-cell-resource-resolver.d.ts +4 -0
  26. package/lib/browser/notebook-cell-resource-resolver.d.ts.map +1 -1
  27. package/lib/browser/notebook-cell-resource-resolver.js +35 -2
  28. package/lib/browser/notebook-cell-resource-resolver.js.map +1 -1
  29. package/lib/browser/notebook-editor-widget-factory.d.ts +1 -0
  30. package/lib/browser/notebook-editor-widget-factory.d.ts.map +1 -1
  31. package/lib/browser/notebook-editor-widget-factory.js +17 -3
  32. package/lib/browser/notebook-editor-widget-factory.js.map +1 -1
  33. package/lib/browser/notebook-editor-widget.d.ts +5 -1
  34. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  35. package/lib/browser/notebook-editor-widget.js +20 -2
  36. package/lib/browser/notebook-editor-widget.js.map +1 -1
  37. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  38. package/lib/browser/notebook-frontend-module.js +15 -2
  39. package/lib/browser/notebook-frontend-module.js.map +1 -1
  40. package/lib/browser/notebook-open-handler.d.ts +14 -9
  41. package/lib/browser/notebook-open-handler.d.ts.map +1 -1
  42. package/lib/browser/notebook-open-handler.js +38 -16
  43. package/lib/browser/notebook-open-handler.js.map +1 -1
  44. package/lib/browser/notebook-type-registry.d.ts +5 -1
  45. package/lib/browser/notebook-type-registry.d.ts.map +1 -1
  46. package/lib/browser/notebook-type-registry.js +27 -7
  47. package/lib/browser/notebook-type-registry.js.map +1 -1
  48. package/lib/browser/notebook-types.d.ts +2 -0
  49. package/lib/browser/notebook-types.d.ts.map +1 -1
  50. package/lib/browser/notebook-types.js.map +1 -1
  51. package/lib/browser/service/notebook-clipboard-service.d.ts +10 -0
  52. package/lib/browser/service/notebook-clipboard-service.d.ts.map +1 -0
  53. package/lib/browser/service/notebook-clipboard-service.js +42 -0
  54. package/lib/browser/service/notebook-clipboard-service.js.map +1 -0
  55. package/lib/browser/service/notebook-context-manager.d.ts +4 -1
  56. package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
  57. package/lib/browser/service/notebook-context-manager.js +33 -12
  58. package/lib/browser/service/notebook-context-manager.js.map +1 -1
  59. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  60. package/lib/browser/service/notebook-service.js +4 -0
  61. package/lib/browser/service/notebook-service.js.map +1 -1
  62. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  63. package/lib/browser/view/notebook-cell-editor.js +11 -2
  64. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  65. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  66. package/lib/browser/view/notebook-cell-list-view.js +4 -2
  67. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  68. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +6 -4
  69. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
  70. package/lib/browser/view/notebook-cell-toolbar-factory.js +21 -18
  71. package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
  72. package/lib/browser/view/notebook-cell-toolbar.d.ts.map +1 -1
  73. package/lib/browser/view/notebook-cell-toolbar.js +3 -2
  74. package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
  75. package/lib/browser/view/notebook-code-cell-view.d.ts +5 -1
  76. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  77. package/lib/browser/view/notebook-code-cell-view.js +45 -17
  78. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  79. package/lib/browser/view/notebook-main-toolbar.d.ts +5 -3
  80. package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
  81. package/lib/browser/view/notebook-main-toolbar.js +17 -9
  82. package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
  83. package/lib/browser/view/notebook-markdown-cell-view.js +11 -8
  84. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  85. package/lib/browser/view-model/notebook-cell-model.d.ts +20 -0
  86. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  87. package/lib/browser/view-model/notebook-cell-model.js +61 -2
  88. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  89. package/lib/browser/view-model/notebook-model.d.ts +4 -2
  90. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  91. package/lib/browser/view-model/notebook-model.js +25 -14
  92. package/lib/browser/view-model/notebook-model.js.map +1 -1
  93. package/lib/common/notebook-common.d.ts +7 -1
  94. package/lib/common/notebook-common.d.ts.map +1 -1
  95. package/lib/common/notebook-common.js +28 -4
  96. package/lib/common/notebook-common.js.map +1 -1
  97. package/package.json +9 -7
  98. package/src/browser/contributions/notebook-actions-contribution.ts +90 -9
  99. package/src/browser/contributions/notebook-cell-actions-contribution.ts +88 -11
  100. package/src/browser/contributions/notebook-label-provider-contribution.ts +63 -0
  101. package/src/browser/contributions/notebook-outline-contribution.ts +112 -0
  102. package/src/browser/contributions/notebook-output-action-contribution.ts +82 -0
  103. package/src/browser/contributions/notebook-preferences.ts +31 -0
  104. package/src/browser/notebook-cell-resource-resolver.ts +39 -1
  105. package/src/browser/notebook-editor-widget-factory.ts +18 -5
  106. package/src/browser/notebook-editor-widget.tsx +20 -2
  107. package/src/browser/notebook-frontend-module.ts +20 -4
  108. package/src/browser/notebook-open-handler.ts +48 -20
  109. package/src/browser/notebook-type-registry.ts +26 -6
  110. package/src/browser/notebook-types.ts +2 -0
  111. package/src/browser/service/notebook-clipboard-service.ts +43 -0
  112. package/src/browser/service/notebook-context-manager.ts +36 -10
  113. package/src/browser/service/notebook-service.ts +4 -0
  114. package/src/browser/style/index.css +19 -4
  115. package/src/browser/view/notebook-cell-editor.tsx +12 -2
  116. package/src/browser/view/notebook-cell-list-view.tsx +5 -2
  117. package/src/browser/view/notebook-cell-toolbar-factory.tsx +17 -15
  118. package/src/browser/view/notebook-cell-toolbar.tsx +3 -2
  119. package/src/browser/view/notebook-code-cell-view.tsx +51 -18
  120. package/src/browser/view/notebook-main-toolbar.tsx +20 -11
  121. package/src/browser/view/notebook-markdown-cell-view.tsx +12 -7
  122. package/src/browser/view-model/notebook-cell-model.ts +70 -2
  123. package/src/browser/view-model/notebook-model.ts +29 -16
  124. package/src/common/notebook-common.ts +29 -4
@@ -0,0 +1,63 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { codicon, LabelProvider, LabelProviderContribution } from '@theia/core/lib/browser';
18
+ import { inject, injectable } from '@theia/core/shared/inversify';
19
+ import { CellKind } from '../../common';
20
+ import { NotebookService } from '../service/notebook-service';
21
+ import { NotebookCellOutlineNode } from './notebook-outline-contribution';
22
+ import type Token = require('markdown-it/lib/token');
23
+ import markdownit = require('@theia/core/shared/markdown-it');
24
+
25
+ @injectable()
26
+ export class NotebookLabelProviderContribution implements LabelProviderContribution {
27
+
28
+ @inject(NotebookService)
29
+ protected readonly notebookService: NotebookService;
30
+
31
+ @inject(LabelProvider)
32
+ protected readonly labelProvider: LabelProvider;
33
+
34
+ protected markdownIt = markdownit();
35
+
36
+ canHandle(element: object): number {
37
+ if (NotebookCellOutlineNode.is(element)) {
38
+ return 200;
39
+ }
40
+ return 0;
41
+ }
42
+
43
+ getIcon(element: NotebookCellOutlineNode): string {
44
+ return element.notebookCell.cellKind === CellKind.Markup ? codicon('markdown') : codicon('code');
45
+ }
46
+
47
+ getName(element: NotebookCellOutlineNode): string {
48
+ return element.notebookCell.cellKind === CellKind.Code ?
49
+ element.notebookCell.text.split('\n')[0] :
50
+ this.extractPlaintext(this.markdownIt.parse(element.notebookCell.text.split('\n')[0], {}));
51
+ }
52
+
53
+ getLongName(element: NotebookCellOutlineNode): string {
54
+ return element.notebookCell.cellKind === CellKind.Code ?
55
+ element.notebookCell.text.split('\n')[0] :
56
+ this.extractPlaintext(this.markdownIt.parse(element.notebookCell.text.split('\n')[0], {}));
57
+ }
58
+
59
+ extractPlaintext(parsedMarkdown: Token[]): string {
60
+ return parsedMarkdown.map(token => token.children ? this.extractPlaintext(token.children) : token.content).join('');
61
+ }
62
+
63
+ }
@@ -0,0 +1,112 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { inject, injectable } from '@theia/core/shared/inversify';
18
+ import { codicon, FrontendApplicationContribution, LabelProvider, TreeNode } from '@theia/core/lib/browser';
19
+ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
20
+ import { OutlineViewService } from '@theia/outline-view/lib/browser/outline-view-service';
21
+ import { NotebookModel } from '../view-model/notebook-model';
22
+ import { OutlineSymbolInformationNode } from '@theia/outline-view/lib/browser/outline-view-widget';
23
+ import { NotebookEditorWidget } from '../notebook-editor-widget';
24
+ import { NotebookCellModel } from '../view-model/notebook-cell-model';
25
+ import { DisposableCollection, URI } from '@theia/core';
26
+ import { CellKind, CellUri } from '../../common';
27
+ import { NotebookService } from '../service/notebook-service';
28
+ export interface NotebookCellOutlineNode extends OutlineSymbolInformationNode {
29
+ notebookCell: NotebookCellModel;
30
+ uri: URI;
31
+ }
32
+
33
+ export namespace NotebookCellOutlineNode {
34
+ export function is(element: object): element is NotebookCellOutlineNode {
35
+ return TreeNode.is(element) && OutlineSymbolInformationNode.is(element) && 'notebookCell' in element;
36
+ }
37
+ }
38
+
39
+ @injectable()
40
+ export class NotebookOutlineContribution implements FrontendApplicationContribution {
41
+
42
+ @inject(NotebookEditorWidgetService)
43
+ protected readonly notebookEditorWidgetService: NotebookEditorWidgetService;
44
+
45
+ @inject(OutlineViewService)
46
+ protected readonly outlineViewService: OutlineViewService;
47
+
48
+ @inject(LabelProvider)
49
+ protected readonly labelProvider: LabelProvider;
50
+
51
+ @inject(NotebookService)
52
+ protected readonly notebookService: NotebookService;
53
+
54
+ protected currentEditor?: NotebookEditorWidget;
55
+
56
+ protected editorListeners: DisposableCollection = new DisposableCollection();
57
+ protected editorModelListeners: DisposableCollection = new DisposableCollection();
58
+
59
+ onStart(): void {
60
+ this.notebookEditorWidgetService.onDidChangeFocusedEditor(editor => this.updateOutline(editor));
61
+
62
+ this.outlineViewService.onDidSelect(node => this.selectCell(node));
63
+ this.outlineViewService.onDidTapNode(node => this.selectCell(node));
64
+ }
65
+
66
+ protected async updateOutline(editor: NotebookEditorWidget | undefined): Promise<void> {
67
+ if (editor && !editor.isDisposed) {
68
+ await editor.ready;
69
+ this.currentEditor = editor;
70
+ this.editorListeners.dispose();
71
+ this.editorListeners.push(editor.onDidChangeVisibility(() => {
72
+ if (this.currentEditor === editor && !editor.isVisible) {
73
+ this.outlineViewService.publish([]);
74
+ }
75
+ }));
76
+ if (editor.model) {
77
+ this.editorModelListeners.dispose();
78
+ this.editorModelListeners.push(editor.model.onDidChangeSelectedCell(() => {
79
+ if (editor === this.currentEditor) {
80
+ this.updateOutline(editor);
81
+ }
82
+ }));
83
+ const roots = editor && editor.model && await this.createRoots(editor.model);
84
+ this.outlineViewService.publish(roots || []);
85
+ }
86
+ }
87
+ }
88
+
89
+ protected async createRoots(model: NotebookModel): Promise<OutlineSymbolInformationNode[] | undefined> {
90
+ return model.cells.map(cell => ({
91
+ id: cell.uri.toString(),
92
+ iconClass: cell.cellKind === CellKind.Markup ? codicon('markdown') : codicon('code'),
93
+ parent: undefined,
94
+ children: [],
95
+ selected: model.selectedCell === cell,
96
+ expanded: false,
97
+ notebookCell: cell,
98
+ uri: model.uri,
99
+ } as NotebookCellOutlineNode));
100
+ }
101
+
102
+ selectCell(node: object): void {
103
+ if (NotebookCellOutlineNode.is(node)) {
104
+ const parsed = CellUri.parse(node.notebookCell.uri);
105
+ const model = parsed && this.notebookService.getNotebookEditorModel(parsed.notebook);
106
+ if (model) {
107
+ model.setSelectedCell(node.notebookCell);
108
+ }
109
+ }
110
+ }
111
+
112
+ }
@@ -0,0 +1,82 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Command, CommandContribution, CommandRegistry } from '@theia/core';
18
+ import { inject, injectable } from '@theia/core/shared/inversify';
19
+ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
20
+ import { CellOutput, CellUri } from '../../common';
21
+ import { NotebookCellModel } from '../view-model/notebook-cell-model';
22
+ import { EditorManager } from '@theia/editor/lib/browser';
23
+
24
+ export namespace NotebookOutputCommands {
25
+ export const ENABLE_SCROLLING = Command.toDefaultLocalizedCommand({
26
+ id: 'cellOutput.enableScrolling',
27
+ });
28
+
29
+ export const OPEN_LARGE_OUTPUT = Command.toDefaultLocalizedCommand({
30
+ id: 'workbench.action.openLargeOutput',
31
+ label: 'Open Large Output'
32
+ });
33
+ }
34
+
35
+ @injectable()
36
+ export class NotebookOutputActionContribution implements CommandContribution {
37
+
38
+ @inject(NotebookEditorWidgetService)
39
+ protected readonly notebookEditorService: NotebookEditorWidgetService;
40
+
41
+ @inject(EditorManager)
42
+ protected readonly editorManager: EditorManager;
43
+
44
+ registerCommands(commands: CommandRegistry): void {
45
+ commands.registerCommand(NotebookOutputCommands.ENABLE_SCROLLING, {
46
+ execute: outputId => {
47
+ const [cell, output] = this.findOutputAndCell(outputId) ?? [];
48
+ if (cell && output?.metadata) {
49
+ output.metadata['scrollable'] = true;
50
+ cell.restartOutputRenderer(output.outputId);
51
+ }
52
+ }
53
+ });
54
+
55
+ commands.registerCommand(NotebookOutputCommands.OPEN_LARGE_OUTPUT, {
56
+ execute: outputId => {
57
+ const [cell, output] = this.findOutputAndCell(outputId) ?? [];
58
+ if (cell && output) {
59
+ this.editorManager.open(CellUri.generateCellOutputUri(CellUri.parse(cell.uri)!.notebook, output.outputId));
60
+ }
61
+ }
62
+ });
63
+ }
64
+
65
+ protected findOutputAndCell(output: string): [NotebookCellModel, CellOutput] | undefined {
66
+ const model = this.notebookEditorService.focusedEditor?.model;
67
+ if (!model) {
68
+ return undefined;
69
+ }
70
+
71
+ const outputId = output.slice(0, output.lastIndexOf('-'));
72
+
73
+ for (const cell of model.cells) {
74
+ for (const outputModel of cell.outputs) {
75
+ if (outputModel.outputId === outputId) {
76
+ return [cell, outputModel];
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ }
@@ -0,0 +1,31 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { nls } from '@theia/core';
18
+ import { PreferenceSchema } from '@theia/core/lib/browser';
19
+
20
+ export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers';
21
+
22
+ export const notebookPreferenceSchema: PreferenceSchema = {
23
+ properties: {
24
+ [NOTEBOOK_LINE_NUMBERS]: {
25
+ type: 'string',
26
+ enum: ['on', 'off'],
27
+ default: 'off',
28
+ description: nls.localizeByDefault('Controls the display of line numbers in the cell editor.')
29
+ },
30
+ }
31
+ };
@@ -65,7 +65,7 @@ export class NotebookCellResourceResolver implements ResourceResolver {
65
65
  protected readonly notebookService: NotebookService;
66
66
 
67
67
  async resolve(uri: URI): Promise<Resource> {
68
- if (uri.scheme !== CellUri.scheme) {
68
+ if (uri.scheme !== CellUri.cellUriScheme) {
69
69
  throw new Error(`Cannot resolve cell uri with scheme '${uri.scheme}'`);
70
70
  }
71
71
 
@@ -90,3 +90,41 @@ export class NotebookCellResourceResolver implements ResourceResolver {
90
90
  }
91
91
 
92
92
  }
93
+
94
+ @injectable()
95
+ export class NotebookOutputResourceResolver implements ResourceResolver {
96
+
97
+ @inject(NotebookService)
98
+ protected readonly notebookService: NotebookService;
99
+
100
+ async resolve(uri: URI): Promise<Resource> {
101
+ if (uri.scheme !== CellUri.outputUriScheme) {
102
+ throw new Error(`Cannot resolve output uri with scheme '${uri.scheme}'`);
103
+ }
104
+
105
+ const parsedUri = CellUri.parseCellOutputUri(uri);
106
+ if (!parsedUri) {
107
+ throw new Error(`Cannot parse uri '${uri.toString()}'`);
108
+ }
109
+
110
+ const notebookModel = this.notebookService.getNotebookEditorModel(parsedUri.notebook);
111
+
112
+ if (!notebookModel) {
113
+ throw new Error(`No notebook found for uri '${parsedUri.notebook}'`);
114
+ }
115
+
116
+ const ouputModel = notebookModel.cells.flatMap(cell => cell.outputs).find(output => output.outputId === parsedUri.outputId);
117
+
118
+ if (!ouputModel) {
119
+ throw new Error(`No output found with id '${parsedUri.outputId}' in '${parsedUri.notebook}'`);
120
+ }
121
+
122
+ return {
123
+ uri: uri,
124
+ dispose: () => { },
125
+ readContents: async () => ouputModel.outputs[0].data.toString(),
126
+ readOnly: true,
127
+ };
128
+ }
129
+
130
+ }
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { URI } from '@theia/core';
17
+ import { nls, URI } from '@theia/core';
18
18
  import { WidgetFactory, NavigatableWidgetOptions, LabelProvider } from '@theia/core/lib/browser';
19
19
  import { inject, injectable } from '@theia/core/shared/inversify';
20
20
  import { NotebookEditorWidget, NotebookEditorWidgetContainerFactory, NotebookEditorProps } from './notebook-editor-widget';
@@ -51,10 +51,13 @@ export class NotebookEditorWidgetFactory implements WidgetFactory {
51
51
 
52
52
  const editor = await this.createEditor(uri, options.notebookType);
53
53
 
54
- const icon = this.labelProvider.getIcon(uri);
55
- editor.title.label = this.labelProvider.getName(uri);
56
- editor.title.iconClass = icon + ' file-icon';
57
-
54
+ this.setLabels(editor, uri);
55
+ const labelListener = this.labelProvider.onDidChange(event => {
56
+ if (event.affects(uri)) {
57
+ this.setLabels(editor, uri);
58
+ }
59
+ });
60
+ editor.onDidDispose(() => labelListener.dispose());
58
61
  return editor;
59
62
  }
60
63
 
@@ -67,4 +70,14 @@ export class NotebookEditorWidgetFactory implements WidgetFactory {
67
70
  });
68
71
  }
69
72
 
73
+ private setLabels(editor: NotebookEditorWidget, uri: URI): void {
74
+ editor.title.caption = uri.path.fsPath();
75
+ if (editor.model?.readOnly) {
76
+ editor.title.caption += ` • ${nls.localizeByDefault('Read-only')}`;
77
+ }
78
+ const icon = this.labelProvider.getIcon(uri);
79
+ editor.title.label = this.labelProvider.getName(uri);
80
+ editor.title.iconClass = icon + ' file-icon';
81
+ }
82
+
70
83
  }
@@ -16,7 +16,7 @@
16
16
 
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { CommandRegistry, MenuModelRegistry, URI } from '@theia/core';
19
- import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable, lock, unlock } from '@theia/core/lib/browser';
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
21
  import { CellKind } from '../common';
22
22
  import { CellRenderer as CellRenderer, NotebookCellListView } from './view/notebook-cell-list-view';
@@ -32,6 +32,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
32
32
  import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
33
33
  import { NotebookContextManager } from './service/notebook-context-manager';
34
34
  import { NotebookViewportService } from './view/notebook-viewport-service';
35
+ import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution';
35
36
  const PerfectScrollbar = require('react-perfect-scrollbar');
36
37
 
37
38
  export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory');
@@ -43,6 +44,7 @@ export function createNotebookEditorWidgetContainer(parent: interfaces.Container
43
44
 
44
45
  child.bind(NotebookContextManager).toSelf().inSingletonScope();
45
46
  child.bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope();
47
+ child.bind(NotebookCellToolbarFactory).toSelf().inSingletonScope();
46
48
  child.bind(NotebookCodeCellRenderer).toSelf().inSingletonScope();
47
49
  child.bind(NotebookMarkdownCellRenderer).toSelf().inSingletonScope();
48
50
  child.bind(NotebookViewportService).toSelf().inSingletonScope();
@@ -116,11 +118,12 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
116
118
  readonly onPostRendererMessage = this.onPostRendererMessageEmitter.event;
117
119
 
118
120
  protected readonly onDidReceiveKernelMessageEmitter = new Emitter<unknown>();
119
- readonly onDidRecieveKernelMessage = this.onDidReceiveKernelMessageEmitter.event;
121
+ readonly onDidReceiveKernelMessage = this.onDidReceiveKernelMessageEmitter.event;
120
122
 
121
123
  protected readonly renderers = new Map<CellKind, CellRenderer>();
122
124
  protected _model?: NotebookModel;
123
125
  protected _ready: Deferred<NotebookModel> = new Deferred();
126
+ protected scrollBarRef = React.createRef<{ updateScroll(): void }>();
124
127
 
125
128
  get notebookType(): string {
126
129
  return this.props.notebookType;
@@ -152,12 +155,25 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
152
155
  this.renderers.set(CellKind.Markup, this.markdownCellRenderer);
153
156
  this.renderers.set(CellKind.Code, this.codeCellRenderer);
154
157
  this._ready.resolve(this.waitForData());
158
+ this.ready.then(model => {
159
+ if (model.cells.length === 1 && model.cells[0].source === '') {
160
+ this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]);
161
+ model.setSelectedCell(model.cells[0]);
162
+ }
163
+ model.cells.forEach(cell => cell.onWillBlurCellEditor(() => this.node.focus()));
164
+ model.onDidAddOrRemoveCell(e => e.newCellIds?.forEach(cellId => model.cells.find(cell => cell.handle === cellId)?.onWillBlurCellEditor(() => this.node.focus())));
165
+ });
155
166
  }
156
167
 
157
168
  protected async waitForData(): Promise<NotebookModel> {
158
169
  this._model = await this.props.notebookData;
159
170
  this.saveable.delegate = this._model;
160
171
  this.toDispose.push(this._model);
172
+ this.toDispose.push(this._model.onDidChangeContent(() => {
173
+ // Update the scroll bar content after the content has changed
174
+ // Wait one frame to ensure that the content has been rendered
175
+ animationFrame().then(() => this.scrollBarRef.current?.updateScroll());
176
+ }));
161
177
  this.toDispose.push(this._model.onDidChangeReadOnly(readOnly => {
162
178
  if (readOnly) {
163
179
  lock(this.title);
@@ -204,6 +220,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
204
220
  {this.notebookMainToolbarRenderer.render(this._model, this.node)}
205
221
  <div className='theia-notebook-viewport' ref={(ref: HTMLDivElement) => this.viewportService.viewportElement = ref}>
206
222
  <PerfectScrollbar className='theia-notebook-scroll-container'
223
+ ref={this.scrollBarRef}
207
224
  onScrollY={(e: HTMLDivElement) => this.viewportService.onScroll(e)}>
208
225
  <NotebookCellListView renderers={this.renderers}
209
226
  notebookModel={this._model}
@@ -241,6 +258,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
241
258
  this.onDidReceiveKernelMessageEmitter.dispose();
242
259
  this.onPostRendererMessageEmitter.dispose();
243
260
  this.viewportService.dispose();
261
+ this._model?.dispose();
244
262
  super.dispose();
245
263
  }
246
264
 
@@ -16,7 +16,7 @@
16
16
  import '../../src/browser/style/index.css';
17
17
 
18
18
  import { ContainerModule } from '@theia/core/shared/inversify';
19
- import { KeybindingContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser';
19
+ import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, PreferenceContribution, WidgetFactory } from '@theia/core/lib/browser';
20
20
  import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
21
21
  import { NotebookOpenHandler } from './notebook-open-handler';
22
22
  import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core';
@@ -24,10 +24,9 @@ import { NotebookTypeRegistry } from './notebook-type-registry';
24
24
  import { NotebookRendererRegistry } from './notebook-renderer-registry';
25
25
  import { NotebookService } from './service/notebook-service';
26
26
  import { NotebookEditorWidgetFactory } from './notebook-editor-widget-factory';
27
- import { NotebookCellResourceResolver } from './notebook-cell-resource-resolver';
27
+ import { NotebookCellResourceResolver, NotebookOutputResourceResolver } from './notebook-cell-resource-resolver';
28
28
  import { NotebookModelResolverService } from './service/notebook-model-resolver-service';
29
29
  import { NotebookCellActionContribution } from './contributions/notebook-cell-actions-contribution';
30
- import { NotebookCellToolbarFactory } from './view/notebook-cell-toolbar-factory';
31
30
  import { createNotebookModelContainer, NotebookModel, NotebookModelFactory, NotebookModelProps } from './view-model/notebook-model';
32
31
  import { createNotebookCellModelContainer, NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from './view-model/notebook-cell-model';
33
32
  import { createNotebookEditorWidgetContainer, NotebookEditorWidgetContainerFactory, NotebookEditorProps, NotebookEditorWidget } from './notebook-editor-widget';
@@ -41,6 +40,11 @@ import { NotebookEditorWidgetService } from './service/notebook-editor-widget-se
41
40
  import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service';
42
41
  import { NotebookColorContribution } from './contributions/notebook-color-contribution';
43
42
  import { NotebookMonacoTextModelService } from './service/notebook-monaco-text-model-service';
43
+ import { NotebookOutlineContribution } from './contributions/notebook-outline-contribution';
44
+ import { NotebookLabelProviderContribution } from './contributions/notebook-label-provider-contribution';
45
+ import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution';
46
+ import { NotebookClipboardService } from './service/notebook-clipboard-service';
47
+ import { notebookPreferenceSchema } from './contributions/notebook-preferences';
44
48
 
45
49
  export default new ContainerModule((bind, unbind, isBound, rebind) => {
46
50
  bind(NotebookColorContribution).toSelf().inSingletonScope();
@@ -53,7 +57,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
53
57
  bind(NotebookRendererRegistry).toSelf().inSingletonScope();
54
58
 
55
59
  bind(WidgetFactory).to(NotebookEditorWidgetFactory).inSingletonScope();
56
- bind(NotebookCellToolbarFactory).toSelf().inSingletonScope();
57
60
 
58
61
  bind(NotebookService).toSelf().inSingletonScope();
59
62
  bind(NotebookEditorWidgetService).toSelf().inSingletonScope();
@@ -63,10 +66,13 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
63
66
  bind(NotebookRendererMessagingService).toSelf().inSingletonScope();
64
67
  bind(NotebookKernelHistoryService).toSelf().inSingletonScope();
65
68
  bind(NotebookKernelQuickPickService).toSelf().inSingletonScope();
69
+ bind(NotebookClipboardService).toSelf().inSingletonScope();
66
70
 
67
71
  bind(NotebookCellResourceResolver).toSelf().inSingletonScope();
68
72
  bind(ResourceResolver).toService(NotebookCellResourceResolver);
69
73
  bind(NotebookModelResolverService).toSelf().inSingletonScope();
74
+ bind(NotebookOutputResourceResolver).toSelf().inSingletonScope();
75
+ bind(ResourceResolver).toService(NotebookOutputResourceResolver);
70
76
 
71
77
  bind(NotebookCellActionContribution).toSelf().inSingletonScope();
72
78
  bind(MenuContribution).toService(NotebookCellActionContribution);
@@ -78,6 +84,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
78
84
  bind(MenuContribution).toService(NotebookActionsContribution);
79
85
  bind(KeybindingContribution).toService(NotebookActionsContribution);
80
86
 
87
+ bind(NotebookOutputActionContribution).toSelf().inSingletonScope();
88
+ bind(CommandContribution).toService(NotebookOutputActionContribution);
89
+
81
90
  bind(NotebookEditorWidgetContainerFactory).toFactory(ctx => (props: NotebookEditorProps) =>
82
91
  createNotebookEditorWidgetContainer(ctx.container, props).get(NotebookEditorWidget)
83
92
  );
@@ -89,4 +98,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
89
98
  );
90
99
 
91
100
  bind(NotebookMonacoTextModelService).toSelf().inSingletonScope();
101
+
102
+ bind(NotebookOutlineContribution).toSelf().inSingletonScope();
103
+ bind(FrontendApplicationContribution).toService(NotebookOutlineContribution);
104
+ bind(NotebookLabelProviderContribution).toSelf().inSingletonScope();
105
+ bind(LabelProviderContribution).toService(NotebookLabelProviderContribution);
106
+
107
+ bind(PreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema });
92
108
  });
@@ -14,32 +14,56 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { URI, MaybePromise } from '@theia/core';
17
+ import { URI, MaybePromise, Disposable } from '@theia/core';
18
18
  import { NavigatableWidgetOpenHandler, WidgetOpenerOptions } from '@theia/core/lib/browser';
19
- import { inject, injectable } from '@theia/core/shared/inversify';
19
+ import { injectable } from '@theia/core/shared/inversify';
20
20
  import { NotebookFileSelector, NotebookTypeDescriptor } from '../common/notebook-protocol';
21
- import { NotebookTypeRegistry } from './notebook-type-registry';
22
21
  import { NotebookEditorWidget } from './notebook-editor-widget';
23
22
  import { match } from '@theia/core/lib/common/glob';
24
23
  import { NotebookEditorWidgetOptions } from './notebook-editor-widget-factory';
25
24
 
25
+ export interface NotebookWidgetOpenerOptions extends WidgetOpenerOptions {
26
+ notebookType?: string;
27
+ }
28
+
26
29
  @injectable()
27
30
  export class NotebookOpenHandler extends NavigatableWidgetOpenHandler<NotebookEditorWidget> {
28
31
 
29
- id: string = 'notebook';
32
+ readonly id = NotebookEditorWidget.ID;
33
+
34
+ protected notebookTypes: NotebookTypeDescriptor[] = [];
30
35
 
31
- @inject(NotebookTypeRegistry)
32
- protected notebookTypeRegistry: NotebookTypeRegistry;
36
+ registerNotebookType(notebookType: NotebookTypeDescriptor): Disposable {
37
+ this.notebookTypes.push(notebookType);
38
+ return Disposable.create(() => {
39
+ this.notebookTypes.splice(this.notebookTypes.indexOf(notebookType), 1);
40
+ });
41
+ }
42
+
43
+ canHandle(uri: URI, options?: NotebookWidgetOpenerOptions): MaybePromise<number> {
44
+ if (options?.notebookType) {
45
+ return this.canHandleType(uri, this.notebookTypes.find(type => type.type === options.notebookType));
46
+ }
47
+ return Math.max(...this.notebookTypes.map(type => this.canHandleType(uri, type)));
48
+ }
49
+
50
+ canHandleType(uri: URI, notebookType?: NotebookTypeDescriptor): number {
51
+ if (notebookType?.selector && this.matches(notebookType.selector, uri)) {
52
+ return this.calculatePriority(notebookType);
53
+ } else {
54
+ return 0;
55
+ }
56
+ }
33
57
 
34
- canHandle(uri: URI, options?: WidgetOpenerOptions | undefined): MaybePromise<number> {
35
- const priorities = this.notebookTypeRegistry.notebookTypes
36
- .filter(notebook => notebook.selector && this.matches(notebook.selector, uri))
37
- .map(notebook => this.calculatePriority(notebook));
38
- return Math.max(...priorities);
58
+ protected calculatePriority(notebookType: NotebookTypeDescriptor | undefined): number {
59
+ if (!notebookType) {
60
+ return 0;
61
+ }
62
+ return notebookType.priority === 'option' ? 100 : 200;
39
63
  }
40
64
 
41
65
  protected findHighestPriorityType(uri: URI): NotebookTypeDescriptor | undefined {
42
- const matchingTypes = this.notebookTypeRegistry.notebookTypes
66
+ const matchingTypes = this.notebookTypes
43
67
  .filter(notebookType => notebookType.selector && this.matches(notebookType.selector, uri))
44
68
  .map(notebookType => ({ descriptor: notebookType, priority: this.calculatePriority(notebookType) }));
45
69
 
@@ -56,15 +80,19 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler<NotebookEd
56
80
  return type.descriptor;
57
81
  }
58
82
 
59
- protected calculatePriority(notebookType: NotebookTypeDescriptor | undefined): number {
60
- if (!notebookType) {
61
- return 0;
62
- }
63
- return notebookType.priority === 'option' ? 100 : 200;
83
+ // Override for better options typing
84
+ override open(uri: URI, options?: NotebookWidgetOpenerOptions): Promise<NotebookEditorWidget> {
85
+ return super.open(uri, options);
64
86
  }
65
87
 
66
- protected override createWidgetOptions(uri: URI, options?: WidgetOpenerOptions | undefined): NotebookEditorWidgetOptions {
88
+ protected override createWidgetOptions(uri: URI, options?: NotebookWidgetOpenerOptions): NotebookEditorWidgetOptions {
67
89
  const widgetOptions = super.createWidgetOptions(uri, options);
90
+ if (options?.notebookType) {
91
+ return {
92
+ notebookType: options.notebookType,
93
+ ...widgetOptions
94
+ };
95
+ }
68
96
  const notebookType = this.findHighestPriorityType(uri);
69
97
  if (!notebookType) {
70
98
  throw new Error('No notebook types registered for uri: ' + uri.toString());
@@ -75,11 +103,11 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler<NotebookEd
75
103
  };
76
104
  }
77
105
 
78
- matches(selectors: readonly NotebookFileSelector[], resource: URI): boolean {
106
+ protected matches(selectors: readonly NotebookFileSelector[], resource: URI): boolean {
79
107
  return selectors.some(selector => this.selectorMatches(selector, resource));
80
108
  }
81
109
 
82
- selectorMatches(selector: NotebookFileSelector, resource: URI): boolean {
110
+ protected selectorMatches(selector: NotebookFileSelector, resource: URI): boolean {
83
111
  return !!selector.filenamePattern
84
112
  && match(selector.filenamePattern, resource.path.name + resource.path.ext);
85
113
  }