Static HTML - Quarto + Python
Hi,
Really excited about the direction of this package.
I would like to display the outputs within a static html i.e. one produced with Quarto. I wasn't able to get the output to show using the Jupyter Widget. Is this use case possible? If not, is this something that could work in future?
quarto render test.ipynb --execute
test.ipynb -
import pandas as pd
from mosaic_widget import MosaicWidget
weather = pd.read_csv("https://uwdata.github.io/mosaic-datasets/data/seattle-weather.csv", parse_dates=['date'])
spec = {
"params": {
"click": { "select": "single" },
"domain": ["sun", "fog", "drizzle", "rain", "snow"],
"colors": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"]
},
"vconcat": [
{
"hconcat": [
{
"plot": [
{
"mark": "dot",
"data": { "from": "weather", "filterBy": "$click" },
"x": { "dateMonthDay": "date" },
"y": "temp_max",
"fill": "weather",
"r": "precipitation",
"opacity": 0.7
},
{ "select": "intervalX", "as": "$range" },
{ "select": "highlight", "by": "$range", "fill": "#eee" },
{ "legend": "color", "as": "$click", "columns": 1 }
],
"xyDomain": "Fixed",
"xTickFormat": "%b",
"colorDomain": "$domain",
"colorRange": "$colors",
"rDomain": "Fixed",
"rRange": [2, 10],
"width": 800
}
]
},
{
"plot": [
{
"mark": "barX",
"data": { "from": "weather" },
"x": { "count": None },
"y": "weather",
"fill": "#f5f5f5"
},
{
"mark": "barX",
"data": { "from": "weather", "filterBy": "$range" },
"x": { "count": None },
"y": "weather",
"fill": "weather",
"order": "weather"
},
{ "select": "toggleY", "as": "$click" },
{ "select": "highlight", "by": "$click" }
],
"xDomain": "Fixed",
"yDomain": "$domain",
"yLabel": None,
"colorDomain": "$domain",
"colorRange": "$colors",
"width": 800
}
]
}
MosaicWidget(spec, data = {"weather": weather})
I haven't tried with quarto. Do you see any errors? Maybe @cscheid can help.
Thanks for tagging me -
I see an error message in the console, specifically that a request to anywidget.js is failing. I don't know how Mosaic or anywidget works, but something somewhere is asking for anywidget.js and Quarto hasn't been told that about this file. Unfortunately I think that it means Mosaic won't work with Quarto until we figure out how to make anywidget work with Quarto.
Does any widget usually work with quarto? That would be huge since there are so many any widgets now.
cc @manzt
Hey, I think there are some issues with quarto and Jupyter Widgets: https://github.com/manzt/anywidget/issues/343#issuecomment-2515888853.
EDIT: hoping to have a closer look next week!
Also, there is another dimension here (specifically with mosaic, but other widgets that rely on custom messages as well).
render generates a static HTML file without a Python backend. IIRC mosaic requires the duckdb on the Python side, so the custom messaging wouldn't work in this static context (messages would get emitted from the front-end but never resolvve). I have been thinking about this in the context of quak.
It would be nice if there was a "client-only mode" for the mosaic architecture in Jupyter, to embed the data and use DuckDB WASM. This would substantially increase the JS-bundle, but have a lot of benefits for this kind of use case. Maybe most simple to experiment with an environment variable first. E.g.,
MOSAIC_EXPERIMENTAL_EMBED=1 quarto render test.ipynb --execute
The alternative would be making the renders more capable and bring the Jupyter kernel in the browser (e.g., via pyodide). Something like JupyterLite, but more minimal and for presentation. I think the Myst folks are looking into something like this (cc: @rowanc1). Could accommodate all widgets that way, but I think it's also worth exploring this more lightweight "embed" version of mosaic (since it could also be used in something like MosaicWidget(...).save("index.html")).
Happy to poke around with a PR post PhD :)
Thanks for the background. I think having a client-only mode for mosaic would be nice but also having the kernel in the browser could be great (and maybe a more generic solution for similar use cases). Either way, doesn't sound like this isn't a simple bug but a larger new feature. Marked it as such.
If you're willing to take Mosaic into OJS that works (with duckdb-wasm backend). You can use ojs_define to get Python data cleanly over to OJS at render time (ojs_define works interactively too using shiny, but I suspect this will lead to issues on duckdb/mosaic side)
Newbieish Mosaic working example in Quarto using OJS (no Python or ojs_define: I preprocessed data outside of Quarto): https://calcwithdec.dev/posts/linked-visuals/ Source: https://gitlab.com/declann/calcwithdec.dev/blob/main/posts/linked-visuals/index.qmd
This uses the Javascript API, but you can use JSON API from OJS (might be a compile step - mosaic exposes all the pieces) edit: docs for this: https://idl.uw.edu/mosaic/api/spec/parser-generators.html
It looks like it is possible to package MosaicWidget into a fully static web bundle.
Here is a Marimo notebook implementation of the Mosaic spec playground we access when running npm run dev:
https://github.com/user-attachments/assets/89d359bd-abd3-44da-9d7a-8252037a24a8
AnyWidget seems to work nicely in this environment. The primary headache is around data fetching directly from the browser, but this comes with Pyodide inherently.
Since Marimo notebooks can be downloaded directly as a static WASM web bundle, it should be relatively straightforward to create a Quarto filter or shortcode-based extension to embed Mosaic visualizations into Quarto documents.