@jupyterlab/application#JupyterFrontEndPlugin TypeScript Examples

The following examples show how to use @jupyterlab/application#JupyterFrontEndPlugin. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: jupyterlab-plugin.ts    From beakerx_tabledisplay with Apache License 2.0 6 votes vote down vote up
plugin: JupyterFrontEndPlugin<void> = {
  id: 'beakerx_tabledisplay:plugin',
  requires: [IJupyterWidgetRegistry],
  optional: [IThemeManager],
  autoStart: true,
  activate: (app: JupyterFrontEnd, widgets: IJupyterWidgetRegistry, themeManager: IThemeManager | null): void => {
    class TableDisplayView extends beakerx_tabledisplay.TableDisplayView {
      constructor(options?: WidgetView.InitializeParameters) {
        super(options);
        
        if (themeManager) {
          themeManager.themeChanged.connect(this._onThemeChanged, this);
        }
      }
    
      protected _onThemeChanged(): void {
        // Recompute CSS variables and redraw
        this.currentScope.computeCSSVariablesAndRedraw();
      }
    
      public remove(): void {
        if (themeManager) {
          themeManager.themeChanged.disconnect(this._onThemeChanged, this);
        }
        return super.remove();
      }
    }

    widgets.registerWidget({
      name: 'beakerx_tabledisplay',
      version: BEAKERX_MODULE_VERSION,
      exports: {
        TableDisplayModel: beakerx_tabledisplay.TableDisplayModel,
        TableDisplayView
      }
    });
    
  }
}
Example #2
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
ros: JupyterFrontEndPlugin<any>[] = [
  bag,
  launch,
  logConsole,
  master,
  menu,
  settings,
  zethus
]
Example #3
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
master: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-ros/master',
  autoStart: true,
  requires: [IStatusBar],
  optional: [],
  activate: (app: JupyterFrontEnd, statusBar: IStatusBar) => {
    
    if (!statusBar) { console.log("No status bar!"); return; }
    
    statusBar.registerStatusItem('jupyterlab-ros/master:status', {
      item: new StatusMaster(),
      align: 'left',
      rank: 4,
      isActive: () => true
    });
  }
}
Example #4
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
menu: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-ros/menu',
  autoStart: true,
  requires: [IMainMenu],
  optional: [],
  activate: (app: JupyterFrontEnd, mainMenu: IMainMenu) => {
    
    const { commands } = app;

    // Create a new menu
    const menu: Menu = new Menu({ commands });
    menu.title.label = 'ROS';
    mainMenu.addMenu(menu, { rank: 80 });
    
    // Open Zethus
    menu.addItem({ command: 'jupyterlab-ros/zethus:open' });
    menu.addItem({ type: 'separator' });

    // Open Logger
    menu.addItem({ command: 'jupyterlab-ros/logConsole:open' });
    menu.addItem({ type: 'separator' });

    // Open Settings
    menu.addItem({ command: 'jupyterlab-ros/settings:open' });
  }
}
Example #5
Source File: index.ts    From jupyterlab-execute-time with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
extension: JupyterFrontEndPlugin<void> = {
  id: PLUGIN_NAME,
  autoStart: true,
  requires: [INotebookTracker, ISettingRegistry],
  activate: (
    app: JupyterFrontEnd,
    tracker: INotebookTracker,
    settingRegistry: ISettingRegistry
  ) => {
    app.docRegistry.addWidgetExtension(
      'Notebook',
      new ExecuteTimeWidgetExtension(tracker, settingRegistry)
    );

    // eslint-disable-next-line no-console
    console.log('JupyterLab extension jupyterlab-execute-time is activated!');
  },
}
Example #6
Source File: index.ts    From jupyterlab-tabular-data-editor with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
extension: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-tabular-data-editor',
  autoStart: true,
  activate: activateCsv,
  requires: [IFileBrowserFactory],
  optional: [
    ILauncher,
    ILayoutRestorer,
    IThemeManager,
    IMainMenu,
    ISearchProviderRegistry
  ]
}
Example #7
Source File: index.ts    From jupyter-extensions with Apache License 2.0 5 votes vote down vote up
GitSyncPlugin: JupyterFrontEndPlugin<void> = {
  id: 'gitsync:gitsync',
  requires: [ILabShell, IDocumentManager],
  activate: activate,
  autoStart: true,
}
Example #8
Source File: index.ts    From jupyterlab-unfold with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
extension: JupyterFrontEndPlugin<IFileBrowserFactory> = {
  id: EXTENSION_ID,
  provides: IFileBrowserFactory,
  requires: [IDocumentManager, ITranslator, ISettingRegistry],
  optional: [IStateDB],
  activate: async (
    app: JupyterFrontEnd,
    docManager: IDocumentManager,
    translator: ITranslator,
    settings: ISettingRegistry,
    state: IStateDB | null
  ): Promise<IFileBrowserFactory> => {
    const setting = await settings.load(SETTINGS_ID);

    const tracker = new WidgetTracker<FileTreeBrowser>({ namespace });
    const createFileBrowser = (
      id: string,
      options: IFileBrowserFactory.IOptions = {}
    ) => {
      const model = new FilterFileTreeBrowserModel({
        translator: translator,
        auto: options.auto ?? true,
        manager: docManager,
        driveName: options.driveName || '',
        refreshInterval: options.refreshInterval,
        state:
          options.state === null
            ? undefined
            : options.state || state || undefined
      });
      const widget = new FileTreeBrowser({
        id,
        model,
        restore: true,
        translator,
        app
      });

      widget.listing.singleClickToUnfold = setting.get('singleClickToUnfold')
        .composite as boolean;

      setting.changed.connect(() => {
        widget.listing.singleClickToUnfold = setting.get('singleClickToUnfold')
          .composite as boolean;
      });

      // Track the newly created file browser.
      void tracker.add(widget);

      return widget;
    };

    // Manually restore and load the default file browser.
    const defaultBrowser = createFileBrowser(EXTENSION_ID, {
      auto: false,
      restore: false
    });

    // TODO Remove this! Why is this needed?
    // The @jupyterlab/filebrowser-extension:launcher-toolbar-button extension should take care of this
    const { commands } = app;
    const trans = translator.load('jupyterlab');

    // Add a launcher toolbar item.
    const launcher = new ToolbarButton({
      icon: addIcon,
      onClick: () => {
        if (commands.hasCommand('launcher:create')) {
          return commands.execute('launcher:create');
        }
      },
      tooltip: trans.__('New Launcher'),
      actualOnClick: true
    });
    defaultBrowser.toolbar.insertItem(0, 'launch', launcher);

    return { createFileBrowser, defaultBrowser, tracker };
  }
}
Example #9
Source File: plugin.tsx    From jupyterlab-tour with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
defaultsPlugin: JupyterFrontEndPlugin<void> = {
  id: DEFAULTS_PLUGIN_ID,
  autoStart: true,
  activate: activateDefaults,
  requires: [ITourManager],
  optional: [INotebookTracker]
}
Example #10
Source File: plugin.tsx    From jupyterlab-tour with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
userPlugin: JupyterFrontEndPlugin<IUserTourManager> = {
  id: USER_PLUGIN_ID,
  autoStart: true,
  activate: activateUser,
  requires: [ISettingRegistry, ITourManager],
  optional: [ITranslator],
  provides: IUserTourManager
}
Example #11
Source File: plugin.tsx    From jupyterlab-tour with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
corePlugin: JupyterFrontEndPlugin<ITourManager> = {
  id: PLUGIN_ID,
  autoStart: true,
  activate,
  requires: [IStateDB],
  optional: [ICommandPalette, IMainMenu, ITranslator],
  provides: ITourManager
}
Example #12
Source File: plugin.ts    From jupyter-videochat with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
areaTogglePlugin: JupyterFrontEndPlugin<void> = {
  id: `${NS}:toggle-area`,
  autoStart: true,
  requires: [IVideoChatManager],
  optional: [ICommandPalette],
  activate: activateToggleArea,
}
Example #13
Source File: plugin.ts    From jupyter-videochat with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
retroPlugin: JupyterFrontEndPlugin<void> = {
  id: `${NS}:retro`,
  autoStart: true,
  requires: [IVideoChatManager],
  optional: [IFileBrowserFactory, IMainMenu],
  activate: activateRetro,
}
Example #14
Source File: plugin.ts    From jupyter-videochat with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
publicRoomsPlugin: JupyterFrontEndPlugin<void> = {
  id: `${NS}:rooms-public`,
  autoStart: true,
  requires: [IVideoChatManager],
  optional: [IRouter, ICommandPalette],
  activate: activatePublicRooms,
}
Example #15
Source File: plugin.ts    From jupyter-videochat with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
serverRoomsPlugin: JupyterFrontEndPlugin<void> = {
  id: `${NS}:rooms-server`,
  autoStart: true,
  requires: [IVideoChatManager],
  optional: [IRouter],
  activate: activateServerRooms,
}
Example #16
Source File: plugin.ts    From jupyter-videochat with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
corePlugin: JupyterFrontEndPlugin<IVideoChatManager> = {
  id: `${NS}:plugin`,
  autoStart: true,
  requires: [ISettingRegistry],
  optional: [ITranslator, ICommandPalette, ILauncher, ILayoutRestorer, IMainMenu],
  provides: IVideoChatManager,
  activate: activateCore,
}
Example #17
Source File: index.ts    From jlab-enhanced-launcher with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
plugin: JupyterFrontEndPlugin<ILauncher> = {
  activate,
  id: EXTENSION_ID,
  requires: [ITranslator],
  optional: [ILabShell, ICommandPalette, ISettingRegistry, IStateDB],
  provides: ILauncher,
  autoStart: true
}
Example #18
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
zethus: JupyterFrontEndPlugin<void> = {
  id: "jupyterlab-ros/zethus",
  autoStart: true,
  requires: [ICommandPalette, ILayoutRestorer],
  activate: (app: JupyterFrontEnd, palette: ICommandPalette, restorer: ILayoutRestorer) => {
    const { commands } = app;
    let widget = null;

    const tracker = new WidgetTracker<ZethusWidget>({
      namespace: 'zethus'
    });

    restorer.restore(tracker, {
      command: 'jupyterlab-ros/zethus:open',
      name: () => 'zethus'
    });

    let command = 'jupyterlab-ros/zethus:open';
    commands.addCommand(command, {
      label: 'Open Zethus',
      caption: 'Open a new Zethus view.',
      isVisible: () => true,
      isEnabled: () => true,
      isToggled: () => widget !== null,
      execute: () => {
        if (widget) {
          widget.dispose();
        } else {
          widget = new ZethusWidget();

          widget.disposed.connect(() => {
            widget = null;
            commands.notifyCommandChanged();
          });

          app.shell.add(widget, 'main');
          tracker.add(widget);

          widget.update();
          commands.notifyCommandChanged();
        }
      }
    });

    palette.addItem({ command, category: 'ROS' });
  }
}
Example #19
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
settings: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-ros/settings',
  autoStart: true,
  requires: [ICommandPalette, ILayoutRestorer, ISettingRegistry],
  optional: [],
  activate: (app: JupyterFrontEnd, palette: ICommandPalette, restorer: ILayoutRestorer, settings: ISettingRegistry) => {
    const { commands } = app;
    const server = ServerConnection.makeSettings();
    const url = URLExt.join(server.baseUrl, 'ros/setting');

    let content: SettingsWidget = null;
    let widget: MainAreaWidget<SettingsWidget> = null;

    const tracker = new WidgetTracker<MainAreaWidget<SettingsWidget>>({
      namespace: 'rossettings'
    });

    restorer.restore(tracker, {
      command: 'jupyterlab-ros/settings:open',
      name: () => 'rossettings'
    });

    const command = 'jupyterlab-ros/settings:open';
    commands.addCommand(command, {
      label: 'Settings',
      caption: 'Specify your JupyterLab-ROS env.',
      isVisible: () => true,
      isEnabled: () => true,
      isToggled: () => widget !== null,
      execute: () => {
        if (widget) {
          widget.dispose();
        } else {
          content = new SettingsWidget(settings, '@robostack/jupyterlab-ros:settings');
          widget = new MainAreaWidget<SettingsWidget>({ content });
          widget.title.label = 'ROS Settings';

          widget.disposed.connect(() => {
            content.dispose();
            content = null;
            widget = null;
            commands.notifyCommandChanged();
          });

          app.shell.add(widget, 'main');
          tracker.add(widget);

          widget.update();
          commands.notifyCommandChanged();
        }
      }
    });

    palette.addItem({ command, category: 'ROS' });

    const loadSetting = (setting: ISettingRegistry.ISettings): void => {
      const env = setting.get('env').composite as string;
      const master = setting.get('master').composite as string;

      if ( env != "" || master != "" ) {
        const msg = { method: 'PUT', body: JSON.stringify({ env, master }) };
  
        ServerConnection.makeRequest(url, msg, server)
        .then( resp => {})
        .catch( err => console.log(err) );
      }
    }

    settings.load('@robostack/jupyterlab-ros:settings')
      .then(rosSetting => {
        rosSetting.changed.connect(loadSetting);
        loadSetting(rosSetting);
      }).catch(err => console.log(err));
  }
}
Example #20
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
logConsole: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-ros/logConsole',
  autoStart: true,
  requires: [ICommandPalette, ILayoutRestorer],
  optional: [],
  activate: (app: JupyterFrontEnd, palette: ICommandPalette, restorer: ILayoutRestorer) => {
    const { commands } = app;

    let widget: LogConsoleWidget = null;

    const tracker = new WidgetTracker<LogConsoleWidget>({
      namespace: 'roslogconsole'
    });

    restorer.restore(tracker, {
      command: 'jupyterlab-ros/logConsole:open',
      name: () => 'roslogconsole'
    });

    // Creating some buttons for the widget toolbar
    commands.addCommand('jupyterlab-ros/logConsole:checkpoint', {
      execute: () => widget?.checkpoint(),
      icon: addIcon,
      label: 'Add Checkpoint'
    });

    commands.addCommand('jupyterlab-ros/logConsole:clear', {
      execute: () =>  widget?.clear(),
      icon: clearIcon,
      label: 'Clear Log'
    });

    commands.addCommand('jupyterlab-ros/logConsole:open', {
      label: 'Ros log',
      caption: 'Log console for ROS.',
      isVisible: () => true,
      isEnabled: () => true,
      isToggled: () => widget !== null,
      execute: () => {
        if (widget) {
          widget.dispose();
        } else {
          widget = new LogConsoleWidget(app);

          widget.disposed.connect(() => {
            widget = null;
            commands.notifyCommandChanged();
          });

          app.shell.add(widget, 'main', { mode: 'split-bottom' });
          tracker.add(widget);

          widget.update();
          commands.notifyCommandChanged();
        }
      }
    });

    palette.addItem({ command: 'jupyterlab-ros/logConsole:open', category: 'ROS' });
  }
}
Example #21
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
launch: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-ros/launch',
  autoStart: true,
  requires: [IFileBrowserFactory, IStatusBar],
  optional: [],
  activate: (app: JupyterFrontEnd, factory: IFileBrowserFactory, statusBar: IStatusBar) => {
    let status: StatusLaunch = null;
    
    app.docRegistry.addFileType({
      name: 'launch',
      icon: launcherIcon,
      displayName: 'Launch File',
      extensions: ['.launch'],
      fileFormat: 'text',
      contentType: 'file',
      mimeTypes: ['application/xml']
    });

    app.commands.addCommand('jupyterlab-ros/launch:launch', {
      label: 'Launch',
      caption: 'Launch ROS launch file.',
      icon: launcherIcon,
      isVisible: () => true,
      isEnabled: () => true,
      execute: () => {
        const file = factory.tracker.currentWidget.selectedItems().next();
        status?.launch(file.path);
      }
    });

    app.contextMenu.addItem({
      command: 'jupyterlab-ros/launch:launch',
      selector: '.jp-DirListing-item[data-file-type="launch"]',
      rank: 0
    });

    if (!statusBar) { console.log("No status bar!"); return; }
    status = new StatusLaunch();

    const item = statusBar.registerStatusItem('jupyterlab-ros/launch:status', {
      item: status,
      align: 'left',
      rank: 4,
      isActive: () => true
    });
  }
}
Example #22
Source File: index.ts    From jupyterlab-ros with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
bag: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-ros/bag',
  autoStart: true,
  requires: [IFileBrowserFactory, ILayoutRestorer],
  optional: [],
  activate: (app: JupyterFrontEnd, factory: IFileBrowserFactory, restorer: ILayoutRestorer) => {
    
    app.docRegistry.addFileType({
      name: 'bag',
      icon: runIcon,
      displayName: 'Bag File',
      extensions: ['.bag'],
      contentType: 'file',
      fileFormat: 'base64',
      iconClass: runIcon.name,
      iconLabel: runIcon.name,
      mimeTypes: ['application/octet-stream']
    });

    const tracker = new WidgetTracker<BagWidget>({ namespace: 'rosbag' });

    restorer.restore(tracker, {
      command: 'jupyterlab-ros/bag:launch',
      args: widget => ({ path: widget.content.model.session.path }),
      name: widget => widget.content.model.session.name
    });

    app.commands.addCommand('jupyterlab-ros/bag:launch', {
      label: 'Open Bag',
      caption: 'Open ROS bag file.',
      icon: runIcon,
      execute: (args: any) => {
        console.log("launch");

        const path = ( args.path || factory.tracker.currentWidget.selectedItems().next().path );

        app.serviceManager.sessions.findByPath(path)
        .then( model => {

          if (model) {
            const session = app.serviceManager.sessions.connectTo({ model });
            const widget = new BagWidget(new BagPanel(new BagModel(session)));
  
            app.shell.add(widget, 'main');
            tracker.add(widget);
          
          } else {
            const session = new SessionContext({
              sessionManager: app.serviceManager.sessions,
              specsManager: app.serviceManager.kernelspecs,
              name: path,
              path: path
            });
  
            session.initialize()
            .then( async value => {
              if (value) {
                await sessionContextDialogs.selectKernel(session);
                const widget = new BagWidget(new BagPanel(new BagModel(session.session)));
                
                app.shell.add(widget, 'main');
                tracker.add(widget);
              }
            }).catch( err => console.error(err) );
          }
          
        }).catch( err => console.log(err) );
      }
    });

    app.contextMenu.addItem({
      command: 'jupyterlab-ros/bag:launch',
      selector: '.jp-DirListing-item[data-file-type="bag"]',
      rank: 0
    });
  }
}
Example #23
Source File: index.ts    From jupyterlab-gitplus with GNU Affero General Public License v3.0 5 votes vote down vote up
gitPlusPlugin: JupyterFrontEndPlugin<void> = {
  activate,
  requires: [IMainMenu, IEditorTracker, INotebookTracker],
  id: '@reviewnb/gitplus',
  autoStart: true
}
Example #24
Source File: index.ts    From jupyter-extensions with Apache License 2.0 5 votes vote down vote up
extension: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-comments',
  autoStart: true,
  activate: activate,
  requires: [ILabShell, ICommandPalette, IDocumentManager],
}
Example #25
Source File: index.ts    From jupyterlab-interactive-dashboard-editor with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
extension: JupyterFrontEndPlugin<IDashboardTracker> = {
  id: 'jupyterlab-interactive-dashboard-editor',
  autoStart: true,
  requires: [INotebookTracker, IMainMenu, IDocumentManager, ILauncher],
  provides: IDashboardTracker,
  activate: (
    app: JupyterFrontEnd,
    notebookTracker: INotebookTracker,
    mainMenu: IMainMenu,
    docManager: IDocumentManager,
    launcher: ILauncher
  ): IDashboardTracker => {
    // Tracker for Dashboard
    const dashboardTracker = new DashboardTracker({ namespace: 'dashboards' });

    //Tracker for DashboardWidgets
    const outputTracker = new WidgetTracker<DashboardWidget>({
      namespace: 'dashboard-outputs'
    });

    // Clipboard for copy/pasting outputs.
    const clipboard = new Set<Widgetstore.WidgetInfo>();

    // Define dashboard file type.
    const dashboardFiletype: Partial<DocumentRegistry.IFileType> = {
      name: 'dashboard',
      displayName: 'Dashboard',
      contentType: 'file',
      extensions: ['.dashboard', '.dash'],
      fileFormat: 'text',
      icon: DashboardIcons.tealDashboard,
      iconLabel: 'Dashboard',
      mimeTypes: ['application/json']
    };
    // Add dashboard file type to the doc registry.
    app.docRegistry.addFileType(dashboardFiletype);

    addCommands(
      app,
      dashboardTracker,
      outputTracker,
      clipboard,
      docManager,
      notebookTracker
    );

    // Create a new model factory.
    const modelFactory = new DashboardModelFactory({ notebookTracker });

    // Create a new widget factory.
    const widgetFactory = new DashboardDocumentFactory({
      name: 'dashboard',
      modelName: 'dashboard',
      fileTypes: ['dashboard'],
      defaultFor: ['dashboard'],
      commandRegistry: app.commands,
      outputTracker
    });

    app.docRegistry.addModelFactory(modelFactory);
    app.docRegistry.addWidgetFactory(widgetFactory);

    // Add newly created dashboards to the tracker, set their icon and label,
    // and set the default width, height, and scrollMode.
    widgetFactory.widgetCreated.connect((_sender, widget) => {
      void dashboardTracker.add(widget.content);

      widget.title.icon = dashboardFiletype.icon;
      widget.title.iconClass = dashboardFiletype.iconClass || '';
      widget.title.iconLabel = dashboardFiletype.iconLabel || '';

      const model = widget.content.model;
      // TODO: Make scrollMode changable in JL. Default 'infinite' for now.
      model.scrollMode = 'infinite';
      model.width = Dashboard.DEFAULT_WIDTH;
      model.height = Dashboard.DEFAULT_HEIGHT;
    });

    // Add commands to context menus.
    app.contextMenu.addItem({
      command: CommandIDs.save,
      selector: '.pr-JupyterDashboard',
      rank: 3
    });

    app.contextMenu.addItem({
      command: CommandIDs.undo,
      selector: '.pr-JupyterDashboard',
      rank: 1
    });

    app.contextMenu.addItem({
      command: CommandIDs.redo,
      selector: '.pr-JupyterDashboard',
      rank: 2
    });

    app.contextMenu.addItem({
      command: CommandIDs.cut,
      selector: '.pr-JupyterDashboard',
      rank: 3
    });

    app.contextMenu.addItem({
      command: CommandIDs.copy,
      selector: '.pr-JupyterDashboard',
      rank: 4
    });

    app.contextMenu.addItem({
      command: CommandIDs.paste,
      selector: '.pr-JupyterDashboard',
      rank: 5
    });

    const experimentalMenu = new Menu({ commands: app.commands });
    experimentalMenu.title.label = 'Experimental';

    experimentalMenu.addItem({
      command: CommandIDs.saveToMetadata
    });

    experimentalMenu.addItem({
      command: CommandIDs.toggleInfiniteScroll
    });

    experimentalMenu.addItem({
      command: CommandIDs.trimDashboard
    });

    app.contextMenu.addItem({
      type: 'submenu',
      submenu: experimentalMenu,
      selector: '.pr-JupyterDashboard',
      rank: 6
    });

    app.contextMenu.addItem({
      command: CommandIDs.deleteOutput,
      selector: '.pr-EditableWidget',
      rank: 0
    });

    app.contextMenu.addItem({
      command: CommandIDs.toggleFitContent,
      selector: '.pr-EditableWidget',
      rank: 1
    });

    app.contextMenu.addItem({
      command: CommandIDs.toggleWidgetMode,
      selector: '.pr-EditableWidget',
      rank: 2
    });

    app.contextMenu.addItem({
      type: 'separator',
      selector: '.pr-EditableWidget',
      rank: 3
    });

    app.contextMenu.addItem({
      command: CommandIDs.openFromMetadata,
      selector: '.jp-Notebook',
      rank: 16
    });

    // Add commands to key bindings
    app.commands.addKeyBinding({
      command: CommandIDs.deleteOutput,
      args: {},
      keys: ['Backspace'],
      selector: '.pr-EditableWidget'
    });

    app.commands.addKeyBinding({
      command: CommandIDs.undo,
      args: {},
      keys: ['Z'],
      selector: '.pr-JupyterDashboard'
    });

    app.commands.addKeyBinding({
      command: CommandIDs.redo,
      args: {},
      keys: ['Shift Z'],
      selector: '.pr-JupyterDashboard'
    });

    app.commands.addKeyBinding({
      command: CommandIDs.cut,
      args: {},
      keys: ['Accel X'],
      selector: '.pr-JupyterDashboard'
    });

    app.commands.addKeyBinding({
      command: CommandIDs.copy,
      args: {},
      keys: ['Accel C'],
      selector: '.pr-JupyterDashboard'
    });

    app.commands.addKeyBinding({
      command: CommandIDs.paste,
      args: {},
      keys: ['Accel V'],
      selector: '.pr-JupyterDashboard'
    });

    app.commands.addKeyBinding({
      command: CommandIDs.toggleFitContent,
      args: {},
      keys: ['K'],
      selector: '.pr-EditableWidget'
    });

    // Add commands to edit menu.
    mainMenu.fileMenu.addGroup([
      {
        command: CommandIDs.setDimensions
      },
      {
        command: CommandIDs.setTileSize
      }
    ]);

    mainMenu.fileMenu.newMenu.addGroup([
      {
        command: CommandIDs.createNew
      }
    ]);

    launcher.add({
      command: CommandIDs.createNew,
      category: 'Other',
      rank: 1
    });

    return dashboardTracker;
  }
}