diff --git a/app/index.template.js b/app/index.template.js index c80ed36c0c..523ea36d75 100644 --- a/app/index.template.js +++ b/app/index.template.js @@ -33,7 +33,9 @@ async function loadComponent(url, scope) { async function createModule(scope, module) { try { const factory = await window._JUPYTERLAB[scope].get(module); - return factory(); + const instance = factory(); + instance.__scope__ = scope; + return instance; } catch (e) { console.warn( `Failed to create module: package: ${scope}; module: ${module}` @@ -79,6 +81,7 @@ async function main() { // populate the list of disabled extensions const disabled = []; + const availablePlugins = []; /** * Iterate over active plugins in an extension. @@ -98,7 +101,18 @@ async function main() { let plugins = Array.isArray(exports) ? exports : [exports]; for (let plugin of plugins) { - if (PageConfig.Extension.isDisabled(plugin.id)) { + const isDisabled = PageConfig.Extension.isDisabled(plugin.id); + availablePlugins.push({ + id: plugin.id, + description: plugin.description, + requires: plugin.requires ?? [], + optional: plugin.optional ?? [], + provides: plugin.provides ?? null, + autoStart: plugin.autoStart, + enabled: !isDisabled, + extension: extension.__scope__ + }); + if (isDisabled) { disabled.push(plugin.id); continue; } @@ -200,7 +214,7 @@ async function main() { PageConfig.setOption('allPlugins', '{{{ json notebook_plugins }}}'); const NotebookApp = require('@jupyter-notebook/application').NotebookApp; - const app = new NotebookApp({ mimeExtensions }); + const app = new NotebookApp({ mimeExtensions, availablePlugins }); app.registerPluginModules(mods); diff --git a/app/package.json b/app/package.json index d0c76187e0..59865f8c09 100644 --- a/app/package.json +++ b/app/package.json @@ -163,6 +163,7 @@ "@jupyterlab/metadataform-extension": "^4.1.0-beta.0", "@jupyterlab/notebook-extension": "^4.1.0-beta.0", "@jupyterlab/pdf-extension": "^4.1.0-beta.0", + "@jupyterlab/pluginmanager-extension": "^4.1.0-beta.0", "@jupyterlab/running-extension": "^4.1.0-beta.0", "@jupyterlab/settingeditor": "^4.1.0-beta.0", "@jupyterlab/settingeditor-extension": "^4.1.0-beta.0", @@ -284,6 +285,7 @@ "@jupyterlab/notebook-extension:tracker", "@jupyterlab/notebook-extension:widget-factory" ], + "@jupyterlab/pluginmanager-extension": true, "@jupyterlab/shortcuts-extension": true, "@jupyterlab/terminal-extension": true, "@jupyterlab/theme-light-extension": true, diff --git a/packages/application-extension/schema/shell.json b/packages/application-extension/schema/shell.json index 7e0e44f992..00a67f0160 100644 --- a/packages/application-extension/schema/shell.json +++ b/packages/application-extension/schema/shell.json @@ -9,7 +9,8 @@ "title": "Customize shell widget positioning", "description": "Overrides default widget position in the application layout", "default": { - "Markdown Preview": { "area": "right" } + "Markdown Preview": { "area": "right" }, + "Plugins": { "area": "left" } } } }, diff --git a/packages/application/src/app.ts b/packages/application/src/app.ts index 45ec75774f..7d8818b4de 100644 --- a/packages/application/src/app.ts +++ b/packages/application/src/app.ts @@ -2,6 +2,7 @@ // Distributed under the terms of the Modified BSD License. import { + JupyterLab, JupyterFrontEnd, JupyterFrontEndPlugin, } from '@jupyterlab/application'; @@ -40,6 +41,17 @@ export class NotebookApp extends JupyterFrontEnd { } } + // Create an IInfo dictionary from the options to override the defaults. + const info = Object.keys(JupyterLab.defaultInfo).reduce((acc, val) => { + if (val in options) { + (acc as any)[val] = JSON.parse(JSON.stringify((options as any)[val])); + } + return acc; + }, {} as Partial); + + // Populate application info. + this._info = { ...JupyterLab.defaultInfo, ...info }; + this.restored = this.shell.restored; this.restored.then(() => this._formatter.invoke()); @@ -71,6 +83,13 @@ export class NotebookApp extends JupyterFrontEnd { readonly version = PageConfig.getOption('appVersion') ?? 'unknown'; + /** + * The NotebookApp application information dictionary. + */ + get info(): JupyterLab.IInfo { + return this._info; + } + /** * The JupyterLab application paths dictionary. */ @@ -149,6 +168,7 @@ export class NotebookApp extends JupyterFrontEnd { }); } + private _info: JupyterLab.IInfo = JupyterLab.defaultInfo; private _formatter = new Throttler(() => { Private.setFormat(this); }, 250); @@ -173,6 +193,11 @@ export namespace NotebookApp { * The mime renderer extensions. */ readonly mimeExtensions: IRenderMime.IExtensionModule[]; + + /** + * The information about available plugins. + */ + readonly availablePlugins: JupyterLab.IPluginInfo[]; } /** diff --git a/yarn.lock b/yarn.lock index 519a3b58ff..204b280de7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2165,6 +2165,7 @@ __metadata: "@jupyterlab/metadataform-extension": ^4.1.0-beta.0 "@jupyterlab/notebook-extension": ^4.1.0-beta.0 "@jupyterlab/pdf-extension": ^4.1.0-beta.0 + "@jupyterlab/pluginmanager-extension": ^4.1.0-beta.0 "@jupyterlab/running-extension": ^4.1.0-beta.0 "@jupyterlab/settingeditor": ^4.1.0-beta.0 "@jupyterlab/settingeditor-extension": ^4.1.0-beta.0 @@ -3898,6 +3899,20 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/pluginmanager-extension@npm:^4.1.0-beta.0": + version: 4.1.0-beta.0 + resolution: "@jupyterlab/pluginmanager-extension@npm:4.1.0-beta.0" + dependencies: + "@jupyterlab/application": ^4.1.0-beta.0 + "@jupyterlab/apputils": ^4.2.0-beta.0 + "@jupyterlab/pluginmanager": ^4.1.0-beta.0 + "@jupyterlab/translation": ^4.1.0-beta.0 + "@jupyterlab/ui-components": ^4.1.0-beta.0 + "@lumino/coreutils": ^2.1.2 + checksum: 70a2defa672b2e273d304846d5e7a0ca8f9b617787044fc65bc5332403f0d7d3f78ce9e2eee8d7b1d1085f806b084e2f12e3e1e6a9a83f0905e9a927982593e0 + languageName: node + linkType: hard + "@jupyterlab/pluginmanager@npm:^4.1.0-beta.0": version: 4.1.0-beta.0 resolution: "@jupyterlab/pluginmanager@npm:4.1.0-beta.0"