From 1bb65f4ea0edd0a742d752dcb5920568de7be472 Mon Sep 17 00:00:00 2001 From: Alan Fleming <> Date: Mon, 3 Jun 2024 22:01:20 +1000 Subject: [PATCH] Manager, WidgetManager and plugin refactoring (simplification) . Warning: some signature changes. --- packages/base-manager/src/manager-base.ts | 2 +- python/jupyterlab_widgets/package.json | 2 - python/jupyterlab_widgets/src/manager.ts | 283 ++++++++++++---------- python/jupyterlab_widgets/src/plugin.ts | 143 +---------- python/jupyterlab_widgets/src/renderer.ts | 17 +- yarn.lock | 18 +- 6 files changed, 184 insertions(+), 281 deletions(-) diff --git a/packages/base-manager/src/manager-base.ts b/packages/base-manager/src/manager-base.ts index aa63ab24ed..7f578fd9ed 100644 --- a/packages/base-manager/src/manager-base.ts +++ b/packages/base-manager/src/manager-base.ts @@ -217,7 +217,7 @@ export abstract class ManagerBase implements IWidgetManager { async get_model(model_id: string): Promise { let i = 0; while (!this._models[model_id] && i < this._sleepTimes.length) { - new Promise((r) => setTimeout(r, this._sleepTimes[i++])) + new Promise((resolve) => setTimeout(resolve, this._sleepTimes[i++])); } const modelPromise = this._models[model_id]; if (modelPromise === undefined) { diff --git a/python/jupyterlab_widgets/package.json b/python/jupyterlab_widgets/package.json index 1711edbb5f..c06acc14e3 100644 --- a/python/jupyterlab_widgets/package.json +++ b/python/jupyterlab_widgets/package.json @@ -64,7 +64,6 @@ "@jupyterlab/services": "^6.0.0 || ^7.0.0", "@jupyterlab/settingregistry": "^3.0.0 || ^4.0.0", "@jupyterlab/translation": "^3.0.0 || ^4.0.0", - "@lumino/algorithm": "^1.11.1 || ^2.0.0", "@lumino/coreutils": "^1.11.1 || ^2.1", "@lumino/disposable": "^1.10.1 || ^2.1", "@lumino/properties": "^2.0.1", @@ -76,7 +75,6 @@ }, "devDependencies": { "@jupyterlab/builder": "^3.0.0 || ^4.0.0", - "@jupyterlab/cells": "^3.0.0 || ^4.0.0", "@types/jquery": "^3.5.16", "@types/semver": "^7.3.6", "@typescript-eslint/eslint-plugin": "^5.8.0", diff --git a/python/jupyterlab_widgets/src/manager.ts b/python/jupyterlab_widgets/src/manager.ts index a1d9d73d97..9bd5ed42fb 100644 --- a/python/jupyterlab_widgets/src/manager.ts +++ b/python/jupyterlab_widgets/src/manager.ts @@ -20,6 +20,8 @@ import { import { IDisposable } from '@lumino/disposable'; +import { AttachedProperty } from '@lumino/properties'; + import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; import { ReadonlyPartialJSONValue } from '@lumino/coreutils'; @@ -351,7 +353,7 @@ export abstract class LabWidgetManager /** * A singleton widget manager per kernel for the lifecycle of the kernel. - * The factory of the rendermime will be update to use the widgetManager + * The factory of the rendermime will be updated to use the widgetManager * directly if it isn't the globalRendermime. * * Note: The rendermime of the instance is always the global rendermime. @@ -365,8 +367,11 @@ export class KernelWidgetManager extends LabWidgetManager { const instance = Private.kernelWidgetManagers.get(kernel.id); if (instance) { instance._useKernel(kernel); - configureRendermime(rendermime, instance, pendingManagerMessage); - instance._firstLoad = false; + KernelWidgetManager.configureRendermime( + rendermime, + instance, + pendingManagerMessage + ); return instance; } if (!kernel.handleComms) { @@ -379,7 +384,11 @@ export class KernelWidgetManager extends LabWidgetManager { this.loadCustomWidgetDefinitions() ); this._useKernel(kernel); - configureRendermime(rendermime, this, pendingManagerMessage); + KernelWidgetManager.configureRendermime( + rendermime, + this, + pendingManagerMessage + ); } _useKernel(this: KernelWidgetManager, kernel: Kernel.IKernelConnection) { @@ -412,6 +421,38 @@ export class KernelWidgetManager extends LabWidgetManager { this.clear_state().then(() => this.restoreWidgets()); } + /** + * Configure a non-global rendermime. Passing the global rendermine will do + * nothing. + * + * @param rendermime + * @param manager The manager to use with WidgetRenderer. + * @param pendingManagerMessage A message that is displayed while the manager + * has not been provided. If manager is not provided here a non-empty string + * assumes the manager will be provided at some time in the future. + * + * The default will search for a manager once prior to waiting for a manager. + * @returns + */ + static configureRendermime( + rendermime?: IRenderMimeRegistry, + manager?: KernelWidgetManager, + pendingManagerMessage = '' + ) { + if (!rendermime || rendermime === LabWidgetManager.globalRendermime) { + return; + } + rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); + rendermime.addFactory( + { + safe: false, + mimeTypes: [WIDGET_VIEW_MIMETYPE], + createRenderer: (options: IRenderMime.IRendererOptions) => + new WidgetRenderer(options, manager, pendingManagerMessage), + }, + -10 + ); + } _handleKernelConnectionStatusChange( sender: Kernel.IKernelConnection, status: Kernel.ConnectionStatus @@ -451,10 +492,13 @@ export class KernelWidgetManager extends LabWidgetManager { } catch (err) { // Do nothing } + this.triggerRestored(); + } + + triggerRestored() { this._restoredStatus = true; this._restored.emit(); } - /** * Dispose the resources held by the manager. */ @@ -463,7 +507,7 @@ export class KernelWidgetManager extends LabWidgetManager { return; } super.dispose(); - configureRendermime(this.rendermime); + KernelWidgetManager.configureRendermime(this.rendermime); Private.kernelWidgetManagers.delete(this.kernel.id); this._handleKernelChanged({ name: 'kernel', @@ -478,10 +522,6 @@ export class KernelWidgetManager extends LabWidgetManager { return this._kernel; } - get firstLoad() { - return this._firstLoad; - } - loadCustomWidgetDefinitions() { for (const data of LabWidgetManager.WIDGET_REGISTRY) { this.register(data); @@ -491,27 +531,34 @@ export class KernelWidgetManager extends LabWidgetManager { filterModelState(serialized_state: any): any { return this.filterExistingModelState(serialized_state); } - private _firstLoad = true; + private _kernel: Kernel.IKernelConnection; protected _kernelRestoreInProgress = false; } /** - * Monitor kernel of the Context swapping the kernel manager on demand. - * A better name would be `NotebookManagerSwitcher'. + * A single 'WidgetManager' per context. + * It monitors the kernel of the context swapping the kernel manager when the + * kernel is changed. + * A better name would be `WidgetManagerChanger'. TODO: change name and context. */ export class WidgetManager extends Backbone.Model implements IDisposable { constructor( - context: DocumentRegistry.IContext, + context: DocumentRegistry.Context, rendermime: IRenderMimeRegistry, - settings: WidgetManager.Settings, - renderers?: IterableIterator + settings?: WidgetManager.Settings ) { + const instance = Private.widgetManagerProperty.get(context); + if (instance) { + WidgetManager._rendermimeSetFactory(rendermime, instance); + return instance; + } super(); - this._rendermime = rendermime; + Private.widgetManagerProperty.set(context, this); this._context = context; this._settings = settings; - this._renderers = renderers; + this._renderers = new Set(); + WidgetManager._rendermimeSetFactory(rendermime, this); context.sessionContext.kernelChanged.connect( this._handleKernelChange, @@ -529,7 +576,7 @@ export class WidgetManager extends Backbone.Model implements IDisposable { ); if (context?.saveState) { context.saveState.connect((sender, saveState) => { - if (saveState === 'started' && settings.saveState) { + if (saveState === 'started' && settings?.saveState) { this._saveState(); } }); @@ -545,26 +592,36 @@ export class WidgetManager extends Backbone.Model implements IDisposable { return; } const state = this.widgetManager.get_state_sync({ drop_defaults: true }); - if (this._context.model.setMetadata) { - this._context.model.setMetadata('widgets', { - 'application/vnd.jupyter.widget-state+json': state, - }); - } else { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore JupyterLab 3 support - this._context.model.metadata.set('widgets', { + const model = this._context.model; + if (model instanceof NotebookModel) { + model.setMetadata('widgets', { 'application/vnd.jupyter.widget-state+json': state, }); } } + static _rendermimeSetFactory( + rendermime: IRenderMimeRegistry, + manager: WidgetManager + ) { + if (rendermime === LabWidgetManager.globalRendermime) { + throw new Error('Using global rendermime is not permitted!'); + } + rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); + rendermime.addFactory( + { + safe: false, + mimeTypes: [WIDGET_VIEW_MIMETYPE], + createRenderer: manager._newWidgetRenderer.bind(manager), + }, + -10 + ); + } + async updateWidgetManager() { let wManager: KernelWidgetManager | undefined; - if (!this.kernel) { - //'No kernel' is matched in WidgetRenderer to supress 'model not found errors'. - configureRendermime(this.rendermime, undefined, 'No kernel'); - } else { - await this.context.sessionContext.ready; + await this.context.sessionContext.ready; + if (this.kernel) { wManager = new KernelWidgetManager(this.kernel); } if (wManager === this._widgetManager) { @@ -580,28 +637,38 @@ export class WidgetManager extends Backbone.Model implements IDisposable { if (!wManager) { return; } - if (wManager.firstLoad) { + wManager.onUnhandledIOPubMessage.connect( + this.onUnhandledIOPubMessage, + this + ); + if (!wManager.restored) { await new Promise((resolve) => { this._widgetManager?.restored.connect(resolve); }); - if (!this.restored) { - this.restoreWidgets(this._context!.model); - } } - if (this._renderers) { - for (const r of this._renderers) { - r.manager = wManager; - } + this._renderers.forEach( + (renderer: WidgetRenderer) => (renderer.manager = wManager) + ); + if (await this._restoreWidgets(this._context!.model)) { + wManager.triggerRestored(); } - configureRendermime(this.rendermime, wManager, 'Loading widget ...'); - wManager.onUnhandledIOPubMessage.connect( - this.onUnhandledIOPubMessage, - this + } + + _newWidgetRenderer(options: IRenderMime.IRendererOptions) { + const renderer = new WidgetRenderer( + options, + this.widgetManager, + this.widgetManager ? 'Loading widget ...' : 'No kernel' + ); + this._renderers.add(renderer); + renderer.disposed.connect((renderer_: WidgetRenderer) => + this._renderers.delete(renderer_) ); + return renderer; } onUnhandledIOPubMessage( - sender: LabWidgetManager, + sender: KernelWidgetManager, msg: KernelMessage.IIOPubMessage ) { if (WidgetManager.loggerRegistry) { @@ -641,34 +708,16 @@ export class WidgetManager extends Backbone.Model implements IDisposable { return this._widgetManager; } - /** - * A signal emitted when state is restored to the widget manager. - * - * #### Notes - * This indicates that previously-unavailable widget models might be available now. - */ - get restored(): ISignal { - return this._restored; - } - - /** - * Whether the state has been restored yet or not. - */ - get restoredStatus(): boolean { - return this._restoredStatus; - } - /** * Restore widgets from model. */ - async restoreWidgets(model: INotebookModel): Promise { + async _restoreWidgets( + model: DocumentRegistry.IModel + ): Promise { try { if (model instanceof NotebookModel) { - await this._loadFromNotebook(model); + return await this._loadFromNotebook(model); } - // If the restore worked above, then update our state. - this._restoredStatus = true; - this._restored.emit(); } catch (err) { // Do nothing if the restore did not work. } @@ -677,22 +726,25 @@ export class WidgetManager extends Backbone.Model implements IDisposable { /** * Load widget state from notebook metadata */ - async _loadFromNotebook(notebook: INotebookModel): Promise { - if (!this.widgetManager) { - return; - } - const widget_md = notebook.getMetadata - ? (notebook.getMetadata('widgets') as any) - : // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore JupyterLab 3 support - notebook.metadata.get('widgets'); - if (widget_md && widget_md[WIDGET_STATE_MIMETYPE]) { - let state = widget_md[WIDGET_STATE_MIMETYPE]; - state = this.widgetManager.filterModelState(state); - - // Restore any widgets from saved state that are not live - await this.widgetManager.set_state(state); + async _loadFromNotebook(notebook: INotebookModel): Promise { + if (this.widgetManager) { + const widget_md = notebook.getMetadata + ? (notebook.getMetadata('widgets') as any) + : // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore JupyterLab 3 support + notebook.metadata.get('widgets'); + if (widget_md && widget_md[WIDGET_STATE_MIMETYPE]) { + let state = widget_md[WIDGET_STATE_MIMETYPE]; + state = this.widgetManager.filterModelState(state); + const n = Object.keys(state?.state || {}).length; + if (n) { + // Restore any widgets from saved state that are not live + await this.widgetManager.set_state(state); + } + return n; + } } + return 0; } /** @@ -712,8 +764,11 @@ export class WidgetManager extends Backbone.Model implements IDisposable { if (this.isDisposed) { return; } + // Remove the custom factory from the rendermime. TODO: de-register the rendermime factory for this object + KernelWidgetManager.configureRendermime(this.rendermime); + this._renderers.forEach((renderer) => renderer.dispose()); + this._renderers = null!; this._context = null!; - this._renderers = undefined; this._context = null!; this._rendermime = null!; this._settings = null!; @@ -727,7 +782,7 @@ export class WidgetManager extends Backbone.Model implements IDisposable { return this.context.urlResolver.getDownloadUrl(partial); } - get context(): DocumentRegistry.IContext { + get context(): DocumentRegistry.Context { return this._context; } @@ -745,19 +800,19 @@ export class WidgetManager extends Backbone.Model implements IDisposable { * TODO: perhaps should also set dirty when any model changes any data */ setDirty(): void { - if (this._settings.saveState) { - this._context!.model.dirty = true; + if (this._settings?.saveState && this._context?.model) { + this._context.model.dirty = true; } } static loggerRegistry: ILoggerRegistry | null; - protected _restored = new Signal(this); - protected _restoredStatus = false; + // protected _restored = new Signal(this); + // protected _restoredStatus = false; private _isDisposed = false; - private _context: DocumentRegistry.IContext; + private _context: DocumentRegistry.Context; private _rendermime: IRenderMimeRegistry; - private _settings: WidgetManager.Settings; + private _settings: WidgetManager.Settings | undefined; private _widgetManager: KernelWidgetManager | undefined; - private _renderers: IterableIterator | undefined; + _renderers: Set; } export namespace WidgetManager { @@ -766,54 +821,21 @@ export namespace WidgetManager { }; } -/** - * Configure a non-global rendermime. Passing the global rendermine will do - * nothing. - * - * @param rendermime - * @param manager The manager to use with WidgetRenderer. - * @param pendingManagerMessage A message that is displayed while the manager - * has not been provided. If manager is not provided here a non-empty string - * assumes the manager will be provided at some time in the future. - * - * The default will search for a manager once prior to waiting for a manager. - * @returns - */ -function configureRendermime( - rendermime?: IRenderMimeRegistry, - manager?: KernelWidgetManager, - pendingManagerMessage = '' -) { - if (!rendermime || rendermime === LabWidgetManager.globalRendermime) { - return; - } - rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE); - rendermime.addFactory( - { - safe: false, - mimeTypes: [WIDGET_VIEW_MIMETYPE], - createRenderer: (options: IRenderMime.IRendererOptions) => - new WidgetRenderer(options, manager, pendingManagerMessage), - }, - -10 - ); -} - /** * Get the widgetManager that owns the model. */ export async function getWidgetManager( model_id: string -): Promise { +): Promise { for (const sleepTime of [0, 50, 1000]) { - await new Promise((r) => setTimeout(r, sleepTime)); + await new Promise((resolve) => setTimeout(resolve, sleepTime)); for (const wManager of Private.kernelWidgetManagers.values()) { if (wManager.has_model(model_id)) { return wManager; } } } - return null; + return undefined; } /** @@ -821,4 +843,11 @@ export async function getWidgetManager( */ namespace Private { export const kernelWidgetManagers = new Map(); + export const widgetManagerProperty = new AttachedProperty< + DocumentRegistry.Context, + WidgetManager | undefined + >({ + name: 'widgetManager', + create: (owner: DocumentRegistry.Context): undefined => undefined, + }); } diff --git a/python/jupyterlab_widgets/src/plugin.ts b/python/jupyterlab_widgets/src/plugin.ts index c475e07436..932d564338 100644 --- a/python/jupyterlab_widgets/src/plugin.ts +++ b/python/jupyterlab_widgets/src/plugin.ts @@ -5,18 +5,9 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { - CodeConsole, - ConsolePanel, - IConsoleTracker, -} from '@jupyterlab/console'; +import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console'; -import { - INotebookModel, - INotebookTracker, - Notebook, - NotebookPanel, -} from '@jupyterlab/notebook'; +import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook'; import { JupyterFrontEnd, @@ -29,14 +20,8 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { ILoggerRegistry } from '@jupyterlab/logconsole'; -import { CodeCell } from '@jupyterlab/cells'; - -import { filter } from '@lumino/algorithm'; - import { DisposableDelegate } from '@lumino/disposable'; -import { AttachedProperty } from '@lumino/properties'; - import { WidgetRenderer } from './renderer'; import { @@ -62,113 +47,19 @@ import { ITranslator, nullTranslator } from '@jupyterlab/translation'; */ const SETTINGS: WidgetManager.Settings = { saveState: false }; -/** - * Iterate through all widget renderers in a notebook. - */ -function* notebookWidgetRenderers( - nb: Notebook -): Generator { - for (const cell of nb.widgets) { - if (cell.model.type === 'code') { - for (const codecell of (cell as CodeCell).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: CodeConsole -): Generator { - for (const cell of Array.from(console.cells)) { - if (cell.model.type === 'code') { - for (const codecell of (cell as unknown as CodeCell).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: JupyterFrontEnd, - path: string -): Generator { - const linkedViews = filter( - app.shell.widgets(), - (w) => w.id.startsWith('LinkedOutputView-') && (w as any).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: IterableIterator[] -): Generator { - for (const it of args) { - yield* it; - } -} - export function registerWidgetManager( - context: DocumentRegistry.IContext, - rendermime: IRenderMimeRegistry, - renderers: IterableIterator + context: DocumentRegistry.Context, + rendermime: IRenderMimeRegistry ): DisposableDelegate { - let wManager = Private.widgetManagerProperty.get(context); - if (!wManager) { - wManager = new WidgetManager(context, rendermime, SETTINGS, renderers); - Private.widgetManagerProperty.set(context, wManager); - } - return new DisposableDelegate(() => { - wManager!.dispose(); - }); + const wManager = new WidgetManager(context, rendermime, SETTINGS); + return new DisposableDelegate(() => wManager!.dispose()); } -function attachWidgetManagerToPanel( - panel: NotebookPanel | ConsolePanel, - app: JupyterFrontEnd -) { +function attachWidgetManager(panel: NotebookPanel | ConsolePanel) { if (panel instanceof NotebookPanel) { - registerWidgetManager( - panel.context, - panel.content.rendermime, - chain( - notebookWidgetRenderers(panel.content), - outputViews(app, panel.context.path) - ) - ); + new WidgetManager(panel.context, panel.content.rendermime, SETTINGS); } else if (panel instanceof ConsolePanel) { - // A bit of a hack to make this a 'context' - registerWidgetManager( - panel.console as any, - panel.console.rendermime, - chain(consoleWidgetRenderers(panel.console)) - ); + new WidgetManager(panel.console as any, panel.console.rendermime); } } @@ -236,9 +127,9 @@ function activateWidgetExtension( ); for (const tracker of [widgetTracker, consoleTracker]) { if (tracker !== null) { - tracker.forEach((panel) => attachWidgetManagerToPanel(panel, app)); + tracker.forEach((panel) => attachWidgetManager(panel)); tracker.widgetAdded.connect((sender, panel) => - attachWidgetManagerToPanel(panel, app) + attachWidgetManager(panel) ); } } @@ -361,15 +252,3 @@ export default [ controlWidgetsPlugin, outputWidgetPlugin, ]; -namespace Private { - /** - * A private attached property for a widget manager. - */ - export const widgetManagerProperty = new AttachedProperty< - DocumentRegistry.Context, - WidgetManager | undefined - >({ - name: 'widgetManager', - create: (owner: DocumentRegistry.Context): undefined => undefined, - }); -} diff --git a/python/jupyterlab_widgets/src/renderer.ts b/python/jupyterlab_widgets/src/renderer.ts index 6f0efbb731..621ccca470 100644 --- a/python/jupyterlab_widgets/src/renderer.ts +++ b/python/jupyterlab_widgets/src/renderer.ts @@ -41,9 +41,7 @@ export class WidgetRenderer super(); this.mimeType = options.mimeType; this._pendingManagerMessage = pendingManagerMessage; - if (manager) { - this.manager = manager; - } + this.manager = manager; } /** @@ -52,14 +50,14 @@ export class WidgetRenderer * Will accept the first non-null manager and ignore anything afterwards. */ - set manager(value: LabWidgetManager | null) { + set manager(value: LabWidgetManager | undefined) { if (value && !this._managerIsSet) { // Can only set the manager once this._manager.resolve(value); this._managerIsSet = true; - value.restored.connect(this._rerender, this); + value.restored.connect(this.rerender, this); this.disposed.connect(() => - value.restored.disconnect(this._rerender, this) + value.restored.disconnect(this.rerender, this) ); } } @@ -87,7 +85,7 @@ export class WidgetRenderer wModel = (await manager.get_model(source.model_id)) as DOMWidgetModel; } catch (err) { if (this._pendingManagerMessage === 'No kernel') { - this.node.textContent = 'Model not found in new kernel'; + this.node.textContent = `Model not found for this kernel: ${model.data['text/plain']}`; } else if (manager.restoredStatus) { // The manager has been restored, so this error won't be going away. this.node.textContent = 'Error displaying widget: model not found'; @@ -131,8 +129,9 @@ export class WidgetRenderer super.dispose(); } - private _rerender(): void { - if (this._rerenderMimeModel) { + rerender(): void { + // TODO: Add conditions for when re-rendering should occur. + if (this._rerenderMimeModel && !this.children.length) { // Clear the error message this.node.textContent = ''; this.removeClass('jupyter-widgets'); diff --git a/yarn.lock b/yarn.lock index 0558c955e8..da9009d3b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -987,7 +987,6 @@ __metadata: "@jupyter-widgets/output": ^6.0.8 "@jupyterlab/application": ^3.0.0 || ^4.0.0 "@jupyterlab/builder": ^3.0.0 || ^4.0.0 - "@jupyterlab/cells": ^3.0.0 || ^4.0.0 "@jupyterlab/console": ^3.0.0 || ^4.0.0 "@jupyterlab/docregistry": ^3.0.0 || ^4.0.0 "@jupyterlab/logconsole": ^3.0.0 || ^4.0.0 @@ -1001,7 +1000,6 @@ __metadata: "@jupyterlab/services": ^6.0.0 || ^7.0.0 "@jupyterlab/settingregistry": ^3.0.0 || ^4.0.0 "@jupyterlab/translation": ^3.0.0 || ^4.0.0 - "@lumino/algorithm": ^1.11.1 || ^2.0.0 "@lumino/coreutils": ^1.11.1 || ^2.1 "@lumino/disposable": ^1.10.1 || ^2.1 "@lumino/properties": ^2.0.1 @@ -1245,7 +1243,7 @@ __metadata: languageName: node linkType: hard -"@jupyterlab/cells@npm:^3.0.0 || ^4.0.0, @jupyterlab/cells@npm:^4.2.1": +"@jupyterlab/cells@npm:^4.2.1": version: 4.2.1 resolution: "@jupyterlab/cells@npm:4.2.1" dependencies: @@ -2756,13 +2754,6 @@ __metadata: languageName: node linkType: hard -"@lumino/algorithm@npm:^1.11.1 || ^2.0.0, @lumino/algorithm@npm:^2.0.1": - version: 2.0.1 - resolution: "@lumino/algorithm@npm:2.0.1" - checksum: cbf7fcf6ee6b785ea502cdfddc53d61f9d353dcb9659343511d5cd4b4030be2ff2ca4c08daec42f84417ab0318a3d9972a17319fa5231693e109ab112dcf8000 - languageName: node - linkType: hard - "@lumino/algorithm@npm:^1.9.1 || ^2.1, @lumino/algorithm@npm:^1.9.2": version: 1.9.2 resolution: "@lumino/algorithm@npm:1.9.2" @@ -2770,6 +2761,13 @@ __metadata: languageName: node linkType: hard +"@lumino/algorithm@npm:^2.0.1": + version: 2.0.1 + resolution: "@lumino/algorithm@npm:2.0.1" + checksum: cbf7fcf6ee6b785ea502cdfddc53d61f9d353dcb9659343511d5cd4b4030be2ff2ca4c08daec42f84417ab0318a3d9972a17319fa5231693e109ab112dcf8000 + languageName: node + linkType: hard + "@lumino/application@npm:^2.3.1": version: 2.3.1 resolution: "@lumino/application@npm:2.3.1"