This file gives coding agents the repo map, working boundaries, and commands that matter.
# Full local gate
make check
# Lint, format check, and type-check only
make lint
# Tests only
make test
# Build the package
make build
# Regenerate only the widget bundle
make widget-build
# Build docs as static HTML
make book-build
# Serve docs locally
make book-start
This is a Jupyter Book executable plugin that turns MyST-native {marimo} directives
into hydrated marimo islands.
Jupyter Book invokes the jupyter-book-marimo console script as a MyST executable
plugin. The plugin advertises one document transform, marimo-islands, two directives
(marimo and marimo-config), and replaces supported marimo nodes with anywidget
nodes.
The transform:
- Scans the MyST document for internal
marimoCellnodes emitted by the{marimo}directive. - Reads page-level metadata from the
{marimo-config}directive. - Executes the collected cells through
runtime.py. - Copies
container-widget.mjsinto.jupyter-book-marimo/so Jupyter Book can serve the anywidget bridge as a same-origin ESM asset. - Attaches custom stylesheet hrefs or embedded CSS blocks to the widget model.
- Emits a MyST
anywidgetnode for each marimo output.
The extractor owns marimo execution. It builds a MarimoIslandGenerator, runs the page
to a final state, captures runtime assets, and returns one output model per executable
cell.
The browser runtime and the server export do not share generated cell IDs, so the
extractor rewrites islands to use browser cell indexes and injects a stable page-local
CellManager prefix into the exported notebook code.
Jupyter Book renders anywidgets in a shadow root. marimo islands expect light DOM and a hidden notebook source node. The container widget is the boundary between those systems and is served as a same-origin bridge asset. marimo's island runtime URLs still come from marimo's generated island head.
The generated .jupyter-book-marimo/ directory is only the served copy of that bridge.
The maintained source is TypeScript under the top-level widget/ Deno project, and the
packaged src/jupyter_book_marimo/assets/container-widget.mjs file is a generated Deno
bundle.
It mounts the marimo output into light DOM, retains one notebook source node per app, loads marimo island assets once, installs plugin styling, mirrors theme state into nested shadow roots, and forces document navigation for same-origin page changes so Jupyter Book does not reuse a stale marimo runtime bridge.
Authoring concerns live in one small parser module. It validates directive payloads, rejects unsupported options, and normalizes execution options shared by the plugin and extractor.
Page-level dependencies live in the {marimo-config} directive's pyproject option.
runtime.py converts that TOML block into uv run arguments by reusing marimo's
sandbox parsing, so the plugin has one dependency parser path.
Use MyST directives with an explicit cell language:
```{marimo} python
import marimo as mo
mo.md("hello")
```Supported languages are exactly python, sql, and markdown. Options use MyST
directive syntax: eval, echo, editor, hide-code, hide-output, output,
server-output, error, disabled, unparsable, include, name, and column.
SQL-specific options are query for the output variable and engine for the marimo SQL
engine object.
Page-level metadata is written in a {marimo-config} directive:
```{marimo-config}
---
header: |
# Python inserted before exported notebook code
eval: true
output: true
server-output: true
error: true
pyproject: |
requires-python = ">=3.10"
dependencies = ["pandas"]
---
```jupyter-book-marimo/
├── src/jupyter_book_marimo/
│ ├── plugin.py # MyST executable plugin entrypoint
│ ├── authoring.py # directive validation and execution options
│ ├── extract.py # marimo execution and island export
│ ├── runtime.py # subprocess extraction and uv sandbox execution
│ └── assets/container-widget.mjs # generated Deno bundle packaged at runtime
├── widget/ # TypeScript source for the anywidget bridge
├── scripts/bundle_widget.py # Deno bundle writer for the packaged bridge
├── tests/ # pytest unit tests
├── docs/ # Jupyter Book docs application surface
├── Makefile
└── pyproject.toml
- Keep generated output out of commits:
dist/,docs/_build/,docs/_site/, anddocs/.jupyter-book-marimo/are build artifacts. - Prefer
make checkbefore handoff. It runs Python and Deno format checks, lint, type-checks, tests, widget bundle generation, package build, and a strict docs build. make buildrunsmake widget-buildbeforeuv build.- Use
make widget-buildwhen you only need to refreshsrc/jupyter_book_marimo/assets/container-widget.mjs. - Use
make book-buildafter changes to docs, MyST parsing, runtime assets, or browser hydration behavior. - Do not edit
docs/tutorials/by hand. Those pages are generated from upstream tutorial content. Keep repo-owned docs work indocs/api/,docs/index.md,docs/myst.yml, anddocs/styles/. - If browser behavior changes, verify the built book with
agent-browseror an equivalent real browser run. Static tests are not enough for the widget/theme boundary.