@theia/notebook 1.53.0-next.6 → 1.53.0-next.64

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 (63) hide show
  1. package/lib/browser/contributions/notebook-actions-contribution.d.ts +1 -0
  2. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  3. package/lib/browser/contributions/notebook-actions-contribution.js +26 -2
  4. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  5. package/lib/browser/contributions/notebook-cell-actions-contribution.js +5 -5
  6. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  7. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts +14 -0
  8. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts.map +1 -0
  9. package/lib/browser/contributions/notebook-status-bar-contribution.js +75 -0
  10. package/lib/browser/contributions/notebook-status-bar-contribution.js.map +1 -0
  11. package/lib/browser/index.d.ts +1 -0
  12. package/lib/browser/index.d.ts.map +1 -1
  13. package/lib/browser/index.js +1 -0
  14. package/lib/browser/index.js.map +1 -1
  15. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  16. package/lib/browser/notebook-editor-widget.js +3 -2
  17. package/lib/browser/notebook-editor-widget.js.map +1 -1
  18. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  19. package/lib/browser/notebook-frontend-module.js +5 -0
  20. package/lib/browser/notebook-frontend-module.js.map +1 -1
  21. package/lib/browser/notebook-open-handler.d.ts +3 -2
  22. package/lib/browser/notebook-open-handler.d.ts.map +1 -1
  23. package/lib/browser/notebook-open-handler.js +12 -5
  24. package/lib/browser/notebook-open-handler.js.map +1 -1
  25. package/lib/browser/service/notebook-cell-editor-service.d.ts +16 -0
  26. package/lib/browser/service/notebook-cell-editor-service.d.ts.map +1 -0
  27. package/lib/browser/service/notebook-cell-editor-service.js +53 -0
  28. package/lib/browser/service/notebook-cell-editor-service.js.map +1 -0
  29. package/lib/browser/view/notebook-cell-editor.d.ts +3 -0
  30. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  31. package/lib/browser/view/notebook-cell-editor.js +32 -17
  32. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  33. package/lib/browser/view/notebook-cell-list-view.d.ts +6 -4
  34. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  35. package/lib/browser/view/notebook-cell-list-view.js +12 -12
  36. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  37. package/lib/browser/view/notebook-code-cell-view.d.ts +2 -0
  38. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  39. package/lib/browser/view/notebook-code-cell-view.js +6 -1
  40. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  41. package/lib/browser/view/notebook-markdown-cell-view.d.ts +2 -0
  42. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  43. package/lib/browser/view/notebook-markdown-cell-view.js +8 -3
  44. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  45. package/lib/browser/view-model/notebook-cell-model.d.ts +3 -0
  46. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  47. package/lib/browser/view-model/notebook-cell-model.js +5 -0
  48. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  49. package/package.json +8 -8
  50. package/src/browser/contributions/notebook-actions-contribution.ts +28 -1
  51. package/src/browser/contributions/notebook-cell-actions-contribution.ts +6 -6
  52. package/src/browser/contributions/notebook-status-bar-contribution.ts +77 -0
  53. package/src/browser/index.ts +1 -0
  54. package/src/browser/notebook-editor-widget.tsx +4 -2
  55. package/src/browser/notebook-frontend-module.ts +6 -0
  56. package/src/browser/notebook-open-handler.ts +13 -7
  57. package/src/browser/service/notebook-cell-editor-service.ts +56 -0
  58. package/src/browser/style/index.css +17 -4
  59. package/src/browser/view/notebook-cell-editor.tsx +38 -19
  60. package/src/browser/view/notebook-cell-list-view.tsx +29 -19
  61. package/src/browser/view/notebook-code-cell-view.tsx +5 -0
  62. package/src/browser/view/notebook-markdown-cell-view.tsx +9 -2
  63. package/src/browser/view-model/notebook-cell-model.ts +7 -0
@@ -114,7 +114,8 @@
114
114
  margin: 0px 16px 0px 10px;
115
115
  }
116
116
 
117
- .theia-notebook-cell.focused .theia-notebook-cell-editor-container {
117
+ /* Only mark an editor cell focused if the editor has focus */
118
+ .theia-notebook-cell-editor-container:has(.monaco-editor.focused) {
118
119
  outline-color: var(--theia-notebook-focusedEditorBorder);
119
120
  }
120
121
 
@@ -209,6 +210,10 @@
209
210
  overflow: hidden;
210
211
  }
211
212
 
213
+ .theia-notebook-main-container:focus {
214
+ outline: none;
215
+ }
216
+
212
217
  .theia-notebook-main-container .theia-notebook-main-loading-indicator {
213
218
  /* `progress-animation` is defined in `packages/core/src/browser/style/progress-bar.css` */
214
219
  animation: progress-animation 1.8s 0s infinite cubic-bezier(0.645, 0.045, 0.355, 1);
@@ -272,10 +277,10 @@
272
277
  border: 1px solid var(--theia-notebook-cellToolbarSeparator);
273
278
  background-color: var(--theia-editor-background);
274
279
  color: var(--theia-foreground);
275
- vertical-align: middle;
276
- text-align: center;
280
+ display: flex;
277
281
  height: 24px;
278
282
  margin: 0 8px;
283
+ padding: 2px 4px;
279
284
  }
280
285
 
281
286
  .theia-notebook-add-cell-button:hover {
@@ -286,10 +291,18 @@
286
291
  background-color: var(--theia-toolbar-active);
287
292
  }
288
293
 
289
- .theia-notebook-add-cell-button-icon {
294
+ .theia-notebook-add-cell-button>* {
290
295
  vertical-align: middle;
291
296
  }
292
297
 
298
+ .theia-notebook-add-cell-button-icon::before {
299
+ font: normal normal normal 14px/1 codicon;
300
+ }
301
+
302
+ .theia-notebook-add-cell-button-text {
303
+ margin: 1px 0 0 4px;
304
+ }
305
+
293
306
  .theia-notebook-cell-output-webview {
294
307
  padding: 5px 0px;
295
308
  margin: 0px 15px 0px 9px;
@@ -30,13 +30,15 @@ import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/edito
30
30
  import { ModelDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel';
31
31
  import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '@theia/monaco-editor-core/esm/vs/editor/common/model';
32
32
  import { animationFrame } from '@theia/core/lib/browser';
33
+ import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
33
34
 
34
35
  interface CellEditorProps {
35
- notebookModel: NotebookModel,
36
- cell: NotebookCellModel,
37
- monacoServices: MonacoEditorServices,
36
+ notebookModel: NotebookModel;
37
+ cell: NotebookCellModel;
38
+ monacoServices: MonacoEditorServices;
38
39
  notebookContextManager: NotebookContextManager;
39
- notebookViewportService?: NotebookViewportService,
40
+ notebookCellEditorService: NotebookCellEditorService;
41
+ notebookViewportService?: NotebookViewportService;
40
42
  fontInfo?: BareFontInfo;
41
43
  }
42
44
 
@@ -124,20 +126,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
124
126
  animationFrame().then(() => this.setMatches());
125
127
  }));
126
128
 
127
- this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => {
128
- const editorDomNode = this.editor?.getControl().getDomNode();
129
- if (editorDomNode) {
130
- editorDomNode.scrollIntoView({
131
- behavior: 'instant',
132
- block: 'center'
133
- });
134
- } else {
135
- this.container?.scrollIntoView({
136
- behavior: 'instant',
137
- block: 'center'
138
- });
139
- }
140
- }));
129
+ this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => this.centerEditorInView()));
141
130
 
142
131
  this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
143
132
  if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
@@ -155,6 +144,10 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
155
144
  });
156
145
  this.toDispose.push(disposable);
157
146
  }
147
+
148
+ this.toDispose.push(this.props.cell.onDidRequestCenterEditor(() => {
149
+ this.centerEditorInView();
150
+ }));
158
151
  }
159
152
 
160
153
  override componentWillUnmount(): void {
@@ -162,10 +155,28 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
162
155
  }
163
156
 
164
157
  protected disposeEditor(): void {
158
+ if (this.editor) {
159
+ this.props.notebookCellEditorService.editorDisposed(this.editor.uri);
160
+ }
165
161
  this.toDispose.dispose();
166
162
  this.toDispose = new DisposableCollection();
167
163
  }
168
164
 
165
+ protected centerEditorInView(): void {
166
+ const editorDomNode = this.editor?.getControl().getDomNode();
167
+ if (editorDomNode) {
168
+ editorDomNode.scrollIntoView({
169
+ behavior: 'instant',
170
+ block: 'center'
171
+ });
172
+ } else {
173
+ this.container?.scrollIntoView({
174
+ behavior: 'instant',
175
+ block: 'center'
176
+ });
177
+ }
178
+ }
179
+
169
180
  protected async initEditor(): Promise<void> {
170
181
  const { cell, notebookModel, monacoServices } = this.props;
171
182
  if (this.container) {
@@ -191,7 +202,14 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
191
202
  }));
192
203
  this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => {
193
204
  this.props.notebookModel.setSelectedCell(cell, false);
205
+ this.props.notebookCellEditorService.editorFocusChanged(this.editor);
206
+ }));
207
+ this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => {
208
+ if (this.props.notebookCellEditorService.getActiveCell()?.uri.toString() === this.props.cell.uri.toString()) {
209
+ this.props.notebookCellEditorService.editorFocusChanged(undefined);
210
+ }
194
211
  }));
212
+
195
213
  this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => {
196
214
  const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection);
197
215
  this.props.notebookModel.selectedText = selectedText;
@@ -206,10 +224,11 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
206
224
  this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, false);
207
225
  }
208
226
  }));
227
+ this.props.notebookCellEditorService.editorCreated(uri, this.editor);
228
+ this.setMatches();
209
229
  if (cell.editing && notebookModel.selectedCell === cell) {
210
230
  this.editor.getControl().focus();
211
231
  }
212
- this.setMatches();
213
232
  }
214
233
  }
215
234
 
@@ -18,9 +18,9 @@ import { CellEditType, CellKind, NotebookCellsChangeType } from '../../common';
18
18
  import { NotebookCellModel } from '../view-model/notebook-cell-model';
19
19
  import { NotebookModel } from '../view-model/notebook-model';
20
20
  import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory';
21
- import { animationFrame, codicon, onDomEvent } from '@theia/core/lib/browser';
22
- import { CommandRegistry, DisposableCollection, nls } from '@theia/core';
23
- import { NotebookCommands } from '../contributions/notebook-actions-contribution';
21
+ import { animationFrame, onDomEvent } from '@theia/core/lib/browser';
22
+ import { CommandRegistry, DisposableCollection, MenuModelRegistry, MenuNode, nls } from '@theia/core';
23
+ import { NotebookCommands, NotebookMenus } from '../contributions/notebook-actions-contribution';
24
24
  import { NotebookCellActionContribution } from '../contributions/notebook-cell-actions-contribution';
25
25
  import { NotebookContextManager } from '../service/notebook-context-manager';
26
26
 
@@ -34,7 +34,8 @@ interface CellListProps {
34
34
  notebookModel: NotebookModel;
35
35
  notebookContext: NotebookContextManager;
36
36
  toolbarRenderer: NotebookCellToolbarFactory;
37
- commandRegistry: CommandRegistry
37
+ commandRegistry: CommandRegistry;
38
+ menuRegistry: MenuModelRegistry;
38
39
  }
39
40
 
40
41
  interface NotebookCellListState {
@@ -113,8 +114,9 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
113
114
  .map((cell, index) =>
114
115
  <React.Fragment key={'cell-' + cell.handle}>
115
116
  <NotebookCellDivider
117
+ menuRegistry={this.props.menuRegistry}
116
118
  isVisible={() => this.isEnabled()}
117
- onAddNewCell={(kind: CellKind) => this.onAddNewCell(kind, index)}
119
+ onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, index)}
118
120
  onDrop={e => this.onDrop(e, index)}
119
121
  onDragOver={e => this.onDragOver(e, cell, 'top')} />
120
122
  {this.shouldRenderDragOverIndicator(cell, 'top') && <CellDropIndicator />}
@@ -155,8 +157,9 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
155
157
  )
156
158
  }
157
159
  <NotebookCellDivider
160
+ menuRegistry={this.props.menuRegistry}
158
161
  isVisible={() => this.isEnabled()}
159
- onAddNewCell={(kind: CellKind) => this.onAddNewCell(kind, this.props.notebookModel.cells.length)}
162
+ onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, this.props.notebookModel.cells.length)}
160
163
  onDrop={e => this.onDrop(e, this.props.notebookModel.cells.length - 1)}
161
164
  onDragOver={e => this.onDragOver(e, this.props.notebookModel.cells[this.props.notebookModel.cells.length - 1], 'bottom')} />
162
165
  </ul>;
@@ -221,11 +224,11 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
221
224
  this.setState({ ...this.state, dragOverIndicator: undefined });
222
225
  }
223
226
 
224
- protected onAddNewCell(kind: CellKind, index: number): void {
227
+ protected onAddNewCell(commandId: string, index: number): void {
225
228
  if (this.isEnabled()) {
226
- this.props.commandRegistry.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id,
229
+ this.props.commandRegistry.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, index - 1);
230
+ this.props.commandRegistry.executeCommand(commandId,
227
231
  this.props.notebookModel,
228
- kind,
229
232
  index
230
233
  );
231
234
  }
@@ -242,24 +245,31 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
242
245
 
243
246
  export interface NotebookCellDividerProps {
244
247
  isVisible: () => boolean;
245
- onAddNewCell: (type: CellKind) => void;
248
+ onAddNewCell: (commandId: string) => void;
246
249
  onDrop: (event: React.DragEvent<HTMLLIElement>) => void;
247
250
  onDragOver: (event: React.DragEvent<HTMLLIElement>) => void;
251
+ menuRegistry: MenuModelRegistry;
248
252
  }
249
253
 
250
- export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver }: NotebookCellDividerProps): React.JSX.Element {
254
+ export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver, menuRegistry }: NotebookCellDividerProps): React.JSX.Element {
251
255
  const [hover, setHover] = React.useState(false);
252
256
 
257
+ const menuPath = NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP;
258
+ const menuItems = menuRegistry.getMenuNode(menuPath).children;
259
+
260
+ const renderItem = (item: MenuNode): React.ReactNode => <button
261
+ key={item.id}
262
+ className='theia-notebook-add-cell-button'
263
+ onClick={() => onAddNewCell(item.command || '')}
264
+ title={nls.localizeByDefault(`Add ${item.label} Cell`)}
265
+ >
266
+ <div className={item.icon + ' theia-notebook-add-cell-button-icon'} />
267
+ <div className='theia-notebook-add-cell-button-text'>{item.label}</div>
268
+ </button>;
269
+
253
270
  return <li className='theia-notebook-cell-divider' onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} onDrop={onDrop} onDragOver={onDragOver}>
254
271
  {hover && isVisible() && <div className='theia-notebook-add-cell-buttons'>
255
- <button className='theia-notebook-add-cell-button' onClick={() => onAddNewCell(CellKind.Code)} title={nls.localizeByDefault('Add Code Cell')}>
256
- <div className={codicon('add') + ' theia-notebook-add-cell-button-icon'} />
257
- {nls.localizeByDefault('Code')}
258
- </button>
259
- <button className='theia-notebook-add-cell-button' onClick={() => onAddNewCell(CellKind.Markup)} title={nls.localizeByDefault('Add Markdown Cell')}>
260
- <div className={codicon('add') + ' theia-notebook-add-cell-button-icon'} />
261
- {nls.localizeByDefault('Markdown')}
262
- </button>
272
+ {menuItems.map((item: MenuNode) => renderItem(item))}
263
273
  </div>}
264
274
  </li>;
265
275
  }
@@ -35,6 +35,7 @@ import { EditorPreferences } from '@theia/editor/lib/browser';
35
35
  import { NotebookOptionsService } from '../service/notebook-options';
36
36
  import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
37
37
  import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent';
38
+ import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
38
39
 
39
40
  @injectable()
40
41
  export class NotebookCodeCellRenderer implements CellRenderer {
@@ -62,6 +63,9 @@ export class NotebookCodeCellRenderer implements CellRenderer {
62
63
  @inject(EditorPreferences)
63
64
  protected readonly editorPreferences: EditorPreferences;
64
65
 
66
+ @inject(NotebookCellEditorService)
67
+ protected readonly notebookCellEditorService: NotebookCellEditorService;
68
+
65
69
  @inject(CommandRegistry)
66
70
  protected readonly commandRegistry: CommandRegistry;
67
71
 
@@ -86,6 +90,7 @@ export class NotebookCodeCellRenderer implements CellRenderer {
86
90
  monacoServices={this.monacoServices}
87
91
  notebookContextManager={this.notebookContextManager}
88
92
  notebookViewportService={this.notebookViewportService}
93
+ notebookCellEditorService={this.notebookCellEditorService}
89
94
  fontInfo={this.notebookOptionsService.editorFontInfo} />
90
95
  <NotebookCodeCellStatus cell={cell} notebook={notebookModel}
91
96
  commandRegistry={this.commandRegistry}
@@ -29,6 +29,7 @@ import { NotebookOptionsService } from '../service/notebook-options';
29
29
  import { NotebookCodeCellStatus } from './notebook-code-cell-view';
30
30
  import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './notebook-find-widget';
31
31
  import * as mark from 'advanced-mark.js';
32
+ import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
32
33
 
33
34
  @injectable()
34
35
  export class NotebookMarkdownCellRenderer implements CellRenderer {
@@ -47,6 +48,9 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
47
48
  @inject(NotebookOptionsService)
48
49
  protected readonly notebookOptionsService: NotebookOptionsService;
49
50
 
51
+ @inject(NotebookCellEditorService)
52
+ protected readonly notebookCellEditorService: NotebookCellEditorService;
53
+
50
54
  render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode {
51
55
  return <MarkdownCell
52
56
  markdownRenderer={this.markdownRenderer}
@@ -55,7 +59,8 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
55
59
  notebookOptionsService={this.notebookOptionsService}
56
60
  cell={cell}
57
61
  notebookModel={notebookModel}
58
- notebookContextManager={this.notebookContextManager} />;
62
+ notebookContextManager={this.notebookContextManager}
63
+ notebookCellEditorService={this.notebookCellEditorService} />;
59
64
  }
60
65
 
61
66
  renderDragImage(cell: NotebookCellModel): HTMLElement {
@@ -77,10 +82,11 @@ interface MarkdownCellProps {
77
82
  notebookModel: NotebookModel;
78
83
  notebookContextManager: NotebookContextManager;
79
84
  notebookOptionsService: NotebookOptionsService;
85
+ notebookCellEditorService: NotebookCellEditorService
80
86
  }
81
87
 
82
88
  function MarkdownCell({
83
- markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry
89
+ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry, notebookCellEditorService
84
90
  }: MarkdownCellProps): React.JSX.Element {
85
91
  const [editMode, setEditMode] = React.useState(cell.editing);
86
92
  let empty = false;
@@ -133,6 +139,7 @@ function MarkdownCell({
133
139
  <CellEditor notebookModel={notebookModel} cell={cell}
134
140
  monacoServices={monacoServices}
135
141
  notebookContextManager={notebookContextManager}
142
+ notebookCellEditorService={notebookCellEditorService}
136
143
  fontInfo={notebookOptionsService.editorFontInfo} />
137
144
  <NotebookCodeCellStatus cell={cell} notebook={notebookModel}
138
145
  commandRegistry={commandRegistry}
@@ -126,6 +126,9 @@ export class NotebookCellModel implements NotebookCell, Disposable {
126
126
  protected readonly onDidSelectFindMatchEmitter = new Emitter<NotebookCodeEditorFindMatch>();
127
127
  readonly onDidSelectFindMatch: Event<NotebookCodeEditorFindMatch> = this.onDidSelectFindMatchEmitter.event;
128
128
 
129
+ protected onDidRequestCenterEditorEmitter = new Emitter<void>();
130
+ readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event;
131
+
129
132
  @inject(NotebookCellModelProps)
130
133
  protected readonly props: NotebookCellModelProps;
131
134
 
@@ -299,6 +302,10 @@ export class NotebookCellModel implements NotebookCell, Disposable {
299
302
  this.onWillBlurCellEditorEmitter.fire();
300
303
  }
301
304
 
305
+ requestCenterEditor(): void {
306
+ this.onDidRequestCenterEditorEmitter.fire();
307
+ }
308
+
302
309
  spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
303
310
  if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
304
311
  const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);