# Configuration reference

> Every option in the starlight-llm-actions plugin config.

Every option you can pass to `starlightLlmActions()`. All options are optional; calling it with no arguments uses the defaults shown below.

```ts
starlightLlmActions({
  // top-level options shown here
})
```

## Top-level options

### `actions`

* **Type:** [`ActionsConfig`](#actionsconfig)
* **Default:** all on except `printPdf` (off); five built-in providers enabled by default — see [Providers](/starlight-llm-actions/configuration/providers/#defaults-at-a-glance)

Controls which dropdown items render and how the **Open in…** submenu behaves. See [`ActionsConfig`](#actionsconfig) below.

```js
starlightLlmActions({
  actions: {
    copyMarkdown: true,
    viewMarkdown: true,
    printPdf: false,
    openIn: true,
  },
})
```

### `markdownUrl`

* **Type:** `string`
* **Default:** `'/{slug}.md'`

URL template the plugin uses for the page’s markdown source. `{slug}` is substituted with the page’s path segments. The template must start with `/` and contain `{slug}`; anything else fails the build with a message naming the value it got.

The leading slash is what lets the plugin join the template onto Astro’s `base` — a site served from `/docs/` gets `/docs/guides/example.md`. Write the path as it appears after the base, not relative to the current page.

The home page is the one page whose path segments are not its entry id: its URL is `/`, so it has none, while its entry id is `index`. When a separator follows the placeholder, the root substitutes to nothing and the separator goes with it, so `'/{slug}/index.md'` puts the home page’s Markdown at `/index.md` rather than at `/index/index.md`. With `'/{slug}.md'` there is no way to write zero segments before a bare extension, so the root keeps `index` and still resolves to `/index.md`.

The default route is injected automatically. Sites that publish raw text under a different extension can override:

```js
starlightLlmActions({
  markdownUrl: '/{slug}.txt',
})
```

If your site already publishes per-page markdown via a different route, set [`injectRoute: false`](#injectroute) and point `markdownUrl` at it.

### `injectRoute`

* **Type:** `boolean`
* **Default:** `true`

Whether the plugin should inject its own markdown route at the pattern derived from `markdownUrl`. Set to `false` if your site already publishes per-page markdown (e.g. via a custom HTML→markdown pipeline) and you only want the dropdown UI.

```js
starlightLlmActions({
  markdownUrl: '/raw/{slug}',
  injectRoute: false,
})
```

### `collections`

* **Type:** `(string | CollectionConfig)[]`
* **Default:** `['docs']`

The Content Collections the plugin publishes Markdown for. Every collection listed here gets a `.md` route per entry, a line in `llms.txt`, and its pages in `llms-full.txt`; a page in a collection *not* listed here gets none of that, and the Page Actions dropdown leaves out its Markdown items rather than pointing them at a URL that does not exist.

A bare string names a collection whose entry ids already are its site paths, which is true of Starlight’s own `docs`. Anything else needs the object form:

```ts
type CollectionConfig =
  | string
  | {
      name: string;
      path?: string;  // default: '{id}'
    };
```

| Field  | Required | Description                                                                                   |
| ------ | -------- | --------------------------------------------------------------------------------------------- |
| `name` | yes      | Collection name, as declared in `src/content.config.ts`.                                      |
| `path` | no       | Site path for one entry, with `{id}` standing for the entry id. No leading or trailing slash. |

```js
starlightLlmActions({
  collections: [
    'docs',
    { name: 'changelog', path: 'changelog/entry/{id}' },
  ],
})
```

#### Why `path` exists

Starlight derives a `docs` page’s URL from its entry id, so the two are the same string. A collection with its own route file is under no such obligation: a `changelog` collection whose entries are `0-11-0` and `0-12-0` might serve them at `/changelog/entry/0-11-0`, and `path` is how you say so.

Getting it wrong is what the `.md` convention cannot survive — Markdown at `/changelog/0-11-0.md` for a page published at `/changelog/entry/0-11-0/` is a URL no agent will guess. The template is validated at config time: it must contain `{id}` exactly once and carry no surrounding slashes.

The resulting path is what every other option works in. `markdownUrl` is built from it, and the globs in [`llmsTxt`](#llmstxtconfig) match it — so a changelog entry is `changelog/entry/*`, not `0-*`.

#### What a collection needs

Two things, neither of them enforced by the plugin:

* **A `title` in its schema.** It becomes the `#` heading of the page’s Markdown and its label in every index. A collection without one fails the build with the offending entry named.
* **A route that renders it.** The plugin publishes the Markdown twin of an HTML page; it does not create the HTML page. A collection with no route file gets `.md` files pointing nowhere.

A `draft: true` entry is skipped, in this collection as in `docs`. A schema with no `draft` field simply has no drafts to skip.

### `renderMarkdown`

* **Type:** `'raw' | 'simple' | { module: string }` — see [`RenderMarkdownConfig`](#rendermarkdownconfig)
* **Default:** `'raw'`

How the injected route renders each page body. The default emits the page’s unprocessed source, which on an MDX-heavy site means import statements and component tags rather than prose an AI agent can read directly.

```js
starlightLlmActions({
  renderMarkdown: 'simple',
})
```

`'simple'` renders the page the way Starlight does and flattens the result back to plain Markdown, so components resolve to lists, code fences, and headings. It requires optional dependencies you install yourself. See [`RenderMarkdownConfig`](#rendermarkdownconfig) below.

With [`injectRoute: false`](#injectroute) there is no route for the plugin to render, so the setting changes nothing about your output — but `'simple'` still requires its dependencies to be installed whenever it is set.

### `linkAlternate`

* **Type:** `boolean | `[`LinkAlternateConfig`](#linkalternateconfig)
* **Default:** `false`

Inject a per-page alternate-link tag pointing at that page’s Markdown URL, so crawlers and agents can discover the Markdown without knowing the plugin’s URL convention.

```js
starlightLlmActions({
  linkAlternate: true,
})
```

Every non-draft page then carries:

```html
<link rel="alternate" type="text/markdown" href="/guides/example.md" />
```

The href follows [`markdownUrl`](#markdownurl) and respects Astro’s `base`, and non-ASCII slugs are percent-encoded.

Two kinds of page get no tag, because the injected route generates no Markdown for them: drafts, and the 404 page. See [`LinkAlternateConfig`](#linkalternateconfig) below.

### `llmsTxt`

* **Type:** `boolean | `[`LlmsTxtConfig`](#llmstxtconfig)
* **Default:** `false`

Generate site-level indexes: `/llms.txt`, `/llms-full.txt`, and one `/llms-{subset}.txt` per named subset.

```js
starlightLlmActions({
  llmsTxt: true,
})
```

`llms.txt` is the [llmstxt.org](https://llmstxt.org/) index — your site title, its description, a link to each bundle, and a link to every page’s own Markdown route. [`llmsTxt.title`](#llmstxttitle) and [`llmsTxt.description`](#llmstxtdescription) override the first two when your header wordmark reads poorly as a corpus name. `llms-full.txt` is every page concatenated into one file.

The bundles run the same renderer the [Markdown route](#rendermarkdown) does, so every Markdown surface on your site carries identical output.

Requires `site` in your Astro config, because `llms.txt` links have to be absolute — an agent handed the file as a blob has no base to resolve against. The plugin throws at config time if `site` is unset.

See [`LlmsTxtConfig`](#llmstxtconfig) below for ordering, exclusion, and named subsets.

### `prompt`

* **Type:** `string`
* **Default:** `'Read {md_url}. I want to ask questions about it.'`

Default prompt sent to every “Open in…” provider that doesn’t define its own [`providers.<id>.prompt`](#providers). Placeholders:

* `{md_url}` — absolute URL of the current page’s `.md` route. Preferred for LLMs that fetch URLs, since they get clean markdown instead of rendered HTML.
* `{url}` — absolute URL of the current page (rendered HTML).

The resolved prompt (with placeholders replaced) is what gets URL-encoded into provider URL templates and copied to the clipboard for `clipboard-open` providers.

```js
starlightLlmActions({
  prompt: 'Read this Acme docs page and answer in plain English: {md_url}',
})
```

### `triggerLabel`

* **Type:** `string`
* **Default:** `'Copy page'`

Visible label of the dropdown trigger button next to the page title.

```js
starlightLlmActions({
  triggerLabel: 'AI tools',
})
```

### `trigger`

* **Type:** `'click' | 'hover'`
* **Default:** `'click'`

Controls how the dropdown opens.

* `'click'` — the default. The trigger button must be clicked to open the menu; a second click closes it.
* `'hover'` — the menu opens when the cursor hovers over the trigger button. Clicking pins the menu open so it stays visible while you interact with it; a second click closes it. Keyboard navigation is identical in both modes.

```js
starlightLlmActions({
  trigger: 'hover',
})
```

### `closeOnAction`

* **Type:** `boolean`
* **Default:** `true`

Whether the dropdown closes after an item is clicked. Set to `false` to keep the menu open, which is useful when chaining several actions in a row.

Has no effect when [`trigger`](#trigger) is `'hover'`.

```js
starlightLlmActions({
  closeOnAction: false,
})
```

### `toastDuration`

* **Type:** `number` (positive integer, milliseconds)
* **Default:** `3000`

How long the toast notification stays visible after an action completes.

```js
starlightLlmActions({
  toastDuration: 5000,
})
```

### `preOpenDelay`

* **Type:** `number` (non-negative integer, milliseconds)
* **Default:** `300`

How long to wait after showing the toast before opening a new tab, for providers using the `clipboard-open` [strategy](#strategy). The delay gives the toast time to render before focus shifts away; set it to `0` to open immediately.

```js
starlightLlmActions({
  preOpenDelay: 0,
})
```

### `pageOptOut`

* **Type:** `string | false`
* **Default:** `'llmActions'`

Frontmatter key that, when set to `false` on a page, hides the dropdown on that page. Set this option to `false` to disable the opt-out entirely. Set it to a string to use a different frontmatter key.

```js
starlightLlmActions({
  pageOptOut: 'showAiTools', // page sets `showAiTools: false`
})
```

See [Per-page opt-out](/starlight-llm-actions/guides/per-page-opt-out/) for details.

### `printNotice`

* **Type:** `boolean | `[`PrintNoticeConfig`](#printnoticeconfig)
* **Default:** `false`

Snapshot disclaimer shown when the page is printed (Cmd/Ctrl+P, the dropdown’s PDF button, or browser menu). Hidden on screen; visible only in `@media print`.

* `true` — enable the warning admonition with built-in defaults.
* A [`PrintNoticeConfig`](#printnoticeconfig) object — customise the warning, supply branding (logo + site name), or both.
* `false` (or omit) — disabled.

See [Print/PDF snapshot notice](/starlight-llm-actions/configuration/print-notice/) for the full guide.

## `ActionsConfig`

The `actions` object’s full shape:

```ts
interface ActionsConfig {
  copyMarkdown?: boolean;        // default: true
  viewMarkdown?: boolean;        // default: true
  printPdf?: boolean;            // default: false
  openIn?: boolean | OpenInConfig; // default: true
}
```

### `actions.copyMarkdown`

* **Type:** `boolean`
* **Default:** `true`

Show the **Copy as Markdown** item. See [Copy as Markdown](/starlight-llm-actions/actions/copy/).

### `actions.viewMarkdown`

* **Type:** `boolean`
* **Default:** `true`

Show the **View as Markdown** item. See [View as Markdown](/starlight-llm-actions/actions/view/).

### `actions.printPdf`

* **Type:** `boolean`
* **Default:** `false`

Show the **Download as PDF** item. Off by default; the print dialog is always reachable via Cmd/Ctrl+P. See [Download as PDF](/starlight-llm-actions/actions/print-pdf/).

### `actions.openIn`

* **Type:** `boolean | OpenInConfig`
* **Default:** `true`

The **Open in…** submenu. `true` enables the five default-on providers; `false` removes the submenu. An [`OpenInConfig`](#openinconfig) object lets you relabel the submenu and configure providers individually.

## `OpenInConfig`

```ts
interface OpenInConfig {
  enabled?: boolean;          // default: true
  label?: string;             // default: 'Open in…'
  providers?: ProvidersConfig;
}
```

### `openIn.enabled`

* **Type:** `boolean`
* **Default:** `true`

Whether the submenu renders. Equivalent to setting `openIn` itself to a boolean, but useful when you want to keep other `OpenInConfig` settings around while temporarily disabling the submenu.

### `openIn.label`

* **Type:** `string`
* **Default:** `'Open in…'`

Submenu label.

### `openIn.providers`

* **Type:** [`ProvidersConfig`](#providers)
* **Default:** five default-on providers enabled (ChatGPT, Claude, Gemini, GitHub Copilot, Perplexity); the other eleven are off until opted into.

Per-provider configuration. See [`providers`](#providers) below.

## `providers`

Object keyed by provider id. Each value is either:

* `true` — enable with defaults. Required for default-off providers.
* `false` — disable.
* A [`ProviderOverride`](#provideroverride) object — customise label, prompt, URL template, strategy, etc. Also opts in any default-off provider.
* `undefined` (omitted) — provider keeps its built-in default state.

```ts
interface ProvidersConfig {
  // Default-on
  chatgpt?:    boolean | ProviderOverride;
  claude?:     boolean | ProviderOverride;
  copilot?:    boolean | ProviderOverride;
  gemini?:     boolean | ProviderOverride;
  perplexity?: boolean | ProviderOverride;
  // Default-off (opt in with `true` or an override object)
  aistudio?:    boolean | ProviderOverride;
  cursor?:      boolean | ProviderOverride;
  deepseek?:    boolean | ProviderOverride;
  duckduckgo?:  boolean | ProviderOverride;
  grok?:        boolean | ProviderOverride;
  huggingchat?: boolean | ProviderOverride;
  kagi?:        boolean | ProviderOverride;
  mistral?:     boolean | ProviderOverride;
  phind?:       boolean | ProviderOverride;
  t3chat?:      boolean | ProviderOverride;
  youcom?:      boolean | ProviderOverride;
}
```

For the full catalog and per-provider defaults, see [Configuration: Providers](/starlight-llm-actions/configuration/providers/).

## `ProviderOverride`

Every field is optional; unset fields fall back to the provider’s built-in defaults.

```ts
interface ProviderOverride {
  enabled?: boolean;
  label?: string;
  description?: string;
  prompt?: string;
  url?: string;
  strategy?: 'url-prompt' | 'inline-content' | 'clipboard-open';
  maxBytes?: number;
  fallbackStrategy?: 'url-prompt' | 'inline-content' | 'clipboard-open';
  icon?: string | false;
}
```

### `enabled`

Whether the provider appears in the submenu. Equivalent to passing `<id>: false` directly.

### `label`

Display label. Default: provider’s built-in label (e.g. `'ChatGPT'`).

### `description`

Short description shown under the label. Default: provider’s built-in description (e.g. `'Open this page in ChatGPT'`).

### `prompt`

Prompt template specific to this provider. Falls back to the global [`prompt`](#prompt) if unset.

### `url`

URL template. Placeholders are URL-encoded at click time:

* `{prompt}` — the resolved prompt (with `{url}` and `{md_url}` substituted).
* `{prompt_with_markdown}` — `{prompt}` + `"\n\n"` + the full page markdown.

For `clipboard-open` providers, this is the static URL opened *after* the prompt is copied to the clipboard.

### `strategy`

One of `'url-prompt'`, `'inline-content'`, `'clipboard-open'`. See [Concepts: per-provider strategies](/starlight-llm-actions/getting-started/concepts/#strategies).

### `maxBytes`

For `inline-content` only. Maximum number of bytes the assembled URL may grow to. When the URL would exceed this, the plugin falls back to [`fallbackStrategy`](#fallbackstrategy).

### `fallbackStrategy`

Strategy used when the `inline-content` URL exceeds `maxBytes`. Cursor’s default is `'url-prompt'`.

### `icon`

* An SVG filename under the package’s `icons/` folder (e.g. `'cursor.svg'`).
* An absolute URL.
* `false` — hide the icon.

## `RenderMarkdownConfig`

```ts
type RenderMarkdownConfig = 'raw' | 'simple' | RenderMarkdownModule;


interface RenderMarkdownModule {
  module: string;
}
```

### `'raw'`

The default. Emits the Content Collection entry’s body verbatim — the original Markdown or MDX source, imports and component tags included.

### `'simple'`

Renders the page to HTML the way Starlight does, then flattens that HTML back to plain Markdown. Starlight’s `<Tabs>`, `<FileTree>`, `<Steps>`, and Expressive Code blocks arrive as ordinary Markdown lists, code fences, and headings instead of component tags.

Unknown custom elements are unwrapped, keeping their text content. Add `data-mdast="ignore"` to any element whose rendered output is pure chrome to drop it and its children entirely.

The flattening pipeline is heavy, so the plugin does not install it for you:

```sh
npm install @astrojs/mdx unified rehype-parse rehype-remark remark-gfm remark-stringify hast-util-select unist-util-remove
```

If any are missing, the build fails at config time and names the exact install command, rather than failing partway through page rendering.

> **Framework components are not supported**
>
> `'simple'` renders through an Astro container that registers only the MDX renderer. A page using a React, Vue, Svelte, or Solid component throws during rendering.
>
> That failure is caught per page: the plugin logs a warning naming the page and falls back to that page’s raw source. One such page never fails the build or affects the rest of the site.

### `{ module }`

* **Type:** `string`

Module specifier for your own renderer, whose default export is a `MarkdownRenderer`. Relative paths resolve against your Astro project root.

```js
starlightLlmActions({
  renderMarkdown: { module: './src/render-markdown.ts' },
})
```

**src/render-markdown.ts**

```ts
import type { MarkdownRenderer } from 'starlight-llm-actions';


const render: MarkdownRenderer = async (entry, context) => {
  return entry.body ?? '';
};


export default render;
```

Return the **body only** — the route still prepends the `# title` heading and the description blockquote. Throwing falls back to that page’s raw source with a warning, exactly as `'simple'` does.

The value must be a module specifier rather than a function, because plugin config is serialized on its way into the injected route and a function cannot survive that trip.

## `LinkAlternateConfig`

```ts
interface LinkAlternateConfig {
  type?: string;      // default: 'text/markdown'
  absolute?: boolean; // default: false
}
```

Passing `true` instead of an object is shorthand for `{}` — enabled with both defaults.

### `linkAlternate.type`

* **Type:** `string`
* **Default:** `'text/markdown'`

The tag’s `type` attribute. Change it if your [`markdownUrl`](#markdownurl) serves something else, e.g. `'text/plain'` for a `.txt` template.

### `linkAlternate.absolute`

* **Type:** `boolean`
* **Default:** `false`

Emit a full URL instead of a root-relative path. The origin comes from `site` in your Astro config; the plugin throws at config time if `site` is unset, rather than emitting a broken href.

**astro.config.mjs**

```js
export default defineConfig({
  site: 'https://example.com',
  // …
});
```

```html
<link rel="alternate" type="text/markdown" href="https://example.com/guides/example.md" />
```

## `LlmsTxtConfig`

Passed to [`llmsTxt`](#llmstxt).

```ts
{
  title?: string;       // default: Starlight's title
  description?: string; // default: Starlight's description
  promote?: string[];   // default: ['index*']
  demote?: string[];    // default: []
  exclude?: string[];   // default: []
  subsets?: { label: string; description?: string; paths: string[] }[];
}
```

Passing `true` instead of an object is shorthand for `{}` — enabled with all defaults.

Every pattern here matches a page’s site path (`guides/example`, not `/guides/example/`) through [picomatch](https://github.com/micromatch/picomatch), the same glob dialect `starlight-llms-txt` uses. An existing pattern list ports over unchanged.

For a `docs` page the site path *is* the entry id. For a collection configured with a [`path`](#collections) template, it is the id run through that template — so one pattern dialect covers every collection, written the way the URLs read.

### `llmsTxt.title`

* **Type:** `string`
* **Default:** Starlight’s `title`

Name of the corpus. It becomes the `llms.txt` H1, the phrase in the “Complete documentation” set description, and the product name in every bundle’s `<SYSTEM>` preamble.

Starlight’s `title` is the site’s header wordmark, and a lot of sites keep that short — a site titled `DOCS` opens its `llms.txt` with `# DOCS`, which tells an agent nothing about whose docs it just fetched. The [llms.txt spec](https://llmstxt.org/) makes that H1 the one required element in the file, so this is the way to satisfy it without renaming your header.

```js
starlightLlmActions({
  llmsTxt: {
    title: 'Acme Platform Documentation',
  },
})
```

**llms.txt**

```md
# Acme Platform Documentation
```

### `llmsTxt.description`

* **Type:** `string`
* **Default:** Starlight’s `description`

Summary of the corpus, emitted as the blockquote under the H1.

Starlight’s `description` is a meta-description written for search results, which is a different job from telling an agent what the corpus covers. Setting this also gives the blockquote to a site that has no Starlight `description` at all — without either, the line is omitted.

Each field falls back on its own, so overriding the title leaves your existing description in place.

```js
starlightLlmActions({
  llmsTxt: {
    title: 'Acme Platform Documentation',
    description: 'Guides, API reference, and runbooks for the Acme platform.',
  },
})
```

**llms.txt**

```md
# Acme Platform Documentation


> Guides, API reference, and runbooks for the Acme platform.
```

### `llmsTxt.promote`

* **Type:** `string[]`
* **Default:** `['index*']`

Site paths sorted to the top of every index. Earlier patterns outrank later ones, so `promote: ['index*', 'getting-started/**']` puts the home page first and the getting-started section second.

Pass `[]` to turn the default off and order everything alphabetically.

### `llmsTxt.demote`

* **Type:** `string[]`
* **Default:** `[]`

Site paths sorted to the end. Earlier patterns come before later ones, so the first pattern lands just after the un-ranked pages and the last pattern lands dead last.

A page matching both `promote` and `demote` is demoted — a page you pushed to the end was named more deliberately than one swept up by a broad `promote` glob.

```js
starlightLlmActions({
  llmsTxt: {
    promote: ['index*', 'getting-started/**'],
    demote: ['reference/**', 'changelog*'],
  },
})
```

### `llmsTxt.exclude`

* **Type:** `string[]`
* **Default:** `[]`

Site paths dropped from `llms.txt` and `llms-full.txt`. Drafts are already excluded.

This does not hide a page: an excluded page still serves its own Markdown route, and still carries its `<link rel="alternate">` tag if [`linkAlternate`](#linkalternate) is on. It only stops appearing in the two site-wide indexes.

A [`subsets`](#llmstxtsubsets) entry whose `paths` name an excluded page still carries it, so a section too large for `llms-full.txt` can ship as its own bundle.

```js
starlightLlmActions({
  llmsTxt: {
    exclude: ['internal/**', 'sandbox/**'],
  },
})
```

### `llmsTxt.subsets`

* **Type:** `{ label: string; description?: string; paths: string[] }[]`
* **Default:** `[]`

Named slices of the docs, each emitted as its own bundle and listed in `llms.txt`. Useful when one section is large enough that an agent should be able to fetch it without the rest of the site.

```js
starlightLlmActions({
  llmsTxt: {
    subsets: [
      {
        label: 'REST API',
        description: 'the complete REST API reference',
        paths: ['api/**'],
      },
    ],
  },
})
```

That emits `/llms-rest-api.txt` and adds this line to `llms.txt`:

```md
- [REST API](https://example.com/llms-rest-api.txt): the complete REST API reference
```

| Field         | Required | Behavior                                                                                                             |
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `label`       | yes      | Human-readable name. Lowercased and hyphenated to build the file name, so `'REST API'` becomes `/llms-rest-api.txt`. |
| `description` | no       | Appended after the subset’s link in `llms.txt`.                                                                      |
| `paths`       | yes      | Globs matching the site paths to include. At least one. Takes precedence over [`exclude`](#llmstxtexclude).          |

A subset inherits the `promote`/`demote` ordering, so its pages keep the relative order `llms-full.txt` gives them. It does not inherit [`exclude`](#llmstxtexclude): `paths` is the narrower statement and wins, which is what lets one config drop a section from `llms-full.txt` and publish it as a bundle at the same time.

`paths` that match no page is a build error rather than a bundle holding nothing. The globs run against site paths, so write `guides/example`, not `/guides/example.md`.

Two labels that reduce to the same file name are a config error rather than a silent overwrite, and `'Full'` is reserved for `/llms-full.txt`.

## `PrintNoticeConfig`

```ts
interface PrintNoticeConfig {
  branding?: false | PrintNoticeBranding;
  warning?:  false | PrintNoticeWarning;
}
```

Either field can be `false` to render only the other half.

### `printNotice.branding`

* **Type:** `false | PrintNoticeBranding`
* **Default:** none

Branding row rendered above the page H1 in print/PDF.

```ts
interface PrintNoticeBranding {
  logo?: PrintNoticeLogo;
  siteName?: string;
}


interface PrintNoticeLogo {
  src: string;        // URL or site-relative path
  alt?: string;
  height?: string;    // CSS height, default '1.5rem'
}
```

`src` is emitted as written. If your Astro config sets a `base`, include it (`/my-docs/logo.svg`), or the image resolves against the domain root and the branding row renders broken.

### `printNotice.warning`

* **Type:** `false | PrintNoticeWarning`
* **Default:** built-in defaults when `printNotice: true` or the `warning` field is omitted under a `printNotice` object

Warning admonition rendered below the page H1 in print/PDF.

```ts
interface PrintNoticeWarning {
  title?: string;       // default: 'Documentation Snapshot'
  message?: string[];   // each entry renders as a <p>
  showUrl?: boolean;    // default: true
  showDate?: boolean;   // default: true
  urlLabel?: string;    // default: 'Live version: '
  dateLabel?: string;   // default: 'Exported: '
}
```

See [Print/PDF snapshot notice](/starlight-llm-actions/configuration/print-notice/) for examples.

## Frontmatter options

In addition to plugin options, individual pages can opt out via frontmatter:

```mdx
---
title: A page with no AI dropdown
llmActions: false
---
```

The frontmatter key is configurable via [`pageOptOut`](#pageoptout). See [Per-page opt-out](/starlight-llm-actions/guides/per-page-opt-out/).