This page covers the VS Code notebook editor system, including the canonical data model, view model, central UI widget, virtualized cell list, webview output layer, cell renderers, and the extension API for serializers and renderers. For the Monaco text editor embedded inside code cells, see Core Editor (Monaco). For the extension system that registers serializers and renderers, see Extension System.
The notebook editor is organized into four distinct layers:
| Layer | Key Classes | Location |
|---|---|---|
| Pane | NotebookEditor | browser/notebookEditor.ts |
| Widget | NotebookEditorWidget, NotebookCellList, BackLayerWebView | browser/notebookEditorWidget.ts, browser/view/ |
| ViewModel | NotebookViewModel, CodeCellViewModel, MarkupCellViewModel | browser/viewModel/ |
| Data | NotebookTextModel, NotebookCellTextModel | common/model/ |
Notebook Editor Layer Architecture
Sources: src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts142-476 src/vs/workbench/contrib/notebook/browser/notebookEditor.ts55-107
NotebookTextModel (in src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts) is the canonical, authoritative document store for a notebook file. It:
NotebookCellTextModel cells accessible via INotebookTextModel.cellsonDidChangeContent: Event<NotebookTextModelChangedEvent> for all structural changesapplyEdits(rawEdits, synchronous, beginSelectionState, endSelectionsComputer, undoRedoGroup) as the single mutation pointIUndoRedoService via StackOperation, grouping multiple edits into a single undoable unitAll mutations flow through applyEdits. The operation types are defined by CellEditType in notebookCommon.ts:
CellEditType | Description |
|---|---|
Replace | Insert, remove, or replace cells at a range |
Output | Set or append cell outputs |
OutputItems | Append data items to an existing output |
Metadata | Set full cell metadata |
PartialMetadata | Patch specific metadata keys (nulls allowed) |
PartialInternalMetadata | Patch execution metadata (start time, duration, etc.) |
CellLanguage | Change the language identifier of a cell |
DocumentMetadata | Change notebook-level metadata |
Move | Move a contiguous range of cells to a new index |
NotebookCellTextModel in src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts implements ICell:
| Property | Type | Purpose |
|---|---|---|
handle | number | Stable numeric identifier used throughout the system |
cellKind | CellKind | Markup = 1 or Code = 2 |
language | string | Programming language identifier |
outputs | ICellOutput[] | List of output objects |
metadata | NotebookCellMetadata | Arbitrary user-visible metadata |
internalMetadata | NotebookCellInternalMetadata | Execution metadata (order, start/end time, success) |
textModel | ITextModel | undefined | Lazily resolved Monaco text model |
Each ICellOutput holds outputs: IOutputItemDto[], where each item has a mime: string and data: VSBuffer. The default MIME priority order is NOTEBOOK_DISPLAY_ORDER and is managed by MimeTypeDisplayOrder, which supports user-configurable overrides via prioritize().
Sources: src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts1-100 src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts26-80 src/vs/workbench/contrib/notebook/common/notebookCommon.ts49-245
NotebookViewModel (in src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts) is the view-level projection of NotebookTextModel. It:
viewCells: ICellViewModel[] — the ordered list of visible cells, excluding any cells inside collapsed folding regions_hiddenRangesgetSelections(): ICellRange[]) and focus (getFocus(): ICellRange)NotebookTextModel.onDidChangeContent to cell view modelsonDidChangeViewCells: Event<INotebookViewCellsUpdateEvent> for splice-style changesThe NotebookViewModel is constructed with a ViewContext that bundles shared state:
NotebookOptions — layout and display configurationNotebookEventDispatcher — internal event bus for cell state changes and layout changes(language: string) => IBaseCellEditorOptions for per-language editor option mergingAll cell view models extend BaseCellViewModel:
Cell ViewModel Class Hierarchy
CodeCellViewModel uses a PrefixSumComputer to track cumulative output heights so that each output's vertical offset can be computed efficiently. MarkupCellViewModel caches the last rendered HTML string in renderedHtml; when this changes, BackLayerWebView is asked to re-render the preview.
Sources: src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts30-100 src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts30-120 src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts22-100
NotebookEditorWidget in src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts is the central UI component, implementing both INotebookEditorDelegate (used by cell renderers and BackLayerWebView) and INotebookEditor (the public contract).
| Field | Type | Purpose |
|---|---|---|
_list | INotebookCellList | The virtualized cell list |
_webview | BackLayerWebView | Webview for outputs and markup previews |
_notebookViewModel | NotebookViewModel | Current view model (undefined when no document is loaded) |
_contributions | Map<string, INotebookEditorContribution> | Active editor contributions |
_renderedEditors | Map<ICellViewModel, ICodeEditor> | Maps focused cells to their Monaco editor instances |
_notebookOptions | NotebookOptions | Layout configuration (margins, fonts, scroll behavior) |
scopedContextKeyService | IContextKeyService | Per-editor context key scope |
_overlayContainer | HTMLElement | Root DOM node, overlaid on the workbench |
The INotebookEditor interface in notebookBrowser.ts defines the complete public contract:
onDidChangeModel, onDidChangeViewCells, onDidChangeActiveCell, onDidScroll, onMouseUp, onMouseDown, onDidChangeFocus, onDidChangeSelectiongetSelections(), setSelections(), getFocus(), setFocus()getActiveCell(), focusNotebookCell(), executeNotebookCells(), cancelNotebookCells()revealCellRangeInView(), revealInView(), revealLineInViewAsync()changeModelDecorations(), deltaCellDecorations()changeViewZones(INotebookViewZoneChangeAccessor) for injecting height between cellsNotebookEditorWidget instances are expensive to create. The INotebookEditorService (implemented by NotebookEditorWidgetService) maintains a pool of instances keyed by editor group and view type. NotebookEditor.setInput() calls INotebookEditorService.retrieveWidget() to borrow an existing widget; when the pane is hidden or disposed, the widget is returned to the pool. This avoids tearing down and recreating the cell list and webview on every tab switch.
On construction, NotebookEditorWidget instantiates all registered INotebookEditorContribution implementations from NotebookEditorExtensionsRegistry. Each contribution can:
saveViewState(): unknown — persist per-editor staterestoreViewState(state: unknown) — restore state on model attachdispose() — clean up resourcesSources: src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts142-480 src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts514-700
NotebookCellList in src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts extends WorkbenchList<CellViewModel> and provides the virtualized rendering container.
Key responsibilities:
visibleRanges: ICellRange[] — cells currently intersecting the viewporthiddenRangesPrefixSum: PrefixSumComputer to compute positions when cells are folded awayonDidRemoveOutputs, onDidHideOutputs, and onDidRemoveCellsFromView to trigger webview cleanupNotebookViewZones for between-cell content injection (e.g., AI chat zones, inline diff)NotebookCellOverlays for per-cell overlay DOM nodesThe list uses a custom view class NotebookCellListView and a constant NOTEBOOK_WEBVIEW_BOUNDARY = 5000 — the number of pixels the webview extends above the visible top to accommodate outputs from off-screen cells.
NotebookCellListDelegate in cellRenderer.ts implements IListVirtualDelegate<CellViewModel>:
| Method | Returns |
|---|---|
getHeight(cell) | cell.getHeight(lineHeight) from layout info |
getDynamicHeight(cell) | cell.getDynamicHeight() for cells needing measurement |
getTemplateId(cell) | 'markdown_cell' or 'code_cell' based on cell.cellKind |
Sources: src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts80-200 src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts57-85
Cell Rendering Pipeline
MarkupCellRenderer (template ID 'markdown_cell') in cellRenderer.ts creates DOM structure for markup cells:
CellFocusIndicator)CellTitleToolbarPart with title/delete actions).cell.markdown) — the preview renders into the webview at this positionrenderElement() instantiates MarkupCell, which coordinates with BackLayerWebView to render the markdown preview.
CodeCellRenderer (template ID 'code_cell') creates DOM for code cells:
RunToolbar)CodeEditorWidget from NotebookCellEditorPoolexecution-count-label)BackLayerWebView insetsBoth renderers compose behaviors using CellPartsCollection. Each CellPart handles a specific concern:
CellPart | Responsibility |
|---|---|
CellFocusIndicator | Border-style focus ring |
CellTitleToolbarPart | Per-cell action toolbar |
CellExecutionPart | Execution spinner and state |
CellProgressBar | Loading progress indicator |
CellEditorStatusBar | Language, line count status bar |
CellDecorations | Custom CSS class decorations |
CellComments | Comment thread display |
CellContextKeyPart | Context key bindings |
CollapsedCellInput | Collapsed input placeholder |
CollapsedCellOutput | Collapsed output placeholder |
CellDragAndDropPart | Drag handle behavior |
CellChatPart | Inline chat affordance |
Sources: src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts112-400
BackLayerWebView<T> in src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts provides a single, persistent IWebviewElement that sits behind the cell list DOM. It renders all cell outputs and markup previews.
The webview element is positioned absolutely with position: absolute; height: 1400px and grows as needed. The cell list renders on top of it. Output insets are absolutely positioned div elements inside the webview.
createWebview(targetWindow) generates the webview HTML via generateContent(baseUrl). This HTML page includes:
webviewPreloads.ts script (inlined by preloadsScriptStr())RendererMetadata[] — metadata for all registered extension renderersStaticPreloadMetadata[] — paths to kernel-contributed preload scriptsinsetMapping: Map<IDisplayOutputViewModel, ICachedInset<T>> tracks every rendered output inset. Each ICachedInset stores:
outputId — stable string IDcellInfo — the cell the output belongs tocachedCreation: ICreationRequestMessage — the message used to render it, for replay on reloadreversedInsetMapping: Map<string, IDisplayOutputViewModel> allows lookup by outputId for incoming webview messages.
All communication with the webview content uses typed messages defined in webviewMessages.ts.
Messages from the webview to the host (FromWebviewMessage)
| Message type | Purpose |
|---|---|
initialized | Webview script is ready |
dimension | Output or markup height changed |
mouseenter / mouseleave | Output hover state |
outputFocus / outputBlur | Output focus changed |
scroll-to-reveal | Output requests the host to scroll |
did-scroll-wheel | Mouse wheel forwarded from webview |
clicked-link | Link navigation request |
clicked-data-url | Data URL download request |
customRendererMessage | Bidirectional renderer messaging |
Messages from the host to the webview (ToWebviewMessage)
| Message type | Purpose |
|---|---|
createOutput | Create a new output inset |
showOutput / hideOutput | Toggle output visibility |
updateOutput | Replace output content |
deleteOutput | Remove an output inset |
initializeMarkup | Initialize markup cell previews |
notebookStyles | Push updated CSS theme variables |
notebookOptions | Push updated layout options |
tokenizedStylesChanged | Push updated syntax highlighting CSS |
BackLayerWebView Message Flow
getRendererData() collects all INotebookRendererInfo from INotebookService.getRenderers() and serializes them as RendererMetadata[]. The webview preload dynamically imports each renderer's entrypoint module. When an output is created, the preload selects the appropriate renderer by matching MIME type against mimeTypes, taking into account kernel capabilities and NotebookRendererMatch priority ordering.
Sources: src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts129-560 src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts1-150 src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts92-300
NotebookEditor in src/vs/workbench/contrib/notebook/browser/notebookEditor.ts extends EditorPane and integrates the notebook widget into the workbench tab system.
INotebookEditorPane and IEditorPaneWithScrollingNotebookEditorWidget in _widget: IBorrowValue<NotebookEditorWidget>INotebookEditorViewState via IEditorMemento<INotebookEditorViewState> keyed by NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEYWhen setInput(input, options, context, token) is called:
NotebookEditorWidget is borrowed from INotebookEditorServiceNotebookEditorInput is resolved to a NotebookTextModel via INotebookEditorModelResolverServicesetModel() creates a NotebookViewModel from the text modelINotebookEditorViewState is restored (cell states, scroll position, editor cursors)INotebookEditorOptionsINotebookEditorViewState includes: editingCells, collapsedInputCells, collapsedOutputCells, editorViewStates, hiddenFoldingRanges, scrollPosition, focus, and contributionsState.
Sources: src/vs/workbench/contrib/notebook/browser/notebookEditor.ts55-400
src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts wires up all workbench registrations:
NotebookEditor as the editor pane for NotebookEditorInputNotebookTextDiffEditor for NotebookDiffEditorInputNotebookEditorSerializer and NotebookDiffEditorSerializer as IEditorSerializers for session restoreINotebookService, INotebookKernelService, INotebookExecutionService, INotebookExecutionStateService, INotebookRendererMessagingService, and otherscoreActions, executeActions, etc.) and contribution modules (find, outline, clipboard, etc.)Sources: src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts140-295
Extensions register serializers via vscode.notebooks.registerNotebookSerializer(notebookType, serializer). The serializer object provides:
deserializeNotebook(data: Uint8Array, token): NotebookData — parse file bytes into cells + metadataserializeNotebook(data: NotebookData, token): Uint8Array — serialize back to bytesThe call flows through the extension host RPC layer:
Extension API Bridge
MainThreadNotebooks.$registerNotebookSerializer() registers an adapter with INotebookService that proxies dataToNotebook() and notebookToData() calls back to the extension host via ExtHostNotebookShape.$dataToNotebook() and $notebookToData().
Notebook renderers are contributed via the notebookRenderer contribution point in package.json:
The renderer module exports an activate(context: RendererContext): RendererApi function from the vscode-notebook-renderer types package. It registers renderOutputItem(output: OutputItem, element: HTMLElement) handlers per MIME type.
RendererMessagingSpec controls bidirectional messaging: Never, Always, or Optional. When messaging is enabled, INotebookRendererMessagingService routes messages between the webview renderer and the extension host via MainThreadNotebookRenderers.
ExtHostNotebookController in src/vs/workbench/api/common/extHostNotebook.ts maintains the extension-side state:
_documents: ResourceMap<ExtHostNotebookDocument> — open notebook documents by URI_editors: Map<string, ExtHostNotebookEditor> — open notebook editors by IDonDidOpenNotebookDocument, onDidCloseNotebookDocument, onDidChangeActiveNotebookEditor, onDidChangeVisibleNotebookEditorsMainThreadNotebooks in src/vs/workbench/api/browser/mainThreadNotebook.ts is annotated @extHostNamedCustomer(MainContext.MainThreadNotebook) and handles the main thread side of the MainThreadNotebookShape contract.
Sources: src/vs/workbench/api/common/extHostNotebook.ts43-200 src/vs/workbench/api/browser/mainThreadNotebook.ts30-120 src/vs/workbench/contrib/notebook/common/notebookService.ts24-80
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.