diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 777da2c..2d822a7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -33,10 +33,16 @@ repos:
hooks:
- id: nbstripout
+ - repo: https://github.com/rvben/rumdl-pre-commit
+ rev: "5c0245ee573b1dea03e8cdc5bd41bd449ea8962a" # frozen: v0.2.27
+ hooks:
+ - id: rumdl-fmt
+
- repo: https://github.com/rbubley/mirrors-prettier
rev: "515f543f5718ebfd6ce22e16708bb32c68ff96e1" # frozen: v3.8.3
hooks:
- id: prettier
+ exclude: '\.md$' # Markdown is handled by rumdl
- repo: https://github.com/codespell-project/codespell
rev: "2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a" # frozen: v2.4.2
diff --git a/.rumdl.toml b/.rumdl.toml
new file mode 100644
index 0000000..2147237
--- /dev/null
+++ b/.rumdl.toml
@@ -0,0 +1,12 @@
+[global]
+# This is prose-heavy content, not source-tree Markdown.
+disable = [
+ "MD013", # Line length — prose and code samples are not hard-wrapped
+ "MD033", # Inline HTML — Marp slides use
etc.
+ "MD026", # Trailing punctuation in headings — slide headings intentionally end in ":"
+ "MD076", # Blank line between list items — false-positives inside mixed-indent MyST :::{card}
+]
+
+# content/ is MyST (directives, roles); slides/ are Marp (standard Markdown + HTML).
+[per-file-flavor]
+"content/**/*.md" = "myst"
diff --git a/AGENTS.md b/AGENTS.md
index c770750..048455f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -14,7 +14,7 @@ bun run build # build book + slides into _build/html/ (build-book then b
bun run build-book # myst build --html
bun run build-slides # marp slides/ -> _build/html/slides/
bun run clean # rm -rf _build
-prek -a --quiet # lint/format everything (ruff-format, blacken-docs, prettier, codespell, etc.)
+prek -a --quiet # lint/format everything (ruff-format, blacken-docs, rumdl, prettier, codespell, etc.)
```
## Structure
diff --git a/README.md b/README.md
index 5e4675f..b4842a4 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,4 @@
This workshop is a work in progress. Check back soon!
-See https://scikit-build.org/events/simple-py/ for info on the workshop!
+See for info on the workshop!
diff --git a/content/basic-packaging/01_setup.md b/content/basic-packaging/01_setup.md
index 37d8b0b..3d9858f 100644
--- a/content/basic-packaging/01_setup.md
+++ b/content/basic-packaging/01_setup.md
@@ -65,6 +65,7 @@ When Python runs, it checks to see if there's a `pyvenv.cfg` above it. If there
it is in a virtual environment (venv) and reads site-packages from there. There are two ways to use it:
::::{tab-set}
+
:::{tab-item} Direct usage
```bash
@@ -72,6 +73,7 @@ it is in a virtual environment (venv) and reads site-packages from there. There
```
:::
+
:::{tab-item} Activation
```bash
@@ -81,6 +83,7 @@ deactivate
```
:::
+
::::
> [!WARNING]
@@ -95,7 +98,9 @@ The `.` at the start (most shells support `source` as well) allows the activatio
To create one of these, you have several options:
:::::{card} Create a virtual environment
+
::::{tab-set}
+
:::{tab-item} venv
```bash
@@ -105,6 +110,7 @@ python3 -m venv .venv
This is the slowest, but it's built in![^1]
:::
+
:::{tab-item} virtualenv
```bash
@@ -114,6 +120,7 @@ virtualenv .venv
This is faster than `venv`, has better default installs inside, but does require installation.
:::
+
:::{tab-item} uv
```bash
@@ -123,7 +130,9 @@ uv venv
This is really fast, though it's completely empty (no pip). And it defaults to `.venv`. Also requires installation (a single Rust binary or pip install).
:::
+
::::
+
:::::
We will be using `uv`, which can do a lot of this for us.
@@ -137,7 +146,9 @@ Now that you know how to make virtual environments, how should you install stuff
But a virtual environment is meant to be expendable. You should be able to delete it and recreate it any time. So instead of manually installing, you want to list packages in some format:
:::::{grid} 1 1 2 2
+
::::{grid-item}
+
:::{card} Project (app)
These are for making a virtual env. They don't affect libraries.
@@ -148,8 +159,11 @@ These are for making a virtual env. They don't affect libraries.
- **Lock file**: Versions are pinned exactly
- **dependency-groups**: Multiple collections of packages
:::
+
::::
+
::::{grid-item}
+
:::{card} Package (library)
These are for libraries.
@@ -164,7 +178,9 @@ Most libraries also have developer environments, which follows the "Project
> for these too; this is due to it pre-dating `dependency-groups`.
:::
+
::::
+
:::::
Locked dependencies means that every dependency is fully specified, ideally
@@ -296,6 +312,7 @@ if __name__ == "__main__":
When you run it:
::::{tab-set}
+
:::{tab-item} uv
```bash
@@ -303,6 +320,7 @@ uv run single.py
```
:::
+
:::{tab-item} pipx
```bash
@@ -310,6 +328,7 @@ pipx run single.py
```
:::
+
::::
The dependencies will be downloaded into a temporary venv.
diff --git a/slides/1_01_setup.md b/slides/1_01_setup.md
index 35cebef..31135f6 100644
--- a/slides/1_01_setup.md
+++ b/slides/1_01_setup.md
@@ -74,7 +74,7 @@ System or user installs sound nice, but:
## Virtual environment structure
-```
+```text
.venv
├── .gitignore
├── CACHEDIR.TAG