thebe-core
Version:
Typescript based core functionality for Thebe
138 lines (121 loc) • 4.53 kB
text/typescript
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import type { Widget } from '@lumino/widgets';
import * as LuminoWidget from '@lumino/widgets';
import { MessageLoop } from '@lumino/messaging';
import { KernelWidgetManager, WidgetRenderer, output } from '@jupyter-widgets/jupyterlab-manager';
export const WIDGET_MIMETYPE = 'application/vnd.jupyter.widget-view+json';
import * as base from '@jupyter-widgets/base';
import * as controls from '@jupyter-widgets/controls';
import { shortId } from './utils';
import { RequireJsLoader } from './requireJsLoader';
import { requireLoader } from './loader';
import type { Kernel } from '@jupyterlab/services';
/**
* A Widget Manager class for Thebe using the context-free KernelWidgetManager from
* the JupyterLab Manager and inspierd by the implementation in Voila here:
* https://github.dev/voila-dashboards/voila/blob/main/packages/voila/src/manager.ts
*
*/
export class ThebeManager extends KernelWidgetManager {
id: string;
_loader: RequireJsLoader;
constructor(kernel: Kernel.IKernelConnection, rendermime: IRenderMimeRegistry) {
super(kernel, rendermime);
this.id = shortId();
/** ensure this registry always gets the widget renderer.
* This is essential for cases where widgets are rendered heirarchically
*/
this.addWidgetFactories();
this._registerWidgets();
this._loader = new RequireJsLoader();
}
addWidgetFactories() {
this.rendermime.addFactory(
{
safe: false,
mimeTypes: [WIDGET_MIMETYPE],
createRenderer: (options) => new WidgetRenderer(options, this as any),
},
1,
);
}
removeWidgetFactories() {
this.rendermime.removeMimeType(WIDGET_MIMETYPE);
}
/**
* TODO implement a reasonable method for thebe-core that can load serialized widget state
* see: https://github.dev/voila-dashboards/voila/blob/7090eb3e30c0c4aa25c2b7d5d2d45e8de1333b3b/packages/voila/src/manager.ts#L52
*
*/
async build_widgets(): Promise<void> {
throw new Error('ThebeManager:build_widgets not implmented');
}
async display_view(msg: any, view: any, options: any): Promise<Widget> {
if (options.el) {
LuminoWidget.Widget.attach(view.luminoWidget, options.el);
}
if (view.el) {
view.el.setAttribute('data-thebe-jupyter-widget', '');
view.el.addEventListener('jupyterWidgetResize', () => {
MessageLoop.postMessage(view.luminoWidget, LuminoWidget.Widget.ResizeMessage.UnknownSize);
});
}
return view.luminoWidget;
}
async loadClass(
className: string,
moduleName: string,
moduleVersion: string,
): Promise<typeof base.WidgetModel | typeof base.WidgetView> {
if (!this._loader.requested) {
console.debug(`thebe:manager:loadClass initial requirejs load ${this.id}`);
this._loader.load((require, define) => {
define('@jupyter-widgets/base', base as any);
define('@jupyter-widgets/controls', controls as any);
define('@jupyter-widgets/output', output as any);
});
}
console.debug(`thebe:manager:loadClass ${moduleName}@${moduleVersion}`);
const rjs = await this._loader.ready;
if (
moduleName === '@jupyter-widgets/base' ||
moduleName === '@jupyter-widgets/controls' ||
moduleName === '@jupyter-widgets/output'
) {
return super.loadClass(className, moduleName, moduleVersion);
} else {
let mod;
try {
mod = await requireLoader(rjs, moduleName, moduleVersion);
} catch (err) {
console.error(`thebe:manager:loadClass loader error`, err);
throw err;
}
if (mod[className]) {
return mod[className];
} else {
console.error(
`thebe:manager:loadClass ${className} not found in module ${moduleName}@${moduleVersion}`,
);
throw new Error(`Class ${className} not found in module ${moduleName}@${moduleVersion}`);
}
}
}
private _registerWidgets() {
this.register({
name: '@jupyter-widgets/base',
version: base.JUPYTER_WIDGETS_VERSION,
exports: base as unknown as base.ExportData, // TODO improve typing
});
this.register({
name: '@jupyter-widgets/controls',
version: controls.JUPYTER_CONTROLS_VERSION,
exports: controls as unknown as base.ExportData, // TODO improve typing
});
this.register({
name: '@jupyter-widgets/output',
version: output.OUTPUT_WIDGET_VERSION,
exports: output as any,
});
}
}