abstract
in this document, we are going to discuss how to create a simple jupyterlab plugin having a custom menu 'BookMark'.
Let's start
let's start with a simple plugin project created from extension-cookiecutter-ts
if you are a newbie to cookiecutter, this post would be a help.
Basic Skelecton
this is a basic skelecton of a jupyterlab plugin.

import part
import {IMainMenu} from '@jupyterlab/mainmenu'
import {IFrame, ICommandPalette} from '@jupyterlab/apputils'
import {Menu} from '@phosphor/widgets'
to use @jupyterlab/mainmenu package, you might need to describe the package name in package.json file.
if you use pycharm, alt+enter will do that for you.
not installed? don't be worry. jlpm install comannd will make you have the package under node_modules directory.
extension part
/**
* Initialization data for the jupyterlab_myext extension.
*/
const extension: JupyterLabPlugin<void> = {
id: 'jupyterlab_myext',
autoStart: true,
requires: [IMainMenu, ICommandPalette],
activate: activate_custom_menu
};
in webstorm, do Ctrl+click the requires and you can navigate to the definition.
bookmark data part
export const BookMarks = [
{
name: 'gmo',
url: 'https://www.gmo.jp/en/',
description: 'Creating a simple JupyterLab plugin adding BookMark menu',
target: 'widget'
}
];
this is a hard-coded bookmark data. of course, you can change it to load from file, url(ajax) or to be delivered by javascript from index.html, which is rendered by jupyterlab_launcher/handlers.py
activation definition part
export function activate_custom_menu(app: JupyterLab, mainMenu: IMainMenu, palette:
ICommandPalette): Promise<void>{
// todo
return Promise.resolve(void 0);
}
by the declaration requires: [IMainMenu, ICommandPalette] at extension definition,
those parameters are dynamically delivered when calling activate_custom_menu
if you want to provide a service other widgets can refer, use provides property.
for details, refer
jupyterlab_myext\node_modules@phosphor\application\lib\index.d.ts
Register Commands
phosphorjs uses command|event / listener model like VSCode does.
I believe you can understand what this typescript does.
function appendNewCommand(item: any) {
let iframe: IFrame = null;
let command = `BookMark-${item.name}:show`;
app.commands.addCommand(command, {
label: item.name,
execute: () => {
if (item.target == '_blank') {
let win = window.open(item.url, '_blank');
win.focus();
} else if (item.target == 'widget') {
if (!iframe) {
iframe = new IFrame();
iframe.url = item.url;
iframe.id = item.name;
iframe.title.label = item.name;
iframe.title.closable = true;
iframe.node.style.overflowY = 'auto';
}
if (iframe == null || !iframe.isAttached) {
app.shell.addToMainArea(iframe);
app.shell.activateById(iframe.id);
} else {
app.shell.activateById(iframe.id);
}
}
}
});
}
BookMarks.forEach(item => appendNewCommand(item));
IFrame came from @jupyterlab/apputils
import {IFrame, ICommandPalette} from '@jupyterlab/apputils'
UI rednering
and make menu show actually.
we usually describe rendering part on Private namespace like below.

build

what? no Set? then, make target and lib use ES6 on tsconfig.json


Okay.
jupyter labextension list
jupyter lab build
run
jupyter lab
the whole code
import {
JupyterLab, JupyterLabPlugin
} from '@jupyterlab/application';
import {IMainMenu} from '@jupyterlab/mainmenu'
import {IFrame, ICommandPalette} from '@jupyterlab/apputils'
import {Menu} from '@phosphor/widgets'
import '../style/index.css';
/**
* Initialization data for the jupyterlab_myext extension.
*/
const extension: JupyterLabPlugin<void> = {
id: 'jupyterlab_myext',
autoStart: true,
requires: [IMainMenu, ICommandPalette],
activate: activate_custom_menu
};
export default extension;
export const BookMarks = [
{
name: 'gmo',
url: 'https://www.gmo.jp/en/',
description: 'Creating a simple JupyterLab plugin adding BookMark menu',
target: 'widget'
}
];
export function activate_custom_menu(app: JupyterLab, mainMenu: IMainMenu, palette:
ICommandPalette): Promise<void>{
// create new commands and add them to app.commands
function appendNewCommand(item: any) {
let iframe: IFrame = null;
let command = `BookMark-${item.name}:show`;
app.commands.addCommand(command, {
label: item.name,
execute: () => {
if (item.target == '_blank') {
let win = window.open(item.url, '_blank');
win.focus();
} else if (item.target == 'widget') {
if (!iframe) {
iframe = new IFrame();
iframe.url = item.url;
iframe.id = item.name;
iframe.title.label = item.name;
iframe.title.closable = true;
iframe.node.style.overflowY = 'auto';
}
if (iframe == null || !iframe.isAttached) {
app.shell.addToMainArea(iframe);
app.shell.activateById(iframe.id);
} else {
app.shell.activateById(iframe.id);
}
}
}
});
}
BookMarks.forEach(item => appendNewCommand(item));
// add to mainMenu
let menu = Private.createMenu(app);
mainMenu.addMenu(menu, {rank: 80});
return Promise.resolve(void 0);
}
/**
* A namespace for help plugin private functions.
*/
namespace Private {
/**
* Creates a menu for the help plugin.
*/
export function createMenu(app: JupyterLab): Menu {
const {commands} = app;
let menu:Menu = new Menu({commands});
menu.title.label = 'BookMark';
BookMarks.forEach(item => menu.addItem({command: `BookMark-${item.name}:show`}));
return menu;
}
}
ref
jupyterlabのexternal extensionを作ってみた。
https://qiita.com/crowdy/items/167a65cee4a857184ac7










