diff --git a/packages/core/src/cell.ts b/packages/core/src/cell.ts index 05528312..3e6aab2e 100644 --- a/packages/core/src/cell.ts +++ b/packages/core/src/cell.ts @@ -16,19 +16,19 @@ class ThebeCodeCell extends PassiveCellRenderer implements IThebeCell { session?: ThebeSession; executionCount: number | null; notebook?: ThebeNotebook; - protected initialOutputs: IOutput[]; protected busy: boolean; protected events: EventEmitter; constructor( id: string, source: string, + outputs: IOutput[], config: Config, metadata: JsonObject, rendermime: IRenderMimeRegistry, notebook?: ThebeNotebook, ) { - super(id, rendermime); + super(id, outputs, rendermime); this.kind = 'code'; this.events = new EventEmitter(id, config, EventSubject.cell, this); this.notebook = notebook; @@ -36,7 +36,6 @@ class ThebeCodeCell extends PassiveCellRenderer implements IThebeCell { this.metadata = metadata; this.busy = false; this.executionCount = null; - this.initialOutputs = []; console.debug('thebe:cell constructor', this); } @@ -49,6 +48,7 @@ class ThebeCodeCell extends PassiveCellRenderer implements IThebeCell { const cell = new ThebeCodeCell( icc.id ?? shortId(), ensureString(icc.source), + icc.outputs ?? [], config, icc.metadata, rendermime, diff --git a/packages/core/src/markdown.ts b/packages/core/src/markdown.ts index 8dd8d8c6..227e691a 100644 --- a/packages/core/src/markdown.ts +++ b/packages/core/src/markdown.ts @@ -29,7 +29,7 @@ export default class ThebeMarkdownCell extends PassiveCellRenderer implements IT rendermime: IRenderMimeRegistry, notebook?: ThebeNotebook, ) { - super(id, rendermime); + super(id, [], rendermime); this.kind = 'markdown'; this.id = id; this.notebook = notebook; diff --git a/packages/core/src/notebook.ts b/packages/core/src/notebook.ts index 90d1af61..9c1dba93 100644 --- a/packages/core/src/notebook.ts +++ b/packages/core/src/notebook.ts @@ -53,6 +53,7 @@ class ThebeNotebook { const cell = new ThebeCodeCell( c.id, c.source, + c.outputs ?? [], config, metadata, notebook.rendermime, diff --git a/packages/core/src/passive.ts b/packages/core/src/passive.ts index 1bf71a7c..efc57953 100644 --- a/packages/core/src/passive.ts +++ b/packages/core/src/passive.ts @@ -7,20 +7,35 @@ import { makeMathjaxOptions } from './options'; import { Widget } from '@lumino/widgets'; import { MessageLoop } from '@lumino/messaging'; +function assert(condition: any, msg?: string): asserts condition { + if (!condition) { + throw new Error(msg); + } +} + class PassiveCellRenderer implements IPassiveCell { readonly id: string; readonly rendermime: IRenderMimeRegistry; + initialOutputs: nbformat.IOutput[]; + protected model: OutputAreaModel; protected area: OutputArea; - constructor(id: string, rendermime?: IRenderMimeRegistry, mathjax?: MathjaxOptions) { + constructor( + id: string, + initialOutputs?: nbformat.IOutput[], + rendermime?: IRenderMimeRegistry, + mathjax?: MathjaxOptions, + ) { this.id = id; this.rendermime = rendermime ?? makeRenderMimeRegistry(mathjax ?? makeMathjaxOptions()); + assert(this.rendermime, 'no rendermime'); this.model = new OutputAreaModel({ trusted: true }); this.area = new OutputArea({ model: this.model, rendermime: this.rendermime, }); + this.initialOutputs = initialOutputs ?? []; } /** @@ -34,7 +49,10 @@ class PassiveCellRenderer implements IPassiveCell { return this.area.isAttached; } - attachToDOM(el?: HTMLElement, strict = false) { + attachToDOM( + el?: HTMLElement, + opts: { strict?: boolean; appendExisting?: boolean } = { strict: false, appendExisting: true }, + ) { if (!this.area || !el) { console.error( `thebe:renderer:attachToDOM - could not attach to DOM - area: ${this.area}, el: ${el}`, @@ -44,11 +62,11 @@ class PassiveCellRenderer implements IPassiveCell { if (this.area.isAttached) { // TODO should we detach and reattach? console.debug(`thebe:renderer:attachToDOM - already attached`); - if (strict) return; + if (opts.strict) return; } else { // if the target element has contents, preserve it but wrap it in our output area console.debug(`thebe:renderer:attachToDOM ${this.id} - appending existing contents`); - if (el.innerHTML) { + if (opts.appendExisting && el.innerHTML) { this.area.model.add({ output_type: 'display_data', data: { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a3c1e58a..88010854 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -6,9 +6,7 @@ import type ThebeServer from './server'; import type { ServerStatusEvent } from './events'; import type { Config } from './config'; import type ThebeNotebook from './notebook'; - export type CellKind = 'code' | 'markdown'; - export type JsonObject = Record; export type SessionIModel = Session.IModel; export type KernelISpecModels = KernelSpecAPI.ISpecModels; @@ -86,8 +84,9 @@ export interface IPassiveCell { readonly rendermime: IRenderMimeRegistry; readonly isAttachedToDOM: boolean; readonly outputs: IOutput[]; + readonly initialOutputs: IOutput[]; - attachToDOM(el?: HTMLElement): void; + attachToDOM(el?: HTMLElement, opts?: { strict?: boolean; appendExisting?: boolean }): void; setOutputText(text: string): void; clear(): void; clearOnError(error?: any): void; diff --git a/packages/react/src/hooks/notebook.ts b/packages/react/src/hooks/notebook.ts index 3c10641c..839d8628 100644 --- a/packages/react/src/hooks/notebook.ts +++ b/packages/react/src/hooks/notebook.ts @@ -179,17 +179,19 @@ export function useNotebook( .map((_, idx) => (node) => { console.debug(`new ref[${idx}] - attaching to dom...`, node); if (node != null) { - cells[idx].attachToDOM(node); - cells[idx].render(cells[idx].outputs); - cells[idx].setOutputText('attached to DOM [OUTPUT]'); + cells[idx].attachToDOM(node, { appendExisting: false }); + cells[idx].render(cells[idx].initialOutputs); + // cells[idx].setOutputText('attached to DOM [OUTPUT]'); + console.log('about to hydrate widgets', cells[idx].outputs); cells[idx].outputs.forEach((output, i) => { + console.log('output', i, output); if ( (output.output_type === 'display_data' || output.output_type === 'execute_result') && typeof output.data === 'object' ) { - console.log('output', i, output); + console.log('output:confirmed', i, output); const mimeBundles = output.data as IExecuteResult; if (mimeBundles[WIDGET_VIEW_MIMETYPE]) { const model_id = (mimeBundles[WIDGET_VIEW_MIMETYPE] as { model_id: string })