# Docusaurus

Export a Docusaurus site to one PDF at build time: a plugin, a key in the environment, a link on the site.

Page: https://legivel.com/docs/integrations/docusaurus

The plugin reads the docs sidebars, collects the pages in sidebar order, joins them into one document with a cover and a contents list, and sends it to the legivel API. The PDF lands in the build output next to `index.html`, so the site can link it.

Each PDF carries the Markdown it was made from, so AI agents can read it exactly. The same Markdown is saved beside the PDF (see `source`).

## Setup

1. ### Create an API key

   On the [API keys page](https://legivel.com/account/api-keys). A Free account includes 100 documents a month.
2. ### Install the plugin

   ```bash
   npm install --save-dev docusaurus-plugin-legivel
   ```
3. ### Add it to the config

   docusaurus.config.js

   ```javascript
   // docusaurus.config.js
   module.exports = {
     // …
     plugins: [["docusaurus-plugin-legivel", { filename: "handbook.pdf" }]],
   };
   ```
4. ### Build with the key in the environment

   ```bash
   LEGIVEL_API_KEY="lgvl_your_key_here" npm run build
   ```

   The key is never an option: a key in the config is refused with a message that says where it belongs.
5. ### Link the file from the site

   For example a navbar item. The plugin's own «Download PDF» button is on by default (see `button` below).

   docusaurus.config.js

   ```javascript
   themeConfig: {
     navbar: {
       items: [
         {
           to: "/handbook.pdf",
           label: "Download the PDF",
           position: "right",
           target: "_blank",
         },
       ],
     },
   },
   ```

Every PDF rendered counts as one document of the account's plan: the manual on every build (see `rebuild`), each module on every build, each page PDF only when it changed. The build log ends with the count.

## Options

Every option is optional. A wrong value stops the build with a sentence that names it.

| Name | Type | Description |
| --- | --- | --- |
| `filename` | string | The file name in the build output. No folder, must end in `.pdf`. The default is `docs.pdf`. |
| `sections` | string[] | Sidebar ids, or doc id prefixes such as `guides/`, to include. Every sidebar when omitted. |
| `exclude` | string[] | Doc ids, or doc id prefixes, to leave out. A page with `pdf: false` in its front matter stays out too. |
| `version` | string | The docs version to export, by name (`current`) or label (`2.0`), for a versioned site. The site's default when omitted. |
| `locales` | string[] | The locales that get a PDF. Docusaurus builds each locale on its own; each build writes its PDF into its own output. Every locale when omitted. |
| `pageBreaks` | string | `chapters`, the default, starts a new page before each top-level sidebar entry; `pages` before every page. |
| `modules` | boolean \| string[] | `true`: one PDF per top-level sidebar entry beside the manual, as `handbook-<entry>.pdf`; or a list of the entries wanted, by category label or doc id. Off by default. |
| `pages` | boolean | `true`: one PDF per page under `handbook/<doc id>.pdf`, rendered again only when the page or a setting changed since the last build. Off by default. |
| `themes` | string[] | `["light", "dark"]` renders both; the dark files carry `-dark`. The button serves the one the visitor reads in. The default is `["light"]`. |
| `button` | boolean | A «Download PDF» button at the top of every docs page: the page's own PDF when there is one, else its module's, else the manual. On by default. |
| `credit` | boolean | The line «PDF made with legivel» under the button, linking to legivel.com. A Free account keeps it; a paid plan may set `false`. The PDF itself carries no credit. |
| `cover` | boolean \| object | `true`, the default, for the site title and tagline; `false` for none; or `{ title, subtitle }`. |
| `page` | object | The page settings of the API under their own names: `size` (`A4`, `Letter`, `A5 landscape`, `210mm 297mm`), `margin` (CSS order), `header`, `footer`, `numbering`, `first`. See the [render reference](https://legivel.com/docs/api/render). |
| `features` | object | The feature switches of the API under their own names: `theme` (`light` or `dark`), `mermaid`, `math`, `archive`, `accessible`, `hyphenation`, … |
| `resources` | string | `lenient`, the default, leaves a missing image out; `strict` fails the build on it. |
| `language` | string | A language tag such as `en` or `de`; sets hyphenation and is written into the PDF. The site's locale when omitted. |
| `sign` | object | The `sign` setting of the API: seals the PDF with the account's certificate. See [Sealing](https://legivel.com/docs/sealing). |
| `rebuild` | boolean | `true`, the default, renders on every build, so the PDF never goes stale. `false` renders only when a page or a setting changed since the last build and reuses the last PDF otherwise. |
| `source` | boolean | Attaches the Markdown to the PDF as `document.md` (readable by AI agents and by any PDF reader's attachments pane) and saves it beside the PDF, as `handbook.md` next to `handbook.pdf`. On by default. |
| `docsPluginId` | string | The docs plugin instance to export when a site has several. The default is `default`. |
| `keyEnv` | string | The environment variable that holds the key. The default is `LEGIVEL_API_KEY`. |

Defaults for a manual, where the config sets nothing else: the page number reads «Page X of Y», in the footer center or beside the footer text; the cover carries no header, footer or number; the language is the site's locale.

A fuller config:

docusaurus.config.js

```javascript
plugins: [
  [
    "docusaurus-plugin-legivel",
    {
      filename: "handbook.pdf",
      sections: ["docs"],
      modules: true,
      themes: ["light", "dark"],
      cover: { title: "Product handbook", subtitle: "Built from the docs" },
      page: { size: "A4", footer: "Product handbook" },
      features: { math: true, archive: true },
      language: "en",
    },
  ],
],
```

## Free accounts

A Free account renders up to 20 pages per document. When the manual is longer, the plugin renders the first chapters that fit (a chapter is a top-level sidebar entry with its children) and ends the PDF with a short note that the full manual needs a Starter plan. The build log says the same, with the page count. The tries that are refused for their length spend no document; the one PDF does.

## What the PDF contains

- The cover, when on, on a page of its own: the title, the subtitle, the version on a versioned site, the site address and the build date.
- A contents list with page numbers on the next page, and the PDF's bookmarks, both following the sidebar tree: a top-level entry is a chapter, its children sit under it. A category that links to a page puts that page first; a category without one is a heading of its own, with its generated index's description.
- Each chapter on a new page, in sidebar order, the page's own headings one level below its title. Drafts, unlisted pages and excluded pages stay out.
- Markdown as written: paragraphs, lists, tables, code blocks with their titles, diagrams in `mermaid` fences, math, images. See [What renders](https://legivel.com/docs/markdown).
- Every link works in the PDF: a link to a page of the export, or to a part of the same page, goes to its heading; a link to anything else on the site (a page left out, a blog post, another docs instance, a relative path) carries the full address on the site; a link to another website stays as written.
- Images with a path relative to the file, or a site path under `static/`, embedded from disk at build time, up to the plan's resource allowance per document (nine tenths of it; the rest are left out with their alt text and one line in the log). A site path not on disk is loaded from the site's `url`. Should the document still be refused for its size, the plugin halves the image budget and sends it again, down to no images; a refused try spends no document.
- The Markdown the PDF was made from, attached inside the PDF and saved beside it (`source: false` leaves both out).

## What becomes Markdown, and what is left out

Docusaurus pages are MDX. What has a plain reading becomes Markdown; the rest is left out and named in the build log with its file:

- `<Tabs>`: every tab's content stays, one tab after another, each under its label in bold.
- A partial imported from a Markdown file (`import Part from './_part.mdx'`, placed as `<Part />`) is read in place.
- `<details>` becomes its summary in bold and its text.
- An `<iframe>` (a sandbox, a video) cannot play on paper: it becomes one line, «Embedded content:» and a link to its address, named by its `title` or the host. The log names each one.
- A JSX attribute on an HTML tag (`<img src={'…'} width={200} style={{…}} />`) becomes a plain one, or goes when it holds an object or an expression.
- `:::note`, `:::tip`, `:::info`, `:::warning`, `:::danger`: a quoted block whose first line is the title, or the kind, in bold.
- A code block's `title="…"` becomes a bold line above the block; `showLineNumbers` and line marks go, the language stays.
- `import` and `export` lines are removed.
- Any other component (`<MyWidget />`, a JSX block) is removed with its content; the log names the component and the file.

Every change appears in the build log as `[legivel-pdf] <doc id> (<file>): …`.

## Errors

A failure stops the build with the API's own sentence and code, for example:

```text
docusaurus-plugin-legivel: legivel answered 401 unauthorized: The API key is missing or not valid. Send it as "Authorization: Bearer <key>". Check the key in the environment variable; it starts with lgvl_ and is shown once on the API keys page.
```

The key never appears in the log, in an error or in the output. The codes are the API's, listed under [Errors and quota](https://legivel.com/docs/errors).

## Requirements

Docusaurus 3 with `@docusaurus/plugin-content-docs` (checked on 3.1.0 and 3.10.2), Node 18 or newer. `LEGIVEL_PDF_DEBUG=1` on the build keeps every document the plugin sends under `.docusaurus/legivel-pdf/debug/`.
