@jupyter-widgets/jupyterlab-manager
Version:
The JupyterLab extension providing Jupyter widgets.
387 lines • 15 kB
JavaScript
// 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