-
Notifications
You must be signed in to change notification settings - Fork 795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add offline support to JupyterChart and "jupyter" renderer #3305
Conversation
…encies for offline support
This is done by loading JS dependencies from vl-convert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Tested locally. Thank you @jonmmease, another great feature :) Looking forward to releasing this into the wild.
This looks really neat, thank you @jonmmease ! I was initially going to suggest that maybe |
This: alt.JupyterChart.enable_offline() Works on a single chart object? Or will this render all JupyterCharts offline from the moment this line is called? In case of the first, could it be an argument instead of a method call? alt.JupyterChart(spec, offline=True) |
Oh I see, it's written in the docs. When called, it renders all charts offline. For my understanding, does this work globally on a JupyterLab session level? Or is this JavaScript bundle injected in each chart object? Like, if a notebook has 50 charts, it will inject this bundle 50 times or one time? |
This is a good question. My assumption is that this would inject the bundle for each displayed widget, but I haven't been able to get JupyterLab to actually save the widget state to the resulting notebook file, so I haven't been able to inspect how this works. Even when I check "Settings -> Save Widget State Automatically" I'm not seeing the widget state saved to the ipynb file. @manzt, if you have a moment, could you comment on whether the JavaScript bundle associated with an AnyWidget is injected/saved once per widget instance, or once per session? Thanks! |
The widget ESM for anywidget is injected once per widget instance, not per session. Therefore, if there are multiple views of the same widget instance, there's only one ESM, but separate instances mean separate injections.
Not seeing the widget state saved is due to how ipywidgets handles embedding, by default omitting the "default" trait values (which One option would be to publish the final ESM bundle to a CDN or have a HTML-bloat-friendly version of the ESM, enabled by a flag. It's really tricky to implement depulication of ESM for anywidget, since it would require anywidget keep track of a registry of ESM and be aware of widgets removed and added. I've avoided implementing for simplicity. Open to discussing further solutions. |
Thanks for the context and clarifications @manzt. @mattijn, so as things stand right now, widgets based on AnyWidget don't have their source saved in the notebook file. This means that these charts won't appear when opening a notebook until they are re-run. But also means that this PR doesn't make the situation worse in terms of notebook file size. I'm going to go ahead and merge. Thanks for the reviews all. |
Thanks for the clarification! |
I just had an idea on this front and think I have a way to de-doupe _esm in the front end. |
This PR adds optional offline support for JupyterChart and the "jupyter" renderer.
How to enable offline support
Offline support is enabled on JupyterChart directly by calling the
JupyterChart.enable_offline()
class method.Offline support is enabled for the "jupyter" renderer by activating the renderer with the
offline
kwarg:How it works
In order to support offline usage, the AnyWidget documentation describes how to create JS bundles using bundlers like esbuild: https://anywidget.dev/en/bundling/#bundler-guides. This is the approach I used in the very first JupyterChart PR (#3108).
There are two main downsides of this approach:
Because of these downsides, we opted to start with an online-only version in #3119.
When vl-convert added support for HTML export, it added a
javascript_bundle
function that performs bundling (using deno_emit). Unlike a regular bundler that supports all JavaScript packages and downloads them from the internet during bundling, vl-convert's bundler only supports a couple of packages (vegaEmbed
andlodashDebounce
in particular) but these packages are vendored in the vl-convert executable so no internet connection is required to perform bundling.The
JupyterChart.enable_offline
method calls vl-convert'sjavascript_bundle
function to generate a bundle for the widget code that includes all dependencies, including the correct version of Vega-Lite. This takes around 1 second to run on my machine, which I think is well worth it to overcome both of the downsides above.Docs
I added documentation in both the JupyterChart and "jupyter" renderer sections