Seeking suggestions for the best way to implement a split-view JupyterLab notebook extension

Hi, I’m working on a JupyterLab extension that involves creating a two-vertical-panel resizable-split-view version of the Notebook, like what I’ve sketched out in the mockup below: notebook on the right, other functionality on the left, draggable resize bar in the middle.
My requirement is that I need to leave the actual notebook extension alone – what I’m building will be added to the “Other” section of the launcher; also I need the split view and not a new tab for the “other functionality” because I’m going to be modifying the right-hand-side notebook as well.

What I’ve tried and works so far: I made a copy of the Notebook-extension, renamed the factory and extended the NotebookPanel and NotebookWidgetFactory, and it successfully is added as a separate extension in JupyterLab and opens it’s own notebook copy.

Where I’m stuck: How do I override the element that the notebook should attach to? I plan on using Split.js which has an HTML element representing the right-hand-side panel, which I want to mount it to. Do I…

  1. Create a new parent widget that then embeds the NotebookPanel
  2. Extend the NotebookPanel and add modify its layout to be a split view without creating a new parent widget

I’m having a hard time even finding where the parent HTML element is set for the NotebookPanel. I think I traced it down to the layout in Phosphor.js, but it says the parent should not be set by user code…

Any help with the proper way to approach this problem would be appreciated, thanks.

Are you set on using split.js, or have you considered using the Phosphor split view (which is used, for example, for splitters in the doc panel, or for the splitter in the Advanced Settings view)?

For example, this is how the advanced settings editor does it:

Likely using the phosphor split view will be easiest, and most consistent with other split views in JupyterLab.

If you want to use split.js, you’ll probably want to bridge the phosphor events over split.js to the notebook panel (so resizes work properly, for example), so likely the first thing to do is to make a phosphor widget that uses split.js to lay out its children.

I didn’t know about the Phosphor split view, so I’ll try using that as the parent widget and see how far I get. Thanks!

Also, if you right click on a notebook’s tab in JupyterLab there is a menu item to create a new side-by-side view of that same notebook that will remain synchronized with the other view.

I’m now trying to override the default JupyterLab Notebook so that when it opens it looks like the image below, using the SplitPanel widget.

I figured out I can use the INotebookTracker in my extension to access the Notebook and then use the nbTracker.currentWidget.layout.removeWidget() method to remove the NotebookPanel’s content. So far so good. Then I tried to add a SplitPanel widget (with a NotebookPanel on the right hand side) however there’s no nbTracker.currentWidget.layout.addWidget() method, so now I’m stuck.

What I’m trying to achieve:
I’m able to remove the notebook portion of the notebook using layout.removeWidget():

There is no layout.addWidget() function (see below), so now I’m stuck. I also tried directly setting the content like nbTracker.currentWidget.content = splitPanel but that doesn’t work either:

Please let me know the best way to solve this, thank you!

The biggest problem I see is that you are inserting a widget in the widget hierarchy, so code that depends on the notebook panel .content being a notebook widget won’t work anymore.

Another Lumino thing is to not use the layout add/remove methods, in general, especially once the widget is on the page. Instead use the widget’s methods and attributes. For example, remove the widget by setting its .parent to null (like notebookPanel.content.parent = null).

Perhaps the best thing to do here that does not break the notebook panel widget interface is create a new notebook viewer that makes the top-level widget a split panel. You can reuse the existing notebook component, of course.

I’m now trying to figure out how to “create a new notebook viewer” as you suggested, but I’m again stuck. Any guidance for how to approach this would be much appreciated.
I tried the Adding a button to the toolbar example (from this page, and it works for me and was useful. However, I’m having trouble figuring out how to instead extend/override the NotebookPanel to create a new viewer.

I’ve also tried getting the Notebook factory and then overriding the createNew function, but this approach isn’t working for me either. I was hoping that I could override the createNew function and then pass my own widget like SplitViewNotebookPanel.

Another attempt was disposing of the Notebook Factory, and registering a new one, but it seems like it isn’t cleared from the registry when it’s disposed?? (I get the error of duplicate registered factories, and the default notebook factory still opens notebooks instead of my new factory).


I also wanted to add that I’d like to toggle the SplitPanel functionality from a button in the toolbar, if that affects the suggested implementation. In other words, let’s say the user currently sees the default notebook, then they click the button in the toolbar which triggers the split panel with the notebook on the right.

I even tried manually deleting the notebook registration, but it won’t let me.

And tried setting defaultTypes and defaultFor on my new factory to ‘*’, but still the default notebook viewer is the one that opens

I feel like I’m hitting a lot of dead ends.