diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index df674353633..0450aa54f10 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -265,12 +265,20 @@ function scrollingEnabled(output: OutputItem, options: RenderOptions) { metadata.scrollable : options.outputScrolling; } +interface OutputWithAppend extends OutputItem { + appendedText?(): string | undefined; +} + // div.cell_container // div.output_container // div.output.output-stream <-- outputElement parameter // div.scrollable? tabindex="0" <-- contentParent // div output-item-id="{guid}" <-- content from outputItem parameter -function renderStream(outputInfo: OutputItem, outputElement: HTMLElement, error: boolean, ctx: IRichRenderContext): IDisposable { +function renderStream(outputInfo: OutputWithAppend, outputElement: HTMLElement, error: boolean, ctx: IRichRenderContext): IDisposable { + const appendedText = outputInfo.appendedText?.(); + if (appendedText) { + console.log(`appending output version ${appendedText}`); + } const disposableStore = createDisposableStore(); const outputScrolling = scrollingEnabled(outputInfo, ctx.settings); @@ -301,6 +309,9 @@ function renderStream(outputInfo: OutputItem, outputElement: HTMLElement, error: const existingContent = outputElement.querySelector(`[output-item-id="${outputInfo.id}"]`) as HTMLElement | null; let contentParent = existingContent?.parentElement; if (existingContent && contentParent) { + if (appendedText){ + existingContent + } existingContent.replaceWith(newContent); while (newContent.nextSibling) { // clear out any stale content if we had previously combined streaming outputs into this one diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 6f48147c9e4..e8970f70dee 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -132,7 +132,7 @@ export class ExtHostCell { const compressed = notebookCommon.compressOutputItemStreams(mimeOutputs.get(mime)!); output.items.push({ mime, - data: compressed.buffer + data: compressed.data.buffer }); }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 0fdc7c3c612..4e3b2573ec3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1506,6 +1506,8 @@ export class BackLayerWebView extends Themable { if (content.type === RenderOutputType.Extension) { const output = content.source.model; const firstBuffer = output.outputs.find(op => op.mime === content.mimeType)!; + const appenededData = output.appendedSinceVersion(outputCache.versionId, content.mimeType); + const appended = appenededData ? { valueBytes: appenededData.buffer, previousVersion: outputCache.versionId } : undefined; const valueBytes = copyBufferIfNeeded(firstBuffer.data.buffer, transfer); updatedContent = { @@ -1515,6 +1517,7 @@ export class BackLayerWebView extends Themable { output: { mime: content.mimeType, valueBytes, + appended: appended }, allOutputs: output.outputs.map(output => ({ mime: output.mime })) }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 15fc1f19790..1927f9497dc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -188,11 +188,18 @@ export interface IOutputRequestDto { export interface OutputItemEntry { readonly mime: string; readonly valueBytes: Uint8Array; + readonly appended?: { valueBytes: Uint8Array; previousVersion: number }; } export type ICreationContent = | { readonly type: RenderOutputType.Html; readonly htmlContent: string } - | { readonly type: RenderOutputType.Extension; readonly outputId: string; readonly metadata: unknown; readonly output: OutputItemEntry; readonly allOutputs: ReadonlyArray<{ readonly mime: string }> }; + | { + readonly type: RenderOutputType.Extension; + readonly outputId: string; + readonly metadata: unknown; + readonly output: OutputItemEntry; + readonly allOutputs: ReadonlyArray<{ readonly mime: string }>; + }; export interface ICreationRequestMessage { readonly type: 'html'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 25dab44de71..190980671a0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -804,6 +804,7 @@ async function webviewPreloads(ctx: PreloadContext) { interface ExtendedOutputItem extends rendererApi.OutputItem { readonly _allOutputItems: ReadonlyArray; + appendedText?(): string | undefined; } let hasWarnedAboutAllOutputItemsProposal = false; @@ -813,7 +814,8 @@ async function webviewPreloads(ctx: PreloadContext) { mime: string, metadata: unknown, valueBytes: Uint8Array, - allOutputItemData: ReadonlyArray<{ readonly mime: string }> + allOutputItemData: ReadonlyArray<{ readonly mime: string }>, + appended?: { valueBytes: Uint8Array; previousVersion: number } ): ExtendedOutputItem { function create( @@ -821,12 +823,20 @@ async function webviewPreloads(ctx: PreloadContext) { mime: string, metadata: unknown, valueBytes: Uint8Array, + appended?: { valueBytes: Uint8Array; previousVersion: number } ): ExtendedOutputItem { return Object.freeze({ id, mime, metadata, + appendedText(): string | undefined { + if (appended) { + return textDecoder.decode(appended.valueBytes); + } + return undefined; + }, + data(): Uint8Array { return valueBytes; }, @@ -874,7 +884,7 @@ async function webviewPreloads(ctx: PreloadContext) { }); })); - const item = create(id, mime, metadata, valueBytes); + const item = create(id, mime, metadata, valueBytes, appended); allOutputItemCache.set(mime, Promise.resolve(item)); return item; } @@ -2542,7 +2552,7 @@ async function webviewPreloads(ctx: PreloadContext) { const errors = preloadErrors.filter((e): e is Error => e instanceof Error); showRenderError(`Error loading preloads`, this.element, errors); } else { - const item = createOutputItem(this.outputId, content.output.mime, content.metadata, content.output.valueBytes, content.allOutputs); + const item = createOutputItem(this.outputId, content.output.mime, content.metadata, content.output.valueBytes, content.allOutputs, content.output.appended); const controller = new AbortController(); this.renderTaskAbort = controller;