You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: link contents to .ipynb and expand LaTeX macros before conversion
- Point the "Contents" and "Exercise Answers" tables in basic_lessons/README.md
at the generated .ipynb files instead of the .md sources, so readers land on
the executable notebooks.
- Add convert_to_ipynb.py: a two-step .md -> intermediate.md -> .ipynb pipeline
that first expands the project.math LaTeX macros (myvec/mymatrix/quat/dual)
into an intermediate .md, then runs jupytext on that file. Most .ipynb
renderers cannot draw custom macros, so the generated notebooks now contain
only standard LaTeX and render anywhere. The intermediate file is a transient
build artifact (deleted after conversion; --keep retains it).
Expansion mirrors KaTeX/TeX macro semantics (braced args incl. nested
braces, single space-separated tokens, no-arg macros) and leaves
{code-cell} bodies untouched so Python source is never corrupted.
- Wire the script into the CI "Generate downloadable notebooks" step.
- Document the new pipeline in AGENTS.md.
Co-authored-by: openhands <openhands@all-hands.dev>
|`build_html.sh`| Build script for `jupyter-book`|
@@ -59,18 +60,24 @@ x = np.array([1, 2, 3])
59
60
60
61
### Downloadable `.ipynb` from `.md` notebooks
61
62
62
-
The `basic_lessons/``.md` files are the canonical source. `.ipynb` files are generated at build time so visitors can download them:
63
+
The `basic_lessons/``.md` files are the canonical source. `.ipynb` files are generated at build time so visitors can download them. The conversion is a **two-step pipeline** run by `convert_to_ipynb.py`:
63
64
64
-
1.**CI pipeline** (`.github/workflows/notebook_to_html.yml`) runs `jupytext --from md:myst --to notebook` before the MyST build, converting each `basic_lessons/*.md` → `basic_lessons/*.ipynb`.
65
-
2.**`myst.yml` TOC** references the generated `.ipynb` for the lesson section — MyST renders these identically to the `.md` but provides native "Download notebook" buttons.
66
-
3.**`basic_lessons/.gitignore`** excludes `.ipynb` so only `.md` is tracked in git.
1.**CI pipeline** (`.github/workflows/notebook_to_html.yml`) runs `python convert_to_ipynb.py` before the MyST build.
70
+
2.**Why the intermediate step?** The lessons use custom LaTeX macros (e.g. `\myvec{q}`) that MyST expands at build time by passing them to KaTeX. Most standalone `.ipynb` renderers (JupyterLab, VS Code, nbviewer, ...) do **not** know these macros and would show them literally. So `convert_to_ipynb.py` first expands every macro to its definition from `myst.yml` (`project.math`), writing a temporary `<stem>_expanded.md`; `jupytext` then converts that expanded file to `.ipynb`. The generated `.ipynb` therefore contains **only standard LaTeX** and renders anywhere. You keep writing the original `.md` with the convenient macros.
71
+
3.**`myst.yml` TOC** references the generated `.ipynb` for the lesson section — MyST renders these identically to the `.md` but provides native "Download notebook" buttons.
72
+
4.**`basic_lessons/.gitignore`** excludes `.ipynb` so only `.md` is tracked in git.
73
+
74
+
The intermediate `<stem>_expanded.md` files are also build artifacts — deleted after conversion (use `--keep` to inspect them). They are not tracked and not referenced in `myst.yml`.
@@ -142,7 +147,7 @@ warning classes seen so far (all fixed in #11):
142
147
### CI/CD
143
148
144
149
The GitHub Actions workflow (`.github/workflows/notebook_to_html.yml`) runs on pushes to `main` and on pull requests:
145
-
1. Generates `.ipynb` from `.md`using jupytext
150
+
1. Generates `.ipynb` from `.md`via `python convert_to_ipynb.py` (expanding LaTeX macros into an intermediate `.md` first)
146
151
2. Runs `./build_html.sh` (jupyter-book pipeline)
147
152
3. Uploads `_build/html/` as Pages artifact
148
153
4. Deploys to GitHub Pages
@@ -174,6 +179,12 @@ Thank you! Please report it at https://github.com/MarinhoLab/OpenExecutableBooks
174
179
-`\quat{}` for quaternions
175
180
-`\dual{}` for dual numbers
176
181
182
+
These are defined in `myst.yml` under `project.math` (KaTeX `#1` substitutions) and
183
+
are expanded by MyST when building the site. Use them freely in the `.md` source —
184
+
`convert_to_ipynb.py` expands them into the downloadable `.ipynb` so the notebooks
185
+
also render in standalone viewers. **Code cells are left untouched** (a macro name
186
+
in a Python comment/variable is never expanded).
187
+
177
188
### Image references:
178
189
- Use relative paths: `` (relative to `basic_lessons/`)
179
190
- Images live alongside the lesson files in `basic_lessons/`.
@@ -193,6 +204,7 @@ Thank you! Please report it at https://github.com/MarinhoLab/OpenExecutableBooks
193
204
194
205
### Files excluded from version control:
195
206
-`basic_lessons/*.ipynb` — Generated at build time from `.md` files (via `basic_lessons/.gitignore`)
207
+
-`basic_lessons/*_expanded.md` — transient intermediate files from `convert_to_ipynb.py` (deleted after conversion; `--keep` retains them for inspection)
196
208
197
209
There is intentionally no root `.gitignore`. If you create local `venv/` or `_build/`
198
210
directories, keep them out of commits (e.g. via `.git/info/exclude`).
@@ -207,6 +219,6 @@ directories, keep them out of commits (e.g. via `.git/info/exclude`).
207
219
4. Use `{code-cell}` directives for Python code blocks
208
220
5. Use `%%capture` on `%pip install` cells to suppress output
209
221
6. Update `myst.yml` — add new file(s) to `project.toc` list as `.ipynb` (generated at build time)
210
-
7. Update `basic_lessons/README.md` — add the new lesson to the contents table
211
-
8. Test: `./build_html.sh` from the repository root
222
+
7. Update `basic_lessons/README.md` — add the new lesson to the contents table, linking to the generated `.ipynb` (not the `.md`)
223
+
8. Test: run `python convert_to_ipynb.py` to regenerate the `.ipynb`, then `./build_html.sh` from the repository root
| 0 |[](./lesson0_tutorial.md)| Setting up the virtual environment and installing all required dependencies. |
19
-
| 1 |[](./lesson1_tutorial.md)| Basic operations in Python and `numpy`|
20
-
| 2 |[](./lesson2_tutorial.md)| Learn about elements and operations in $\mathbb{R}^n$, $SO(n)$, and $SE(n)$ with $n\in{\{2,3\}}$ related to positions, orientations, and poses, respectively. |
21
-
| 3 |[](./lesson3_tutorial.md)| Learn about the composition of rigid body motion in series to obtain the forward kinematics model of a robotic manipulator, mapping their configuration space $\myvec{q}\in\mathbb{R}^n$ into their task space $\myvec{x}\in\mathbb{R}^m$. |
22
-
| 4 |[](./lesson4_tutorial.md)| Learn about the first-order differential mapping $\dot{\myvec{x}}=\mymatrix{J}\dot{\myvec{q}}$ between joint space and task space velocities through the calculation of the Jacobian $\mymatrix{J}$. |
23
-
| 5 |[](./lesson5_tutorial.md)| Employ the previous knowledge in all previous lessons to employ a Lyapunov-stable control law to move a manipulator in task space using configuration-space signals. |
18
+
| 0 |[](./lesson0_tutorial.ipynb)| Setting up the virtual environment and installing all required dependencies. |
19
+
| 1 |[](./lesson1_tutorial.ipynb)| Basic operations in Python and `numpy`|
20
+
| 2 |[](./lesson2_tutorial.ipynb)| Learn about elements and operations in $\mathbb{R}^n$, $SO(n)$, and $SE(n)$ with $n\in{\{2,3\}}$ related to positions, orientations, and poses, respectively. |
21
+
| 3 |[](./lesson3_tutorial.ipynb)| Learn about the composition of rigid body motion in series to obtain the forward kinematics model of a robotic manipulator, mapping their configuration space $\myvec{q}\in\mathbb{R}^n$ into their task space $\myvec{x}\in\mathbb{R}^m$. |
22
+
| 4 |[](./lesson4_tutorial.ipynb)| Learn about the first-order differential mapping $\dot{\myvec{x}}=\mymatrix{J}\dot{\myvec{q}}$ between joint space and task space velocities through the calculation of the Jacobian $\mymatrix{J}$. |
23
+
| 5 |[](./lesson5_tutorial.ipynb)| Employ the previous knowledge in all previous lessons to employ a Lyapunov-stable control law to move a manipulator in task space using configuration-space signals. |
0 commit comments