# `/project/scv/examples/imaging` — how the documentation pages here work

## 1. What this directory is

This directory is **served directly to the web**: `/project/scv/examples/imaging/` is published at
`https://rcs.bu.edu/examples/imaging/`. There is no build server, no CI, and no deploy step.
**Writing a file here publishes it.**

`imaging/` is *untracked* in the `bu-rcs/examples` git worktree that `/project/scv/examples` belongs to
(`git ls-files imaging` returns nothing, and `/project/scv/examples/.gitignore` excludes `README.html`).
So there is no commit step either — but also no version history and no safety net. Edit carefully.

Two different documentation patterns exist side by side:

| Pattern | Used by | Shape |
|---|---|---|
| **`README.html`** (RCS house style) | ~35 of 36 packages across `/project/scv/examples` | One hand-written HTML file per package, `<link rel="stylesheet" href="/css/examples.css">` plus `<!--#include virtual="/css/footer.html" -->` |
| **MkDocs site** | `imaging/freesurfer/`, `imaging/mriqc/` | A full static site: sidebar nav, client-side search, multiple pages |

The MkDocs pattern is the better one and is what new imaging packages should use. The rest of this
document describes it.

`imaging/mriqc/` is the **reference implementation**: unlike `freesurfer/`, its markdown sources
*do* exist on this filesystem, at `imaging/.mkdocs_src/mriqc/`. Copy that directory as the starting
point for a new package rather than reverse-engineering `freesurfer/` again.

---

## 2. Anatomy of `imaging/freesurfer/`

Important: **the markdown sources and `mkdocs.yml` for this site do not exist on this filesystem.**
Only the built output was copied in (Aug 2022, with `reconall/` re-copied Feb 2023). The pattern below
was reverse-engineered from that output and confirmed by test builds.

```
freesurfer/
├── index.html            HAND-WRITTEN shim, not MkDocs output (see §9)
├── 404.html              generated
├── search.html           generated
├── sitemap.xml(.gz)      generated
├── getstart/index.html   generated from docs/getstart.md
├── reconall/index.html   generated from docs/reconall.md
├── freeview/index.html   generated — but the body is EMPTY
├── qa/index.html         generated from docs/qa.md
├── appendix/index.html   generated from docs/appendix.md
├── opt/index.html        generated from docs/opt.md — ORPHANED, not in nav
├── css/                  generated by the theme (theme.css, theme_extra.css, fonts/)
├── js/                   generated by the theme (jquery, theme.js, theme_extra.js, html5shiv)
├── search/               generated (lunr.js, main.js, worker.js, search_index.json)
├── img/favicon.ico       generated by the theme
├── images/               HAND-ADDED screenshots referenced by qa.md as ../images/*.png
├── .images/              VESTIGIAL — ~14 MB duplicate of images/
├── .html_backup/         VESTIGIAL — the pre-2022 plain-HTML page + its .qsub files
└── .old/                 VESTIGIAL — older still
```

Generator: **MkDocs** with the built-in **`readthedocs`** theme. Confirmed by the footer
("Built with MkDocs using a theme provided by Read the Docs"), the per-page `mkdocs_page_*` script
block, and `search/lunr.js` + `search_index.json`.

---

## 3. The page skeleton

Every content page is the same theme-generated skeleton. **You do not write this by hand** — MkDocs
emits it. It is documented here as *verification criteria*: if you inspect generated output, this is
what it should look like.

Only six things differ between pages, and all six are derived automatically from `mkdocs.yml` + the
page's own markdown:

| Slot | Derived from |
|---|---|
| `<link rel="canonical">` | `site_url` + the page's slug |
| `<title>` | the page's `<h1>` (or nav title) + ` - ` + `site_name` |
| `mkdocs_page_name` / `mkdocs_page_input_path` / `mkdocs_page_url` | the page |
| Sidebar `<ul>` nav block | `nav:` — the current page's `<li>` gets `class="current"` plus nested `toctree-l2` entries, one per `<h2>` in that page |
| Breadcrumb `<li>` | the page title |
| Prev/next footer buttons + `.rst-versions` bar | the page's position in `nav:` |

Fixed assets every generated page loads: `css/theme.css`, `css/theme_extra.css`, a cdnjs
highlight.js stylesheet + script, `js/jquery-3.6.0.min.js`, `js/html5shiv.min.js` (IE<9 only),
`js/theme_extra.js`, `js/theme.js`, `search/main.js`, a `var base_url` declaration, and a
`SphinxRtdTheme.Navigation.enable(true)` call.

### The `tablesort` scripts are NOT theme output

Every freesurfer page loads two extra scripts:

```html
<script src="https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js" defer></script>
<script src="../javascripts/tablesort.js" defer></script>
```

A test build proves the `readthedocs` theme does not emit these — they came from an
`extra_javascript:` block in the missing `mkdocs.yml`. The second one is the standard MkDocs
tablesort initializer that the author was supposed to create at `docs/javascripts/tablesort.js`
and **never did**, which is why it 404s on every page load (see §9).

If you want sortable tables in a new site, add to `mkdocs.yml`:

```yaml
extra_javascript:
  - https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js
  - javascripts/tablesort.js
```

and **actually create** `docs/javascripts/tablesort.js`. Note the selector must match the
`readthedocs` theme, which wraps page content in `.rst-content` (the snippet in the MkDocs docs
targets `article`, which this theme does not emit):

```js
document.addEventListener("DOMContentLoaded", function () {
  document.querySelectorAll(".rst-content table").forEach(function (table) {
    new Tablesort(table);
  });
});
```

Otherwise omit both lines.

### Copy-to-clipboard buttons on code blocks

Also not theme output. `mriqc/` adds a **Copy** button to every code block from two hand-written
files; copy both from `.mkdocs_src/mriqc/docs/` into a new package and register them:

```yaml
extra_css:
  - css/copybutton.css

extra_javascript:
  - javascripts/copybutton.js
```

Three things about it are load-bearing, and all three cost real debugging to get right:

- **Selector.** `.rst-content pre` — as with tablesort, this theme has no `article` element. Pages
  written with indented or fenced blocks emit plain `<pre><code>`, not `div.highlight`, so do not
  select on a highlight wrapper.
- **The `[scc4]$` prompt must be stripped.** Copying a block verbatim hands the reader the fake
  prompt *and*, for a block that shows a command followed by its output, the output too. The script
  therefore copies **only the prompt lines, with the prompt removed**, and falls back to copying the
  block verbatim when no line in it carries a prompt (an inline qsub script, a directory tree).
  Verified against every block on the mriqc site, including that SGE `#$ -P` directives are *not*
  mistaken for `$` shell prompts.
  - Known limit: a block mixing prompt lines with lines that must also be copied — a shell heredoc —
    would have the non-prompt lines dropped. No such block exists yet.
- **Visible at rest.** The button is a bordered `#2980b9` (the theme accent) button always on the
  page, not a hover-reveal — a control nobody can see is a control nobody uses. Code blocks get
  `padding-right: 5em` so a long first line cannot run underneath it.

The clipboard write uses `navigator.clipboard` with a `document.execCommand` fallback, and a failed
copy flashes "Press Ctrl+C" rather than failing silently.

---

## 4. Toolchain

Do **not** use the `mkdocs/1.3.0` SCC module. Use a dedicated virtualenv.

One-time setup (already done; recorded here so it can be rebuilt):

```
module load python3/3.13.8
python3 -m venv ~/.venv/mkdocs
source ~/.venv/mkdocs/bin/activate
pip install --upgrade pip mkdocs
```

Installed version: **MkDocs 1.6.1** on Python 3.13.8.

Notes:
- The system `/bin/python3` is 3.6.8 — far too old (current MkDocs needs ≥ 3.8). The `python3/3.13.8`
  module is required.
- The venv's `python` is a symlink into the `python3/3.13.8` module tree, so
  `module load python3/3.13.8` **before** `activate` is the reliable recipe.
- PyPI is reachable from SCC login nodes, so `pip install` works without any proxy setup.

---

## 5. Directory layout for a package site

MkDocs **refuses** to build with `site_dir: .` when `docs_dir` sits inside it:

```
ERROR - Config value 'site_dir': The 'docs_dir' should not be within the 'site_dir' as this
        can mean the source files are overwritten by the output or it will be deleted if
        --clean is passed to mkdocs build.
```

So the sources cannot live inside the published package directory. Sources go in a **hidden sibling
directory**, and the build writes *into* the published directory:

```
imaging/
├── .mkdocs_src/               <- SOURCES (hidden; not part of the published site)
│   └── <package>/
│       ├── mkdocs.yml
│       └── docs/
│           ├── index.md
│           ├── getstart.md
│           ├── running.md
│           ├── images/            screenshots
│           └── <package>_example.qsub    downloadable examples
└── <package>/                 <- GENERATED OUTPUT (published, entirely disposable)
    ├── index.html  404.html  search.html  sitemap.xml
    ├── getstart/index.html  running/index.html
    ├── css/  js/  search/  img/
    └── <package>_example.qsub
```

**Everything under `imaging/<package>/` is generated and gets wiped on every rebuild.** Never put a
hand-edited file there. In particular, downloadable `.qsub` / `.m` / `.txt` examples must live in
`docs/` — MkDocs copies unknown file types straight through to the output (verified byte-identical),
so they end up at the published URL *and* survive rebuilds.

---

## 6. `mkdocs.yml` template

```yaml
site_name: BU RCS <Package> Guide
site_url: https://rcs.bu.edu/examples/imaging/<package>/
site_dir: ../../<package>
docs_dir: docs

theme:
  name: readthedocs

nav:
  - Home: index.md
  - Getting Started: getstart.md
  - Running <Package> on the SCC: running.md
  - Checking Your Output: qa.md
  - Appendix: appendix.md
```

`site_url` **must** be the real published URL. FreeSurfer's is typo'd (§9) and that single mistake
corrupts every canonical link and the whole sitemap.

---

## 7. Build and publish

```
module load python3/3.13.8
source ~/.venv/mkdocs/bin/activate
cd /project/scv/examples/imaging/.mkdocs_src/<package>
mkdocs build
```

That is the whole publish process — the output lands in `imaging/<package>/` and is live immediately
at `https://rcs.bu.edu/examples/imaging/<package>/`. No deploy, no git.

Use `mkdocs serve` from the same directory to preview locally while writing.

### Expected divergence from `freesurfer/`

FreeSurfer was built with MkDocs ~1.3 in 2022. MkDocs 1.6.1 produces the same file tree and the same
visual result, but slightly different markup. Verified differences:

| | freesurfer (1.3-era) | MkDocs 1.6.1 |
|---|---|---|
| highlight.js | 10.5.0, `hljs.initHighlightingOnLoad()` | 11.8.0, `hljs.highlightAll()` |
| jQuery | in `<head>`, `defer` | end of `<body>`, no `defer` |
| init call | `window.onload = function () {...}` | `jQuery(function () {...})` |
| breadcrumb | `<li>Page</li>` after a `&raquo;` | `<li class="breadcrumb-item active">` |
| accessibility | `alt="Docs"` | `aria-label="Docs"`, `aria-label` on search input |

New sites will be internally consistent with each other but **not byte-identical to `freesurfer/`**.
That is fine and expected — the goal is matching structure and appearance, not bytes.

One more thing this proves: freesurfer's HTML was **hand-edited after generation**. Its sidebar shows
`AlmaLinux-8-Transition` while the actual heading reads `AlmaLinux 8 Transition`; MkDocs always derives
the sidebar label from the heading, so those cannot disagree in generated output. Do not hand-edit
generated HTML — the next `mkdocs build` destroys it.

---

## 8. Content conventions

Follow the FreeSurfer pages so all imaging docs read alike:

- **Shell prompts** are `[scc4]$` inside fenced/indented code blocks:
  ```
  [scc4]$ module avail freesurfer
  ```
- **Getting Started** opens with the module sequence `module avail <pkg>` →
  `module load <pkg>/<version>`, showing the real output where it is informative. Say explicitly
  which version the page documents, and note when the module default is *not* the newest build.
  - **Do not** include `module spider <pkg>` as a third step. `module avail` already answers the
    same question, and the guides are meant to stay lean. `freesurfer/getstart/` still shows it;
    that page predates this convention and is the odd one out, not the model.
- **Link out** to the RCS modules page
  (`https://www.bu.edu/tech/support/research/software-and-programming/software-and-applications/modules/`)
  and the package's RCS software page (`http://rcs.bu.edu/software/#/package/<pkg>/`).
- **Batch scripts** appear twice: inline in a fenced block so they are readable on the page, *and* as
  a real file in `docs/` so users can `cp` it. Give the copy command explicitly.
- Use tables for version-support matrices and resource-recommendation grids.

---

## 9. Known defects in `freesurfer/` — do not replicate

A checklist for reviewing any new site:

- [ ] `javascripts/tablesort.js` is referenced on every freesurfer page but **does not exist** → 404 on
      every page load. Either create the file or drop the `extra_javascript` entry.
- [ ] `site_url` is typo'd as `https://rcs.bu.edu.freesurfer.readthedocs.io/en/stable/` (dots where
      slashes belong), poisoning every `<link rel="canonical">` and all of `sitemap.xml`.
- [ ] `freeview/index.html` renders with a completely empty body — a page in the nav with no content.
      Two images (`images/freeview_qvgl.PNG`, `images/mobaxterm_vnc.PNG`) were clearly meant for it and
      are referenced by nothing.
- [ ] `opt/index.html` is orphaned: not in the nav, no prev/next, and duplicates a section of
      `reconall/`.
- [ ] `.images/` is a ~14 MB byte-duplicate of `images/`.
- [ ] Two `qa` headings leak stray `</u></font></p>` markup from the pre-MkDocs hand-HTML era.
- [ ] The root `index.html` is a hand-written shim that just links into `getstart/` — no closing
      `</body></html>`, no nav, no search. **New sites use a real `docs/index.md` instead**, which
      gives a proper landing page with the full sidebar and search.
- [ ] Content is pinned to FreeSurfer 7.3.2 while `job_scripts/` uses 7.4.1 and `tut_freesurfer/` uses
      8.0.0. Keep versions consistent and current.

---

## 10. Source material for the packages being documented

Existing material on disk to draw on. **None of these tutorial directories are referenced from any web
page** — they are workshop material that has never been surfaced publicly.

| Package | Material |
|---|---|
| **fmriprep** | `tut_dataprep_scc/03_fmriprep.qsub` (array job, v24.1.1), `tut_dataprep_scc/notes.txt`, `tut_fmriprep_scc/notes.txt`, `tut_fmriprep_scc/fmriprep_1.4.pdf` |
| **mriqc** | `tut_dataprep_scc/02_mriqc.qsub` (v22.0.6), `tut_dataprep_scc/notes.txt` |
| **spm** | *nothing on disk* — content must be written from scratch |
| **conn** | `tut_conn/setup.sh`, `tut_conn/batch_scripts/batch_example.m` (SGE `parallel.profile`), `tut_conn/CONN_Workshop.pptx` |

Also useful: `job_scripts/` (FreeSurfer qsub examples incl. an array-job version) and
`tut_dataprep_scc/` generally, which covers the DICOM → NIfTI → BIDS → MRIQC → fMRIPrep pipeline end
to end.

### SCC module versions

As of 2026-09-09 (`(D)` = default when no version is given):

| Package | Versions |
|---|---|
| fmriprep | v20.2.5, v21.0.1, 22.0.2, 23.1.4, **23.2.3 (D)**, 24.1.1, 25.2.5 |
| mriqc | **22.0.6 (D)**, 24.0.0 |
| spm | 8, 12b, 12, 12.7771, **25.01 (D)**, 25.01.02 |
| conn | 17.f, 18.b, 21.a, 22.a, **22v2407 (D)**, 25b |

Note the defaults lag the newest builds for every package. Say explicitly which version a page
documents, and prefer pinning an explicit version in every example (`module load fmriprep/25.2.5`)
rather than relying on the default.
