Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions .github/workflows/deploy-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ jobs:

- uses: astral-sh/setup-uv@v5

- name: Install marimo
run: pip install marimo

- name: Export app to WebAssembly
run: marimo export html-wasm app/fit_app.py -o dist --mode run
- name: Install marimo and Playwright
run: |
pip install "marimo>=0.25" playwright
python -m playwright install chromium --with-deps

- name: Export app to WebAssembly (offline bundle)
# --offline bundles the Python runtime and all dependency wheels
# (incl. rheofit) with the page: the app boots with no network
# access and never touches a package index at runtime.
run: marimo export html-wasm app/fit_app.py -o dist --mode run --offline

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
pip install -r docs/requirements.txt
pip install -e .

- name: Install Chromium (offline WASM export)
run: python -m playwright install chromium --with-deps

- name: Export interactive notebooks
run: python docs/export_interactive.py

Expand Down
29 changes: 28 additions & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,36 @@ build:
os: ubuntu-24.04
tools:
python: "3.12"
# System libraries for the headless Chromium that the offline WASM
# export (marimo --offline) needs; list from Playwright's own dependency
# data for ubuntu-24.04/chromium.
apt_packages:
- libasound2t64
- libatk-bridge2.0-0t64
- libatk1.0-0t64
- libatspi2.0-0t64
- libcairo2
- libcups2t64
- libdbus-1-3
- libdrm2
- libgbm1
- libglib2.0-0t64
- libnspr4
- libnss3
- libpango-1.0-0
- libx11-6
- libxcb1
- libxcomposite1
- libxdamage1
- libxext6
- libxfixes3
- libxkbcommon0
- libxrandr2
jobs:
pre_build:
# export marimo wasm explorers before Sphinx runs
# headless browser for the offline WASM export, then export the
# marimo explorers before Sphinx runs
- python -m playwright install chromium
- python docs/export_interactive.py

sphinx:
Expand Down
53 changes: 52 additions & 1 deletion docs/export_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
Generated HTML goes to ``docs/_static/interactive/<name>/`` (gitignored);
static preview PNGs are committed next to the model pages.

Each explorer is exported as an *offline* bundle (``--offline``): the Python
runtime, Pyodide, and every dependency wheel ship with the page, so the
explorers boot with no network access and never touch a package index at
runtime. The identical runtime directories (``assets/``, ``pyodide/``,
``packages/``, ``lockfile/``) are shared: the first explorer's copy is moved
to ``docs/_static/interactive/_shared/`` and every ``index.html`` is
rewritten to reference it relatively, so nine explorers cost one runtime.

To add a model: write ``docs/interactive/<name>_explorer.py`` (marimo
notebook, same layout as the others), add an entry to ``MODELS`` below,
and add the iframe section to the model page.
Expand Down Expand Up @@ -103,19 +111,61 @@

def export_wasm(name: str, notebook: Path) -> None:
# marimo's wasm export shells out to `uv`; make sure it's on PATH.
# `--offline` bundles the Python runtime and all dependency wheels with
# the page (needs a headless Chromium: `playwright install chromium`).
bindir = Path(sys.prefix) / ("Scripts" if os.name == "nt" else "bin")
os.environ["PATH"] = str(bindir) + os.pathsep + os.environ["PATH"]
out = STATIC_OUT / name
if out.exists():
shutil.rmtree(out)
subprocess.run(
[sys.executable, "-m", "marimo", "export", "html-wasm",
str(notebook), "-o", str(out), "--mode", "run"],
str(notebook), "-o", str(out), "--mode", "run", "--offline"],
check=True,
)
print(f"exported {notebook.name} -> {out}")


# Runtime directories every --offline export produces. Identical across
# explorers (same marimo version, same dependency set), so they are shared.
OFFLINE_RUNTIME_DIRS = ("assets", "pyodide", "packages", "lockfile")
SHARED = STATIC_OUT / "_shared"


def share_offline_runtime(names: list[str]) -> None:
"""Share one offline runtime across all explorer exports.

Moves the first explorer's runtime directories into ``_shared/`` and
rewrites every explorer's ``index.html`` to reference them via relative
URLs, deleting the per-explorer copies. The model pages' iframes keep
working unchanged: ``<name>/index.html`` still exists, it just loads the
runtime from ``../_shared/``.
"""
first = STATIC_OUT / names[0]
SHARED.mkdir(parents=True, exist_ok=True)
for dirname in OFFLINE_RUNTIME_DIRS:
src, dest = first / dirname, SHARED / dirname
if not src.is_dir():
raise RuntimeError(f"offline runtime missing from export: {src}")
if dest.exists():
shutil.rmtree(dest)
shutil.move(str(src), str(dest))
for name in names:
out = STATIC_OUT / name
html = out / "index.html"
text = html.read_text(encoding="utf-8")
for dirname in OFFLINE_RUNTIME_DIRS:
text = text.replace(f'"./{dirname}/', f'"../_shared/{dirname}/')
if '"./pyodide/' in text or '"./packages/' in text:
raise RuntimeError(f"unrewritten runtime URLs left in {html}")
html.write_text(text, encoding="utf-8")
for dirname in OFFLINE_RUNTIME_DIRS:
d = out / dirname
if d.is_dir():
shutil.rmtree(d)
print(f"shared offline runtime -> {SHARED}")


def preview_png(spec: dict) -> None:
import matplotlib
matplotlib.use("Agg")
Expand Down Expand Up @@ -150,6 +200,7 @@ def main() -> None:
for name, spec in MODELS.items():
export_wasm(name, spec["notebook"])
preview_png(spec)
share_offline_runtime(list(MODELS))


if __name__ == "__main__":
Expand Down
6 changes: 4 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ sphinx-rtd-theme>=2
# sphinxcontrib-mermaid: renders ```mermaid fences as diagrams on the HTML site
sphinxcontrib-mermaid>=0.9
# marimo: exports the interactive model explorers to WebAssembly at build time
# (docs/export_interactive.py). uv is needed on PATH for the export.
marimo>=0.24
# (docs/export_interactive.py). uv is needed on PATH for the export; the
# --offline flag needs a headless Chromium (playwright) plus marimo>=0.25.
marimo>=0.25
playwright
uv>=0.7
Loading