From 5c01af7effc0b66b4b433eaf47e19f5361312290 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 17 Sep 2024 17:22:19 -0500 Subject: [PATCH] Close #159. Combine libembed and output scripts into single dependency to avoid Quarto dependency ordering bug --- CHANGELOG.md | 4 +++ shinywidgets/__init__.py | 2 +- shinywidgets/_dependencies.py | 49 +++++++++++++++------------------- shinywidgets/_output_widget.py | 3 +-- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4cfc37..78ab631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to shinywidgets will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [UNRELEASED] + +* Fixed an issue where widgets would sometimes fail to render in a Quarto document. (#159) + ## [0.3.3] - 2024-08-13 * Fixed a bug with receiving binary data on the frontend, which gets [quak](https://github.com/manzt/quak) and [mosaic-widget](https://idl.uw.edu/mosaic/jupyter/) working with `@render_widget`. (#152) diff --git a/shinywidgets/__init__.py b/shinywidgets/__init__.py index 973da79..2aafaa5 100644 --- a/shinywidgets/__init__.py +++ b/shinywidgets/__init__.py @@ -2,7 +2,7 @@ __author__ = """Carson Sievert""" __email__ = "carson@posit.co" -__version__ = "0.3.3" +__version__ = "0.3.3.9000" from ._as_widget import as_widget from ._dependencies import bokeh_dependency diff --git a/shinywidgets/_dependencies.py b/shinywidgets/_dependencies.py index ee56c1b..e2fc054 100644 --- a/shinywidgets/_dependencies.py +++ b/shinywidgets/_dependencies.py @@ -22,38 +22,33 @@ # TODO: scripts/static_download.R should produce/update these -def libembed_dependency() -> List[HTMLDependency]: - return [ - # Jupyter Notebook/Lab both come "preloaded" with several @jupyter-widgets packages - # (i.e., base, controls, output), all of which are bundled into this extension.js file - # provided by the widgetsnbextension package, which is a dependency of ipywidgets. - # https://github.com/nteract/nes/tree/master/portable-widgets - # https://github.com/jupyter-widgets/ipywidgets/blob/88cec8/packages/html-manager/src/htmlmanager.ts#L115-L120 - # - # Unfortunately, I don't think there is a good way for us to "pre-bundle" these dependencies - # since they could change depending on the version of ipywidgets (and ipywidgets itself - # doesn't include these dependencies in such a way that require("@jupyter-widget/base") would - # work robustly when used in other 3rd party widgets). Moreover, I don't think we can simply - # have @jupyter-widget/base point to https://unpkg.com/@jupyter-widgets/base@__version__/lib/index.js - # (or a local version of this) since it appears the lib entry points aren't usable in the browser. - # - # All this is to say that I think we are stuck with this mega 3.5MB file that contains all of the - # stuff we need to render widgets outside of the notebook. - HTMLDependency( - name="ipywidget-libembed-amd", - version=parse_version_safely(__html_manager_version__), - source={"package": "shinywidgets", "subdir": "static"}, - script={"src": "libembed-amd.js"}, - ), - ] - - def output_binding_dependency() -> HTMLDependency: + # Jupyter Notebook/Lab both come "preloaded" with several @jupyter-widgets packages + # (i.e., base, controls, output), all of which are bundled into this extension.js file + # provided by the widgetsnbextension package, which is a dependency of ipywidgets. + # https://github.com/nteract/nes/tree/master/portable-widgets + # https://github.com/jupyter-widgets/ipywidgets/blob/88cec8/packages/html-manager/src/htmlmanager.ts#L115-L120 + # + # Unfortunately, I don't think there is a good way for us to "pre-bundle" these dependencies + # since they could change depending on the version of ipywidgets (and ipywidgets itself + # doesn't include these dependencies in such a way that require("@jupyter-widget/base") would + # work robustly when used in other 3rd party widgets). Moreover, I don't think we can simply + # have @jupyter-widget/base point to https://unpkg.com/@jupyter-widgets/base@__version__/lib/index.js + # (or a local version of this) since it appears the lib entry points aren't usable in the browser. + # + # All this is to say that I think we are stuck with this mega 3.5MB file that contains all of the + # stuff we need to render widgets outside of the notebook. return HTMLDependency( name="ipywidget-output-binding", version=__version__, source={"package": "shinywidgets", "subdir": "static"}, - script={"src": "output.js"}, + script=[ + {"src": "libembed-amd.js"}, + # Bundle our output.js in the same dependency as libembded since Quarto + # has a bug where it doesn't renders dependencies in the order they are defined + # (i.e., this way we can ensure the output.js script always comes after the libembed-amd.js script tag) + {"src": "output.js"}, + ], stylesheet={"href": "shinywidgets.css"}, ) diff --git a/shinywidgets/_output_widget.py b/shinywidgets/_output_widget.py index 88b09f5..efe7f44 100644 --- a/shinywidgets/_output_widget.py +++ b/shinywidgets/_output_widget.py @@ -8,7 +8,7 @@ from shiny.ui.fill import as_fill_item, as_fillable_container from ._cdn import SHINYWIDGETS_CDN, SHINYWIDGETS_CDN_ONLY -from ._dependencies import libembed_dependency, output_binding_dependency +from ._dependencies import output_binding_dependency __all__ = ("output_widget",) @@ -23,7 +23,6 @@ def output_widget( ) -> Tag: id = resolve_id(id) res = tags.div( - *libembed_dependency(), output_binding_dependency(), head_content( tags.script(