UNPKG

@jupyter-widgets/jupyterlab-manager

Version:

The JupyterLab extension providing Jupyter widgets.

387 lines 15 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { IConsoleTracker, } from '@jupyterlab/console'; import { INotebookTracker, } from '@jupyterlab/notebook'; import { IMainMenu } from '@jupyterlab/mainmenu'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { ILoggerRegistry } from '@jupyterlab/logconsole'; import { filter } from '@lumino/algorithm'; import { DisposableDelegate } from '@lumino/disposable'; import { WidgetRenderer } from './renderer'; import { WidgetManager, WIDGET_VIEW_MIMETYPE, KernelWidgetManager, } from './manager'; import { OutputModel, OutputView, OUTPUT_WIDGET_VERSION } from './output'; import * as base from '@jupyter-widgets/base'; // We import only the version from the specific module in controls so that the // controls code can be split and dynamically loaded in webpack. import { JUPYTER_CONTROLS_VERSION } from '@jupyter-widgets/controls/lib/version'; import '@jupyter-widgets/base/css/index.css'; import '@jupyter-widgets/controls/css/widgets-base.css'; import { KernelMessage } from '@jupyterlab/services'; import { ITranslator, nullTranslator } from '@jupyterlab/translation'; const WIDGET_REGISTRY = []; /** * The cached settings. */ const SETTINGS = { saveState: false }; /** * Iterate through all widget renderers in a notebook. */ function* notebookWidgetRenderers(nb) { for (const cell of nb.widgets) { if (cell.model.type === 'code') { for (const codecell of cell.outputArea.widgets) { // We use Array.from instead of using Lumino 2 (JLab 4) iterator // This is to support Lumino 1 (JLab 3) as well for (const output of Array.from(codecell.children())) { if (output instanceof WidgetRenderer) { yield output; } } } } } } /** * Iterate through all widget renderers in a console. */ function* consoleWidgetRenderers(console) { for (const cell of Array.from(console.cells)) { if (cell.model.type === 'code') { for (const codecell of cell.outputArea.widgets) { for (const output of Array.from(codecell.children())) { if (output instanceof WidgetRenderer) { yield output; } } } } } } /** * Iterate through all matching linked output views */ function* outputViews(app, path) { const linkedViews = filter(app.shell.widgets(), (w) => w.id.startsWith('LinkedOutputView-') && w.path === path); // We use Array.from instead of using Lumino 2 (JLab 4) iterator // This is to support Lumino 1 (JLab 3) as well for (const view of Array.from(linkedViews)) { for (const outputs of Array.from(view.children())) { for (const output of Array.from(outputs.children())) { if (output instanceof WidgetRenderer) { yield output; } } } } } function* chain(...args) { for (const it of args) { yield* it; } } /** * Get the kernel id of current notebook or console panel, this value * is used as key for `Private.widgetManagerProperty` to store the widget * manager of current notebook or console panel. * * @param {ISessionContext} sessionContext The session context of notebook or * console panel. */ async function getWidgetManagerOwner(sessionContext) { await sessionContext.ready; return sessionContext.session.kernel.id; } /** * Common handler for registering both notebook and console * `WidgetManager` * * @param {(Notebook | CodeConsole)} content Context of panel. * @param {ISessionContext} sessionContext Session context of panel. * @param {IRenderMimeRegistry} rendermime Rendermime of panel. * @param {IterableIterator<WidgetRenderer>} renderers Iterator of * `WidgetRenderer` inside panel * @param {(() => WidgetManager | KernelWidgetManager)} widgetManagerFactory * function to create widget manager. */ async function registerWidgetHandler(content, sessionContext, rendermime, renderers, widgetManagerFactory) { const wManagerOwner = await getWidgetManagerOwner(sessionContext); let wManager = Private.widgetManagerProperty.get(wManagerOwner); let currentOwner; if (!wManager) { wManager = widgetManagerFactory(); WIDGET_REGISTRY.forEach((data) => wManager.register(data)); Private.widgetManagerProperty.set(wManagerOwner, wManager); currentOwner = wManagerOwner; content.disposed.connect((_) => { const currentwManager = Private.widgetManagerProperty.get(currentOwner); if (currentwManager) { Private.widgetManagerProperty.delete(currentOwner); } }); sessionContext.kernelChanged.connect((_, args) => { const { newValue } = args; if (newValue) { const newKernelId = newValue.id; const oldwManager = Private.widgetManagerProperty.get(currentOwner); if (oldwManager) { Private.widgetManagerProperty.delete(currentOwner); Private.widgetManagerProperty.set(newKernelId, oldwManager); } currentOwner = newKernelId; } }); } for (const r of renderers) { r.manager = wManager; } // Replace the placeholder widget renderer with one bound to this widget // manager. rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); rendermime.addFactory({ safe: false, mimeTypes: [WIDGET_VIEW_MIMETYPE], createRenderer: (options) => new WidgetRenderer(options, wManager), }, -10); return new DisposableDelegate(() => { if (rendermime) { rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); } wManager.dispose(); }); } // Kept for backward compat ipywidgets<=8, but not used here anymore export function registerWidgetManager(context, rendermime, renderers) { let wManager; const managerReady = getWidgetManagerOwner(context.sessionContext).then((wManagerOwner) => { const currentManager = Private.widgetManagerProperty.get(wManagerOwner); if (!currentManager) { wManager = new WidgetManager(context, rendermime, SETTINGS); WIDGET_REGISTRY.forEach((data) => wManager.register(data)); Private.widgetManagerProperty.set(wManagerOwner, wManager); } else { wManager = currentManager; } for (const r of renderers) { r.manager = wManager; } // Replace the placeholder widget renderer with one bound to this widget // manager. rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); rendermime.addFactory({ safe: false, mimeTypes: [WIDGET_VIEW_MIMETYPE], createRenderer: (options) => new WidgetRenderer(options, wManager), }, -10); }); return new DisposableDelegate(async () => { await managerReady; if (rendermime) { rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); } wManager.dispose(); }); } export async function registerNotebookWidgetManager(panel, renderers) { const content = panel.content; const context = panel.context; const sessionContext = context.sessionContext; const rendermime = content.rendermime; const widgetManagerFactory = () => new WidgetManager(context, rendermime, SETTINGS); return registerWidgetHandler(content, sessionContext, rendermime, renderers, widgetManagerFactory); } export async function registerConsoleWidgetManager(panel, renderers) { const content = panel.console; const sessionContext = content.sessionContext; const rendermime = content.rendermime; const widgetManagerFactory = () => new KernelWidgetManager(sessionContext.session.kernel, rendermime); return registerWidgetHandler(content, sessionContext, rendermime, renderers, widgetManagerFactory); } /** * The widget manager provider. */ export const managerPlugin = { id: '@jupyter-widgets/jupyterlab-manager:plugin', requires: [IRenderMimeRegistry], optional: [ INotebookTracker, IConsoleTracker, ISettingRegistry, IMainMenu, ILoggerRegistry, ITranslator, ], provides: base.IJupyterWidgetRegistry, activate: activateWidgetExtension, autoStart: true, }; function updateSettings(settings) { SETTINGS.saveState = settings.get('saveState').composite; } /** * Activate the widget extension. */ function activateWidgetExtension(app, rendermime, tracker, consoleTracker, settingRegistry, menu, loggerRegistry, translator) { const { commands } = app; const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab_widgets'); const bindUnhandledIOPubMessageSignal = async (nb) => { if (!loggerRegistry) { return; } const wManagerOwner = await getWidgetManagerOwner(nb.context.sessionContext); const wManager = Private.widgetManagerProperty.get(wManagerOwner); if (wManager) { wManager.onUnhandledIOPubMessage.connect((sender, msg) => { const logger = loggerRegistry.getLogger(nb.context.path); let level = 'warning'; if (KernelMessage.isErrorMsg(msg) || (KernelMessage.isStreamMsg(msg) && msg.content.name === 'stderr')) { level = 'error'; } const data = Object.assign(Object.assign({}, msg.content), { output_type: msg.header.msg_type }); logger.rendermime = nb.content.rendermime; logger.log({ type: 'output', data, level }); }); } }; if (settingRegistry !== null) { settingRegistry .load(managerPlugin.id) .then((settings) => { settings.changed.connect(updateSettings); updateSettings(settings); }) .catch((reason) => { console.error(reason.message); }); } // Add a placeholder widget renderer. rendermime.addFactory({ safe: false, mimeTypes: [WIDGET_VIEW_MIMETYPE], createRenderer: (options) => new WidgetRenderer(options), }, -10); if (tracker !== null) { const rendererIterator = (panel) => chain(notebookWidgetRenderers(panel.content), outputViews(app, panel.context.path)); tracker.forEach(async (panel) => { await registerNotebookWidgetManager(panel, rendererIterator(panel)); bindUnhandledIOPubMessageSignal(panel); }); tracker.widgetAdded.connect(async (sender, panel) => { await registerNotebookWidgetManager(panel, rendererIterator(panel)); bindUnhandledIOPubMessageSignal(panel); }); } if (consoleTracker !== null) { const rendererIterator = (panel) => chain(consoleWidgetRenderers(panel.console)); consoleTracker.forEach(async (panel) => { await registerConsoleWidgetManager(panel, rendererIterator(panel)); }); consoleTracker.widgetAdded.connect(async (sender, panel) => { await registerConsoleWidgetManager(panel, rendererIterator(panel)); }); } if (settingRegistry !== null) { // Add a command for automatically saving (jupyter-)widget state. commands.addCommand('@jupyter-widgets/jupyterlab-manager:saveWidgetState', { label: trans.__('Save Widget State Automatically'), execute: (args) => { return settingRegistry .set(managerPlugin.id, 'saveState', !SETTINGS.saveState) .catch((reason) => { console.error(`Failed to set ${managerPlugin.id}: ${reason.message}`); }); }, isToggled: () => SETTINGS.saveState, }); } if (menu) { menu.settingsMenu.addGroup([ { command: '@jupyter-widgets/jupyterlab-manager:saveWidgetState' }, ]); } return { registerWidget(data) { WIDGET_REGISTRY.push(data); }, }; } /** * The base widgets. */ export const baseWidgetsPlugin = { id: `@jupyter-widgets/jupyterlab-manager:base-${base.JUPYTER_WIDGETS_VERSION}`, requires: [base.IJupyterWidgetRegistry], autoStart: true, activate: (app, registry) => { registry.registerWidget({ name: '@jupyter-widgets/base', version: base.JUPYTER_WIDGETS_VERSION, exports: { WidgetModel: base.WidgetModel, WidgetView: base.WidgetView, DOMWidgetView: base.DOMWidgetView, DOMWidgetModel: base.DOMWidgetModel, LayoutModel: base.LayoutModel, LayoutView: base.LayoutView, StyleModel: base.StyleModel, StyleView: base.StyleView, ErrorWidgetView: base.ErrorWidgetView, }, }); }, }; /** * The control widgets. */ export const controlWidgetsPlugin = { id: `@jupyter-widgets/jupyterlab-manager:controls-${JUPYTER_CONTROLS_VERSION}`, requires: [base.IJupyterWidgetRegistry], autoStart: true, activate: (app, registry) => { registry.registerWidget({ name: '@jupyter-widgets/controls', version: JUPYTER_CONTROLS_VERSION, exports: () => { return new Promise((resolve, reject) => { require.ensure(['@jupyter-widgets/controls'], (require) => { // eslint-disable-next-line @typescript-eslint/no-var-requires resolve(require('@jupyter-widgets/controls')); }, (err) => { reject(err); }, '@jupyter-widgets/controls'); }); }, }); }, }; /** * The output widget. */ export const outputWidgetPlugin = { id: `@jupyter-widgets/jupyterlab-manager:output-${OUTPUT_WIDGET_VERSION}`, requires: [base.IJupyterWidgetRegistry], autoStart: true, activate: (app, registry) => { registry.registerWidget({ name: '@jupyter-widgets/output', version: OUTPUT_WIDGET_VERSION, exports: { OutputModel, OutputView }, }); }, }; export default [ managerPlugin, baseWidgetsPlugin, controlWidgetsPlugin, outputWidgetPlugin, ]; var Private; (function (Private) { /** * A private map for a widget manager. */ Private.widgetManagerProperty = new Map(); })(Private || (Private = {})); //# sourceMappingURL=plugin.js.map