'How do you get the currently active notebook name in JupyterLab?

I'm working on creating a server-side extension in JupyterLab and have been searching for quite a while for a way to get the currently active notebook name inside my index.ts file. I found this existing extension that gets the currently active tab name with ILabShell and sets the browser tab to have the same name. In my case I'll be triggering the process with a button on the notebook toolbar so the active tab will always be a notebook. However, when I try to use the code in the activate section of my extension, I get TypeError: labShell.currentChanged is undefined where labShell is an instance of ILabShell. That extension doesn't support JupyterLab 3.0+ so I believe that's part of the problem. However, currentChanged for ILabShell is clearly defined here. What if anything can I change to make it work? Is there another way to accomplish what I'm trying to do? I'm aware of things like this to get the notebook name inside the notebook but that's not quite what I'm trying to do.

Windows 10,
Node v14.17.0,
npm 6.14.13,
jlpm 1.21.1,
jupyter lab 3.0.14

I'm using this example server extension as a template: https://github.com/jupyterlab/extension-examples/tree/master/server-extension

index.ts file from the existing extension to get the current tab name:

import {
  ILabShell,
  JupyterFrontEnd,
  JupyterFrontEndPlugin
} from '@jupyterlab/application';

import { Title, Widget } from '@lumino/widgets';

/**
 * Initialization data for the jupyterlab-active-as-tab-name extension.
 */
const extension: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-active-as-tab-name',
  autoStart: true,
  requires: [ILabShell],
  activate: (app: JupyterFrontEnd, labShell: ILabShell) => {
    const onTitleChanged = (title: Title<Widget>) => {
      console.log('the JupyterLab main application:', title);
      document.title = title.label;
    };

    // Keep the session object on the status item up-to-date.
    labShell.currentChanged.connect((_, change) => {
      const { oldValue, newValue } = change;

      // Clean up after the old value if it exists,
      // listen for changes to the title of the activity
      if (oldValue) {
        oldValue.title.changed.disconnect(onTitleChanged);
      }
      if (newValue) {
        newValue.title.changed.connect(onTitleChanged);
      }
    });
  }
};

export default extension;

My index.ts file:

import {
  ILabShell,
  JupyterFrontEnd,
  JupyterFrontEndPlugin
} from '@jupyterlab/application';

import { ICommandPalette } from '@jupyterlab/apputils';

import { ILauncher } from '@jupyterlab/launcher';

import { requestAPI } from './handler';

import { ToolbarButton } from '@jupyterlab/apputils';

import { DocumentRegistry } from '@jupyterlab/docregistry';

import { INotebookModel, NotebookPanel } from '@jupyterlab/notebook';

import { IDisposable } from '@lumino/disposable';

import { Title, Widget } from '@lumino/widgets';

export class ButtonExtension implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
    
  constructor(app: JupyterFrontEnd) { 
      this.app = app;
  }
  
  readonly app: JupyterFrontEnd
    
  createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {

    // dummy json data to test post requests
    const data2 = {"test message" : "message"}
    const options = {
      method: 'POST',
      body: JSON.stringify(data2),
      headers: {'Content-Type': 'application/json'
      }
    };
 
    // Create the toolbar button
    let mybutton = new ToolbarButton({ 
      label: 'Measure Energy Usage',
      onClick: async () => {

        // POST request to Jupyter server
        const dataToSend = { file: 'nbtest.ipynb' };
        try {
          const reply = await requestAPI<any>('hello', {
            body: JSON.stringify(dataToSend),
            method: 'POST'
          });
          console.log(reply);
        } catch (reason) {
          console.error(
            `Error on POST /jlab-ext-example/hello ${dataToSend}.\n${reason}`
          );
        }
        
        // sample POST request to svr.js
        fetch('http://localhost:9898/api', options);
      }
    });
    // Add the toolbar button to the notebook toolbar
    panel.toolbar.insertItem(10, 'MeasureEnergyUsage', mybutton);
    console.log("MeasEnerUsage activated");

    // The ToolbarButton class implements `IDisposable`, so the
    // button *is* the extension for the purposes of this method.
    return mybutton;
  }
}

/**
 * Initialization data for the server-extension-example extension.
 */
const extension: JupyterFrontEndPlugin<void> = {
  id: 'server-extension-example',
  autoStart: true,
  optional: [ILauncher],
  requires: [ICommandPalette, ILabShell],
  activate: async (
    app: JupyterFrontEnd,
    palette: ICommandPalette,
    launcher: ILauncher | null,
    labShell: ILabShell
  ) => {
    console.log('JupyterLab extension server-extension-example is activated!');
    const your_button = new ButtonExtension(app);
    app.docRegistry.addWidgetExtension('Notebook', your_button);
    
    // sample GET request to jupyter server
    try {
      const data = await requestAPI<any>('hello');
      console.log(data);
    } catch (reason) {
      console.error(`Error on GET /jlab-ext-example/hello.\n${reason}`);
    }

    // get name of active tab

    const onTitleChanged = (title: Title<Widget>) => {
      console.log('the JupyterLab main application:', title);
      document.title = title.label;
    };
    
    // Keep the session object on the status item up-to-date.
    labShell.currentChanged.connect((_, change) => {
      const { oldValue, newValue } = change;

      // Clean up after the old value if it exists,
      // listen for changes to the title of the activity
      if (oldValue) {
        oldValue.title.changed.disconnect(onTitleChanged);
      }
      if (newValue) {
        newValue.title.changed.connect(onTitleChanged);
      }
    });
  }
};

export default extension;



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source