# Marp Processor

## Purpose

Converts Markdown slides to PDF, PPTX, or HTML using [Marp](https://marp.app/).

## How It Works

Discovers `.md` files in the project and runs `marp` on each file, generating
output in the configured formats. Each format produces a separate output file.

Each marp invocation spawns a headless Chromium browser instance via Puppeteer
to render the slides. This makes marp significantly more resource-intensive than
typical processors — see [Concurrency limiting](#concurrency-limiting) below.

## Source Files

- Input: `**/*.md`
- Output: `out/marp/{format}/{relative_path}.{format}`

## Configuration

```toml
[processor.marp]
marp_bin = "marp"                      # The marp command to run
formats = ["pdf"]                      # Output formats (pdf, pptx, html)
args = ["--html", "--allow-local-files"]  # Additional arguments to pass to marp
output_dir = "out/marp"                # Output directory
extra_inputs = []                      # Additional files that trigger rebuilds when changed
max_jobs = 2                           # Limit concurrent marp instances (each spawns Chromium)
```

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `marp_bin` | string | `"marp"` | The marp executable to run |
| `formats` | string[] | `["pdf"]` | Output formats to generate (`pdf`, `pptx`, `html`) |
| `args` | string[] | `["--html", "--allow-local-files"]` | Extra arguments passed to marp |
| `output_dir` | string | `"out/marp"` | Base output directory |
| `extra_inputs` | string[] | `[]` | Extra files whose changes trigger rebuilds |
| `max_jobs` | integer | none | Max concurrent marp processes. See [Concurrency limiting](#concurrency-limiting). |

## Concurrency Limiting

Each marp invocation launches a full headless Chromium browser process, which
consumes hundreds of megabytes of RAM. When running parallel builds with `-j N`,
too many simultaneous Chromium instances cause resource exhaustion and
non-deterministic crashes:

```
TargetCloseError: Protocol error (Target.setDiscoverTargets): Target closed
```

Use `max_jobs` to limit how many marp processes run concurrently, independent of
the global `-j` setting. For example, with `-j 20` and `max_jobs = 2`, at most
2 Chromium instances will be alive at once while other processors still use the
full 20 threads:

```toml
[processor.marp]
formats = ["pdf"]
max_jobs = 2
```

**Recommended value:** `2`. A value of 4 may work on machines with plenty of RAM
but has been observed to produce occasional failures on large projects (700+ slides).
Without `max_jobs`, the global `-j` value applies, which typically causes crashes
at higher parallelism levels.

## Batch Support

Each input file is processed individually, producing its own output file.

## Temporary Files

Marp creates temporary Chromium profile directories (`marp-cli-*`) in `/tmp` for
each invocation. RSConstruct automatically cleans these up after each marp process
completes, since marp itself does not delete them.
