Remap toolbar button in custom.js

I’m trying to remap the “restart and run all” button to “run all cells” in R notebooks (since restarting kills the kernel).

I’ve used custom.js to remove that button and add a new one but the new button doesn’t actually do anything. Looking at it in Developer Tools shows identical markup – what am I missing?

Script:
// hide unsupported “Download As…” options and remap “Restart and Run All Cells for R notebooks”
require([‘base/js/events’,‘base/js/namespace’], function(events, IPython){
events.on(‘kernel_ready.Kernel’, function () {
$(‘li#download_pdf’).toggle();
$(‘li#download_webpdf’).toggle();
events.trigger(‘resize-header.Page’);
// if R kernel remap
var ker = getElementByXpath(“/html/body/div[@id=‘header’]/div[@id=‘menubar-container’]/div[@id=‘menubar’]/div[@id=‘menus’]/div[1]/p[@id=‘kernel_indicator’]/span[@class=‘kernel_indicator_name’]”);
if (ker.textContent == ‘R’){
alert(‘R kernel’);
// create button
var my_button = document.createElement(‘button’);
var my_i = document.createElement(‘i’);
my_button.setAttribute(“class”, “btn btn-default”);
my_button.setAttribute(“title”,“Run All Cells”);
my_button.setAttribute(“data-jupyter-action”,“jupyter-notebook:run-all-cells”);
my_i.setAttribute(“class”,“fa-forward fa”);
my_button.appendChild(my_i);

// JUPYTER method - same result
/*
var my_button = $(‘’)
.attr(‘class’,‘btn btn-default’)
.attr(‘title’,‘run all cells in the notebook’)
.attr(‘data-jupyter-action’,‘jupyter-notebook:run-all-cells’)
.attr(‘aria-label’, ‘Rull All’)
.insertAfter(‘div#run_int’)
.append(
$(‘’)
.addClass(‘fa-forward fa’)
)
// remove old button
const div = getElementByXpath(‘//div[@id=“run_int”]’);
div.removeChild(div.children[3]);
div.appendChild(my_button);

      //TODO: bind new button to action
    }

});
});

HTML shows new button, but doesn’t work:

Run ****

Any pointers would be appreciated.

TIA,
John

I believe I am having a similar issue. Trying to install some custom javascript in a notebook, which apparently used to work (according to the many old stackexchange, etc. posts I’ve read.) According to the latest “Migrating From IPython Notebook” page:

~/.ipython/profile_default/static/custom~/.jupyter/custom

I should be able to put javascript in ${JUPYTER_CONFIG_DIR}/custom/custom.js and the browser should pick it up when the notebook is launched. But it doesn’t.

>pip list
. . .
ipykernel                 6.25.1
ipython                   8.14.0
. . .
jupyter_client            8.3.0
jupyter_core              5.3.1
jupyter-events            0.7.0
jupyter-lsp               2.2.0
jupyter_server            2.7.2
jupyter_server_terminals  0.4.4
jupyterlab                4.0.5
jupyterlab-pygments       0.2.2
jupyterlab_server         2.24.0
. . .
nbclient                  0.8.0
nbconvert                 7.7.4
nbformat                  5.9.2
. . .
notebook                  7.0.2
notebook_shim             0.2.3
. . .
traitlets                 5.9.0

More info from testing. 3 files (didn’t see a way to attach):

foo.ipynb:

{
 "cells": [],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}

custom.js:

console.log("custom log message");

test.sh:

#/bin/bash

set -x
rm -rf test
mkdir test; cd test
python3.10 -m venv foo_venv
source foo_venv/bin/activate 2>> /dev/null
pip install pip setuptools --upgrade >> /dev/null
pip install notebook >> /dev/null
ipython kernel install --name foo_venv --prefix=./foo_venv >> .install.log
jupyter kernelspec list | grep -E "^\s*foo_venv(\s.*)?$"
cp ../foo.ipynb .
cat foo.ipynb
export JUPYTER_CONFIG_DIR=$(pwd)/foo_venv/etc/jupyter/
echo $JUPYTER_CONFIG_DIR 
jupyter migrate    # mine says [JupyterMigrate] Found nothing to migrate.
mkdir -p foo_venv/etc/jupyter/custom
cp ../custom.js foo_venv/etc/jupyter/custom/
date
stat foo_venv/etc/jupyter/custom/custom.js
# BTW, *without* editing ```foo.ipynb```, how can I tell it to just launch this kernel without the popup?
jupyter-notebook foo.ipynb --MultiKernelManager.default_kernel_name=foo_venv 2>> /dev/null
stat foo_venv/etc/jupyter/custom/custom.js 
date
cat foo_venv/etc/jupyter/custom/custom.js   # doing after so as not to change the access/creation times
cd ..
deactivate 2>> /dev/null
set +x

To run: chmod +x test.sh; ./test.sh

  • Creates a dir test, goes into it
  • makes and activates venv foo_venv
  • installs notebook
  • installs foo_venv kernel locally (in (./)foo_venv/share/jupyter/kernels/foo_venv)
  • puts custom.js into ${JUPYTER_CONFIG_DIR}=$(pwd)/foo_venv/etc/jupyter in /custom
  • stats custom.js
  • runs minimal notebook foo.ipynb (See comment above about getting notebook to “just launch” the kernel)
  • (Ctrl-C or File->Shut Down to exit.)
  • stats custom.js again.
++ date
Sun Aug 20 14:44:00 EDT 2023
++ stat foo_venv/etc/jupyter/custom/custom.js
16777220 261616118 -rw-r--r-- 1 me me 0 35 "Aug 20 14:44:00 2023" "Aug 20 14:44:00 2023" "Aug 20 14:44:00 2023" "Aug 20 14:44:00 2023" 4096 8 0 foo_venv/etc/jupyter/custom/custom.js
++ jupyter-notebook foo.ipynb  --MultiKernelManager.default_kernel_name=foo_venv
++ stat foo_venv/etc/jupyter/custom/custom.js
16777220 261616118 -rw-r--r-- 1 me me 0 35 "Aug 20 14:44:05 2023" "Aug 20 14:44:00 2023" "Aug 20 14:44:00 2023" "Aug 20 14:44:00 2023" 4096 8 0 foo_venv/etc/jupyter/custom/custom.js
++ date
Sun Aug 20 14:44:15 EDT 2023

Clearly something jupyter-notebook did accessed custom.js, but I don’t see “custom log message” in the console log.

What I’m actually trying to do:

I have an installer (for a program) that runs a notebook to demonstrate how the program works. It needs to be seamless and foolproof. Telling the user to either hit Control-C (and then kill the browser tab) or go to File->Shut Down has a very low probability of success (IMO). Also, it’s yucky. So I’d like to attach some JS to the hook for closing the browser tab that calls shutdown on the notebook.

Then the instructions to the user are “Close the notebook browser tab to continue.” But I can’t get the notebook to install/run my JS.