From ba8671ac0372c50e46d548fc4dd5e8a84e133044 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 9 Jun 2021 02:18:24 +0530 Subject: [PATCH 1/6] Add an 'interface switcher' to the toolbar Fixes https://github.com/jupyterlab/retrolab/issues/157 --- packages/notebook-extension/src/index.ts | 5 +- packages/notebook-extension/src/toolbar.tsx | 144 ++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 packages/notebook-extension/src/toolbar.tsx diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index 17913804..25861244 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -20,6 +20,8 @@ import { Poll } from '@lumino/polling'; import { Widget } from '@lumino/widgets'; +import { addNotebookToolbarItems } from './toolbar'; + /** * The class for kernel status errors. */ @@ -241,7 +243,8 @@ const shell: JupyterFrontEndPlugin = { const plugins: JupyterFrontEndPlugin[] = [ checkpoints, kernelLogo, - kernelStatus + kernelStatus, + addNotebookToolbarItems ]; export default plugins; diff --git a/packages/notebook-extension/src/toolbar.tsx b/packages/notebook-extension/src/toolbar.tsx new file mode 100644 index 00000000..96ac8434 --- /dev/null +++ b/packages/notebook-extension/src/toolbar.tsx @@ -0,0 +1,144 @@ +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; +import { ICommandPalette, ReactWidget } from '@jupyterlab/apputils'; +import { PageConfig } from '@jupyterlab/coreutils'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { IMainMenu } from '@jupyterlab/mainmenu'; +import { + INotebookModel, + INotebookTracker, + NotebookPanel +} from '@jupyterlab/notebook'; +import { HTMLSelect } from '@jupyterlab/ui-components'; +import { CommandRegistry } from '@lumino/commands'; +import { IDisposable } from '@lumino/disposable'; +import * as React from 'react'; + +/** + * Command IDs used by notebook toolbar items + */ +namespace CommandIDs { + /** + * Open current notebook in classic notebook + */ + export const openClassic = 'retro:open-classic'; + /** + * Open current notebook in JupyterLab + */ + export const openLab = 'retro:open-lab'; +} + +class InterfaceSwitcher extends ReactWidget { + constructor(private commands: CommandRegistry) { + super(); + this.addClass('jp-Notebook-toolbarCellType'); + } + + onChange = (event: React.ChangeEvent) => { + const target = event.target.value; + if (target === '-') { + return; + } + this.commands.execute(target); + }; + + render = () => { + return ( + + + + + + ); + }; +} + +/** + * A notebook widget extension that adds a open in classic notebook button to the toolbar. + */ +class InterfaceSwitcherButton + implements DocumentRegistry.IWidgetExtension { + constructor(commands: CommandRegistry) { + this._commands = commands; + } + + createNew(panel: NotebookPanel): IDisposable { + const switcher = new InterfaceSwitcher(this._commands); + panel.toolbar.insertBefore('kernelName', 'switch-interface', switcher); + return switcher; + } + + private _commands: CommandRegistry; +} + +/** + * A plugin to add custom toolbar items to the notebook page + */ +const notebookToolbarItems: JupyterFrontEndPlugin = { + id: '@retrolab/notebook-extension:open-classic', + autoStart: true, + optional: [INotebookTracker, ICommandPalette, IMainMenu], + activate: ( + app: JupyterFrontEnd, + notebookTracker: INotebookTracker | null, + palette: ICommandPalette | null, + menu: IMainMenu | null + ) => { + if (!notebookTracker) { + // to prevent showing the toolbar button in RetroLab + return; + } + + const { commands, docRegistry, shell } = app; + const baseUrl = PageConfig.getBaseUrl(); + + const isEnabled = () => { + return ( + notebookTracker.currentWidget !== null && + notebookTracker.currentWidget === shell.currentWidget + ); + }; + + commands.addCommand(CommandIDs.openClassic, { + label: 'Open in Classic Notebook', + execute: () => { + const current = notebookTracker.currentWidget; + if (!current) { + return; + } + window.open(`${baseUrl}tree/${current.context.path}`); + }, + isEnabled + }); + + commands.addCommand(CommandIDs.openLab, { + label: 'Open in JupyterLab', + execute: () => { + const current = notebookTracker.currentWidget; + if (!current) { + return; + } + window.open(`${baseUrl}lab/tree/${current.context.path}`); + }, + isEnabled + }); + + if (palette) { + palette.addItem({ command: CommandIDs.openClassic, category: 'Other' }); + } + + if (menu) { + menu.viewMenu.addGroup([{ command: CommandIDs.openClassic }], 1); + } + + const interfaceSwitcher = new InterfaceSwitcherButton(commands); + docRegistry.addWidgetExtension('Notebook', interfaceSwitcher); + } +}; + +export { notebookToolbarItems as addNotebookToolbarItems }; From f8dd65779f5f1be765a08787a9f286ed449e5c8c Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 11 Jun 2021 00:45:16 +0530 Subject: [PATCH 2/6] Try moving interface switching to JupyterLab extension Works on retrolab but can't get it to work on JupyterLab --- app/index.js | 3 + packages/lab-extension/src/index.ts | 115 +----------------- .../src/interfaceswitcher.tsx} | 8 +- packages/notebook-extension/src/index.ts | 5 +- 4 files changed, 14 insertions(+), 117 deletions(-) rename packages/{notebook-extension/src/toolbar.tsx => lab-extension/src/interfaceswitcher.tsx} (94%) diff --git a/app/index.js b/app/index.js index 14a70820..f5336e96 100644 --- a/app/index.js +++ b/app/index.js @@ -88,6 +88,9 @@ async function main() { require('@retrolab/docmanager-extension'), require('@retrolab/help-extension'), require('@retrolab/notebook-extension'), + require('@retrolab/lab-extension').default.filter(({ id }) => + ['@retrolab/lab-extension:interface-switcher'].includes(id) + ), // to handle opening new tabs after creating a new terminal require('@retrolab/terminal-extension'), diff --git a/packages/lab-extension/src/index.ts b/packages/lab-extension/src/index.ts index 5d5cf53f..a70108cb 100644 --- a/packages/lab-extension/src/index.ts +++ b/packages/lab-extension/src/index.ts @@ -2,32 +2,14 @@ // Distributed under the terms of the Modified BSD License. import { - ILabShell, JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; - -import { ICommandPalette, ToolbarButton } from '@jupyterlab/apputils'; - +import { ICommandPalette } from '@jupyterlab/apputils'; import { PageConfig } from '@jupyterlab/coreutils'; - -import { DocumentRegistry } from '@jupyterlab/docregistry'; - import { IMainMenu } from '@jupyterlab/mainmenu'; - import { ITranslator } from '@jupyterlab/translation'; - -import { - INotebookModel, - INotebookTracker, - NotebookPanel -} from '@jupyterlab/notebook'; - -import { retroSunIcon } from '@retrolab/ui-components'; - -import { CommandRegistry } from '@lumino/commands'; - -import { IDisposable } from '@lumino/disposable'; +import { interfaceSwitcher } from './interfaceswitcher'; /** * The command IDs used by the application plugin. @@ -36,97 +18,9 @@ namespace CommandIDs { /** * Toggle Top Bar visibility */ - export const openRetro = 'retrolab:open'; export const launchRetroTree = 'retrolab:launchtree'; } -/** - * A notebook widget extension that adds a retrolab button to the toolbar. - */ -class RetroButton - implements DocumentRegistry.IWidgetExtension { - /** - * Instantiate a new RetroButton. - * @param commands The command registry. - */ - constructor(commands: CommandRegistry) { - this._commands = commands; - } - - /** - * Create a new extension object. - */ - createNew(panel: NotebookPanel): IDisposable { - const button = new ToolbarButton({ - tooltip: 'Open with RetroLab', - icon: retroSunIcon, - onClick: () => { - this._commands.execute(CommandIDs.openRetro); - } - }); - panel.toolbar.insertAfter('cellType', 'retro', button); - return button; - } - - private _commands: CommandRegistry; -} - -/** - * A plugin for the checkpoint indicator - */ -const openRetro: JupyterFrontEndPlugin = { - id: '@retrolab/lab-extension:open-retro', - autoStart: true, - optional: [INotebookTracker, ICommandPalette, IMainMenu, ILabShell], - activate: ( - app: JupyterFrontEnd, - notebookTracker: INotebookTracker | null, - palette: ICommandPalette | null, - menu: IMainMenu | null, - labShell: ILabShell | null - ) => { - // TODO: do not activate if already in a IRetroShell? - if (!notebookTracker || !labShell) { - // to prevent showing the toolbar button in RetroLab - return; - } - - const { commands, docRegistry, shell } = app; - const baseUrl = PageConfig.getBaseUrl(); - - const isEnabled = () => { - return ( - notebookTracker.currentWidget !== null && - notebookTracker.currentWidget === shell.currentWidget - ); - }; - - commands.addCommand(CommandIDs.openRetro, { - label: 'Open in RetroLab', - execute: () => { - const current = notebookTracker.currentWidget; - if (!current) { - return; - } - const { context } = current; - window.open(`${baseUrl}retro/notebooks/${context.path}`); - }, - isEnabled - }); - - if (palette) { - palette.addItem({ command: CommandIDs.openRetro, category: 'Other' }); - } - - if (menu) { - menu.viewMenu.addGroup([{ command: CommandIDs.openRetro }], 1); - } - - const retroButton = new RetroButton(commands); - docRegistry.addWidgetExtension('Notebook', retroButton); - } -}; - /** * A plugin to add a command to open the RetroLab Tree. */ @@ -166,6 +60,9 @@ const launchRetroTree: JupyterFrontEndPlugin = { /** * Export the plugins as default. */ -const plugins: JupyterFrontEndPlugin[] = [launchRetroTree, openRetro]; +const plugins: JupyterFrontEndPlugin[] = [ + launchRetroTree, + interfaceSwitcher +]; export default plugins; diff --git a/packages/notebook-extension/src/toolbar.tsx b/packages/lab-extension/src/interfaceswitcher.tsx similarity index 94% rename from packages/notebook-extension/src/toolbar.tsx rename to packages/lab-extension/src/interfaceswitcher.tsx index 96ac8434..2d5cb73d 100644 --- a/packages/notebook-extension/src/toolbar.tsx +++ b/packages/lab-extension/src/interfaceswitcher.tsx @@ -50,7 +50,7 @@ class InterfaceSwitcher extends ReactWidget { onChange={this.onChange} className="jp-Notebook-toolbarCellTypeDropdown" > - + @@ -79,8 +79,8 @@ class InterfaceSwitcherButton /** * A plugin to add custom toolbar items to the notebook page */ -const notebookToolbarItems: JupyterFrontEndPlugin = { - id: '@retrolab/notebook-extension:open-classic', +const interfaceSwitcher: JupyterFrontEndPlugin = { + id: '@retrolab/lab-extension:interface-switcher', autoStart: true, optional: [INotebookTracker, ICommandPalette, IMainMenu], activate: ( @@ -141,4 +141,4 @@ const notebookToolbarItems: JupyterFrontEndPlugin = { } }; -export { notebookToolbarItems as addNotebookToolbarItems }; +export { interfaceSwitcher }; diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index 25861244..17913804 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -20,8 +20,6 @@ import { Poll } from '@lumino/polling'; import { Widget } from '@lumino/widgets'; -import { addNotebookToolbarItems } from './toolbar'; - /** * The class for kernel status errors. */ @@ -243,8 +241,7 @@ const shell: JupyterFrontEndPlugin = { const plugins: JupyterFrontEndPlugin[] = [ checkpoints, kernelLogo, - kernelStatus, - addNotebookToolbarItems + kernelStatus ]; export default plugins; From 135b4977b2c207c5a0d0ffc37d2c424ecd93c4d1 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 11 Jun 2021 03:13:52 +0530 Subject: [PATCH 3/6] Refactor interface chooser code to not suck --- .../lab-extension/src/interfaceswitcher.tsx | 147 ++++++++++-------- 1 file changed, 85 insertions(+), 62 deletions(-) diff --git a/packages/lab-extension/src/interfaceswitcher.tsx b/packages/lab-extension/src/interfaceswitcher.tsx index 2d5cb73d..4207ab43 100644 --- a/packages/lab-extension/src/interfaceswitcher.tsx +++ b/packages/lab-extension/src/interfaceswitcher.tsx @@ -16,43 +16,42 @@ import { CommandRegistry } from '@lumino/commands'; import { IDisposable } from '@lumino/disposable'; import * as React from 'react'; -/** - * Command IDs used by notebook toolbar items - */ -namespace CommandIDs { - /** - * Open current notebook in classic notebook - */ - export const openClassic = 'retro:open-classic'; - /** - * Open current notebook in JupyterLab - */ - export const openLab = 'retro:open-lab'; +interface ISwitcherChoice { + command: string; + dropdownLabel: string; + commandLabel: string; + urlPrefix: string; + current: boolean; } class InterfaceSwitcher extends ReactWidget { - constructor(private commands: CommandRegistry) { + private switcherChoices: ISwitcherChoice[]; + constructor( + private commands: CommandRegistry, + switcherChoices: ISwitcherChoice[] + ) { super(); this.addClass('jp-Notebook-toolbarCellType'); + this.switcherChoices = switcherChoices; } - onChange = (event: React.ChangeEvent) => { - const target = event.target.value; - if (target === '-') { - return; - } - this.commands.execute(target); - }; - render = () => { return ( sc.current)?.command} > - - - + {this.switcherChoices.map(sc => { + return ( + + ); + })} ); }; @@ -63,12 +62,18 @@ class InterfaceSwitcher extends ReactWidget { */ class InterfaceSwitcherButton implements DocumentRegistry.IWidgetExtension { - constructor(commands: CommandRegistry) { + private switcherChoices: ISwitcherChoice[]; + + constructor(commands: CommandRegistry, switcherChoices: ISwitcherChoice[]) { this._commands = commands; + this.switcherChoices = switcherChoices; } createNew(panel: NotebookPanel): IDisposable { - const switcher = new InterfaceSwitcher(this._commands); + const switcher = new InterfaceSwitcher( + this._commands, + this.switcherChoices + ); panel.toolbar.insertBefore('kernelName', 'switch-interface', switcher); return switcher; } @@ -97,46 +102,64 @@ const interfaceSwitcher: JupyterFrontEndPlugin = { const { commands, docRegistry, shell } = app; const baseUrl = PageConfig.getBaseUrl(); - const isEnabled = () => { - return ( - notebookTracker.currentWidget !== null && - notebookTracker.currentWidget === shell.currentWidget - ); - }; - - commands.addCommand(CommandIDs.openClassic, { - label: 'Open in Classic Notebook', - execute: () => { - const current = notebookTracker.currentWidget; - if (!current) { - return; - } - window.open(`${baseUrl}tree/${current.context.path}`); + const switcherChoices: ISwitcherChoice[] = [ + { + command: 'retrolab:open-classic', + commandLabel: 'Open in Classic Notebook', + dropdownLabel: 'Classic', + urlPrefix: `${baseUrl}tree/`, + current: false }, - isEnabled - }); - - commands.addCommand(CommandIDs.openLab, { - label: 'Open in JupyterLab', - execute: () => { - const current = notebookTracker.currentWidget; - if (!current) { - return; - } - window.open(`${baseUrl}lab/tree/${current.context.path}`); + { + command: 'retrolab:open-retro', + commandLabel: 'Open in RetroLab', + dropdownLabel: 'RetroLab', + urlPrefix: `${baseUrl}retro/tree/`, + current: app.name === 'RetroLab' }, - isEnabled - }); + { + command: 'retrolab:open-lab', + commandLabel: 'Open in JupyterLab', + dropdownLabel: 'JupyterLab', + urlPrefix: `${baseUrl}lab/tree/`, + current: app.name === 'JupyterLab' + } + ]; + + const addInterface = (option: ISwitcherChoice) => { + commands.addCommand(option.command, { + label: option.commandLabel, + execute: () => { + const current = notebookTracker.currentWidget; + if (!current) { + return; + } + window.location.href = `${option.urlPrefix}${current.context.path}`; + }, + isEnabled: () => { + return ( + notebookTracker.currentWidget !== null && + notebookTracker.currentWidget === shell.currentWidget && + !option.current + ); + } + }); - if (palette) { - palette.addItem({ command: CommandIDs.openClassic, category: 'Other' }); - } + if (palette) { + palette.addItem({ command: option.command, category: 'Other' }); + } - if (menu) { - menu.viewMenu.addGroup([{ command: CommandIDs.openClassic }], 1); - } + if (menu) { + menu.viewMenu.addGroup([{ command: option.command }], 1); + } + }; - const interfaceSwitcher = new InterfaceSwitcherButton(commands); + switcherChoices.map(iface => addInterface(iface)); + + const interfaceSwitcher = new InterfaceSwitcherButton( + commands, + switcherChoices + ); docRegistry.addWidgetExtension('Notebook', interfaceSwitcher); } }; From f66e3b8818a125fefd9b35f2dbc46deb6ec615ef Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 15 Jun 2021 03:11:01 +0530 Subject: [PATCH 4/6] Don't use app.name to find current shell --- packages/lab-extension/src/interfaceswitcher.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/lab-extension/src/interfaceswitcher.tsx b/packages/lab-extension/src/interfaceswitcher.tsx index 4207ab43..4fb1b0bf 100644 --- a/packages/lab-extension/src/interfaceswitcher.tsx +++ b/packages/lab-extension/src/interfaceswitcher.tsx @@ -14,6 +14,7 @@ import { import { HTMLSelect } from '@jupyterlab/ui-components'; import { CommandRegistry } from '@lumino/commands'; import { IDisposable } from '@lumino/disposable'; +import { IRetroShell } from '@retrolab/application'; import * as React from 'react'; interface ISwitcherChoice { @@ -87,12 +88,13 @@ class InterfaceSwitcherButton const interfaceSwitcher: JupyterFrontEndPlugin = { id: '@retrolab/lab-extension:interface-switcher', autoStart: true, - optional: [INotebookTracker, ICommandPalette, IMainMenu], + optional: [INotebookTracker, ICommandPalette, IMainMenu, IRetroShell], activate: ( app: JupyterFrontEnd, notebookTracker: INotebookTracker | null, palette: ICommandPalette | null, - menu: IMainMenu | null + menu: IMainMenu | null, + retroShell: IRetroShell | null ) => { if (!notebookTracker) { // to prevent showing the toolbar button in RetroLab @@ -115,14 +117,16 @@ const interfaceSwitcher: JupyterFrontEndPlugin = { commandLabel: 'Open in RetroLab', dropdownLabel: 'RetroLab', urlPrefix: `${baseUrl}retro/tree/`, - current: app.name === 'RetroLab' + current: retroShell !== null }, { command: 'retrolab:open-lab', commandLabel: 'Open in JupyterLab', dropdownLabel: 'JupyterLab', urlPrefix: `${baseUrl}lab/tree/`, - current: app.name === 'JupyterLab' + // If we aren't in retroShell, assume we're in JupyterLab + // So any new Lab based UIs will fallback to behaving like JupyterLab + current: retroShell === null } ]; From a396892eb8d014b1b797914ff8b15e62a95b0cc6 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 15 Jun 2021 03:22:47 +0530 Subject: [PATCH 5/6] Don't explicitly include labextension in retrolab It's automatically loaded by retrolab, since it's a labextension! --- app/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/index.js b/app/index.js index f5336e96..14a70820 100644 --- a/app/index.js +++ b/app/index.js @@ -88,9 +88,6 @@ async function main() { require('@retrolab/docmanager-extension'), require('@retrolab/help-extension'), require('@retrolab/notebook-extension'), - require('@retrolab/lab-extension').default.filter(({ id }) => - ['@retrolab/lab-extension:interface-switcher'].includes(id) - ), // to handle opening new tabs after creating a new terminal require('@retrolab/terminal-extension'), From 3f49eea5519282f289df00b590a6514b56bd6ff0 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Fri, 3 Sep 2021 12:14:42 +0200 Subject: [PATCH 6/6] Add buttons to switch to classic lab and retro --- packages/lab-extension/package.json | 3 + packages/lab-extension/src/index.ts | 160 +++++++++++++++- .../lab-extension/src/interfaceswitcher.tsx | 171 ------------------ packages/lab-extension/tsconfig.json | 3 + 4 files changed, 158 insertions(+), 179 deletions(-) delete mode 100644 packages/lab-extension/src/interfaceswitcher.tsx diff --git a/packages/lab-extension/package.json b/packages/lab-extension/package.json index b2d0043c..21b79387 100644 --- a/packages/lab-extension/package.json +++ b/packages/lab-extension/package.json @@ -50,8 +50,11 @@ "@jupyterlab/docregistry": "^3.1.8", "@jupyterlab/mainmenu": "^3.1.8", "@jupyterlab/notebook": "^3.1.8", + "@jupyterlab/translation": "^3.1.8", + "@jupyterlab/ui-components": "^3.1.8", "@lumino/commands": "^1.15.0", "@lumino/disposable": "^1.7.0", + "@retrolab/application": "^0.3.3", "@retrolab/ui-components": "^0.3.3" }, "devDependencies": { diff --git a/packages/lab-extension/src/index.ts b/packages/lab-extension/src/index.ts index a70108cb..189f6f40 100644 --- a/packages/lab-extension/src/index.ts +++ b/packages/lab-extension/src/index.ts @@ -2,25 +2,172 @@ // Distributed under the terms of the Modified BSD License. import { + ILabShell, JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; -import { ICommandPalette } from '@jupyterlab/apputils'; + +import { CommandToolbarButton, ICommandPalette } from '@jupyterlab/apputils'; + import { PageConfig } from '@jupyterlab/coreutils'; + import { IMainMenu } from '@jupyterlab/mainmenu'; + +import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook'; + import { ITranslator } from '@jupyterlab/translation'; -import { interfaceSwitcher } from './interfaceswitcher'; + +import { + jupyterIcon, + jupyterFaviconIcon, + LabIcon +} from '@jupyterlab/ui-components'; + +import { IRetroShell } from '@retrolab/application'; + +import { retroSunIcon } from '@retrolab/ui-components'; /** * The command IDs used by the application plugin. */ namespace CommandIDs { /** - * Toggle Top Bar visibility + * Launch RetroLab Tree + */ + export const launchRetroTree = 'retrolab:launch-tree'; + + /** + * Open RetroLab + */ + export const openRetro = 'retrolab:open-retro'; + + /** + * Open in Classic Notebook */ - export const launchRetroTree = 'retrolab:launchtree'; + export const openClassic = 'retrolab:open-classic'; + + /** + * Open in JupyterLab + */ + export const openLab = 'retrolab:open-lab'; +} + +interface ISwitcherChoice { + command: string; + commandLabel: string; + buttonLabel: string; + icon: LabIcon; + urlPrefix: string; } +/** + * A plugin to add custom toolbar items to the notebook page + */ +const launchButtons: JupyterFrontEndPlugin = { + id: '@retrolab/lab-extension:interface-switcher', + autoStart: true, + optional: [ + INotebookTracker, + ICommandPalette, + IMainMenu, + IRetroShell, + ILabShell + ], + activate: ( + app: JupyterFrontEnd, + notebookTracker: INotebookTracker | null, + palette: ICommandPalette | null, + menu: IMainMenu | null, + retroShell: IRetroShell | null, + labShell: ILabShell | null + ) => { + if (!notebookTracker) { + // to prevent showing the toolbar button in RetroLab + return; + } + + const { commands, shell } = app; + const baseUrl = PageConfig.getBaseUrl(); + + const isEnabled = () => { + return ( + notebookTracker.currentWidget !== null && + notebookTracker.currentWidget === shell.currentWidget + ); + }; + + const addInterface = (option: ISwitcherChoice) => { + const { command, icon, buttonLabel, commandLabel, urlPrefix } = option; + commands.addCommand(command, { + label: args => (args.noLabel ? '' : commandLabel), + caption: commandLabel, + icon, + execute: () => { + const current = notebookTracker.currentWidget; + if (!current) { + return; + } + window.open(`${urlPrefix}${current.context.path}`); + }, + isEnabled + }); + + if (palette) { + palette.addItem({ command, category: 'Other' }); + } + + if (menu) { + menu.viewMenu.addGroup([{ command }], 1); + } + + notebookTracker.widgetAdded.connect( + async (sender: INotebookTracker, panel: NotebookPanel) => { + panel.toolbar.insertBefore( + 'kernelName', + buttonLabel, + new CommandToolbarButton({ + commands, + id: command, + args: { noLabel: 1 } + }) + ); + await panel.context.ready; + commands.notifyCommandChanged(); + } + ); + }; + + // always add Classic + addInterface({ + command: 'retrolab:open-classic', + commandLabel: 'Open in Classic Notebook', + buttonLabel: 'openClassic', + icon: jupyterIcon, + urlPrefix: `${baseUrl}tree/` + }); + + if (!retroShell) { + addInterface({ + command: 'retrolab:open-retro', + commandLabel: 'Open in RetroLab', + buttonLabel: 'openRetro', + icon: retroSunIcon, + urlPrefix: `${baseUrl}retro/tree/` + }); + } + + if (!labShell) { + addInterface({ + command: 'retrolab:open-lab', + commandLabel: 'Open in JupyterLab', + buttonLabel: 'openLab', + icon: jupyterFaviconIcon, + urlPrefix: `${baseUrl}doc/tree/` + }); + } + } +}; + /** * A plugin to add a command to open the RetroLab Tree. */ @@ -60,9 +207,6 @@ const launchRetroTree: JupyterFrontEndPlugin = { /** * Export the plugins as default. */ -const plugins: JupyterFrontEndPlugin[] = [ - launchRetroTree, - interfaceSwitcher -]; +const plugins: JupyterFrontEndPlugin[] = [launchRetroTree, launchButtons]; export default plugins; diff --git a/packages/lab-extension/src/interfaceswitcher.tsx b/packages/lab-extension/src/interfaceswitcher.tsx deleted file mode 100644 index 4fb1b0bf..00000000 --- a/packages/lab-extension/src/interfaceswitcher.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { - JupyterFrontEnd, - JupyterFrontEndPlugin -} from '@jupyterlab/application'; -import { ICommandPalette, ReactWidget } from '@jupyterlab/apputils'; -import { PageConfig } from '@jupyterlab/coreutils'; -import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { IMainMenu } from '@jupyterlab/mainmenu'; -import { - INotebookModel, - INotebookTracker, - NotebookPanel -} from '@jupyterlab/notebook'; -import { HTMLSelect } from '@jupyterlab/ui-components'; -import { CommandRegistry } from '@lumino/commands'; -import { IDisposable } from '@lumino/disposable'; -import { IRetroShell } from '@retrolab/application'; -import * as React from 'react'; - -interface ISwitcherChoice { - command: string; - dropdownLabel: string; - commandLabel: string; - urlPrefix: string; - current: boolean; -} - -class InterfaceSwitcher extends ReactWidget { - private switcherChoices: ISwitcherChoice[]; - constructor( - private commands: CommandRegistry, - switcherChoices: ISwitcherChoice[] - ) { - super(); - this.addClass('jp-Notebook-toolbarCellType'); - this.switcherChoices = switcherChoices; - } - - render = () => { - return ( - sc.current)?.command} - > - {this.switcherChoices.map(sc => { - return ( - - ); - })} - - ); - }; -} - -/** - * A notebook widget extension that adds a open in classic notebook button to the toolbar. - */ -class InterfaceSwitcherButton - implements DocumentRegistry.IWidgetExtension { - private switcherChoices: ISwitcherChoice[]; - - constructor(commands: CommandRegistry, switcherChoices: ISwitcherChoice[]) { - this._commands = commands; - this.switcherChoices = switcherChoices; - } - - createNew(panel: NotebookPanel): IDisposable { - const switcher = new InterfaceSwitcher( - this._commands, - this.switcherChoices - ); - panel.toolbar.insertBefore('kernelName', 'switch-interface', switcher); - return switcher; - } - - private _commands: CommandRegistry; -} - -/** - * A plugin to add custom toolbar items to the notebook page - */ -const interfaceSwitcher: JupyterFrontEndPlugin = { - id: '@retrolab/lab-extension:interface-switcher', - autoStart: true, - optional: [INotebookTracker, ICommandPalette, IMainMenu, IRetroShell], - activate: ( - app: JupyterFrontEnd, - notebookTracker: INotebookTracker | null, - palette: ICommandPalette | null, - menu: IMainMenu | null, - retroShell: IRetroShell | null - ) => { - if (!notebookTracker) { - // to prevent showing the toolbar button in RetroLab - return; - } - - const { commands, docRegistry, shell } = app; - const baseUrl = PageConfig.getBaseUrl(); - - const switcherChoices: ISwitcherChoice[] = [ - { - command: 'retrolab:open-classic', - commandLabel: 'Open in Classic Notebook', - dropdownLabel: 'Classic', - urlPrefix: `${baseUrl}tree/`, - current: false - }, - { - command: 'retrolab:open-retro', - commandLabel: 'Open in RetroLab', - dropdownLabel: 'RetroLab', - urlPrefix: `${baseUrl}retro/tree/`, - current: retroShell !== null - }, - { - command: 'retrolab:open-lab', - commandLabel: 'Open in JupyterLab', - dropdownLabel: 'JupyterLab', - urlPrefix: `${baseUrl}lab/tree/`, - // If we aren't in retroShell, assume we're in JupyterLab - // So any new Lab based UIs will fallback to behaving like JupyterLab - current: retroShell === null - } - ]; - - const addInterface = (option: ISwitcherChoice) => { - commands.addCommand(option.command, { - label: option.commandLabel, - execute: () => { - const current = notebookTracker.currentWidget; - if (!current) { - return; - } - window.location.href = `${option.urlPrefix}${current.context.path}`; - }, - isEnabled: () => { - return ( - notebookTracker.currentWidget !== null && - notebookTracker.currentWidget === shell.currentWidget && - !option.current - ); - } - }); - - if (palette) { - palette.addItem({ command: option.command, category: 'Other' }); - } - - if (menu) { - menu.viewMenu.addGroup([{ command: option.command }], 1); - } - }; - - switcherChoices.map(iface => addInterface(iface)); - - const interfaceSwitcher = new InterfaceSwitcherButton( - commands, - switcherChoices - ); - docRegistry.addWidgetExtension('Notebook', interfaceSwitcher); - } -}; - -export { interfaceSwitcher }; diff --git a/packages/lab-extension/tsconfig.json b/packages/lab-extension/tsconfig.json index 1b9e4bfd..34d11b40 100644 --- a/packages/lab-extension/tsconfig.json +++ b/packages/lab-extension/tsconfig.json @@ -6,6 +6,9 @@ }, "include": ["src/**/*"], "references": [ + { + "path": "../application" + }, { "path": "../ui-components" }