Todo lo de abajo vive bajo `duxt` en tu `app/app.config.ts`. Cómo funciona la
combinación, y por qué los arrays reemplazan en lugar de añadirse, está en
[Configuración](/getting-started/configuration).

## Sitio

| Clave              | Por defecto          | Qué controla                                            |
| :----------------- | :------------------- | :------------------------------------------------------- |
| `title`            | **ninguno**          | El nombre en la barra de navegación y en el título SEO    |
| `logo`              | **ninguno**          | Un logotipo en lugar del par de icono y nombre — ver abajo    |
| `version`          | **ninguna**          | El distintivo junto a él                                  |
| `organization`     | **ninguna**          | Quién publica el sitio, para schema.org — ver abajo       |
| `locales`          | los siete            | Qué idiomas de la capa sirve este sitio                   |
| `breadcrumb`       | `true`               | `false` quita el rastro sobre el título de la página      |
| `pageIcon`          | **ninguno**          | Icono para páginas sin uno propio — el de la sección gana |
| `packageManagers`  | pnpm, npm, yarn, bun | Qué gestores ofrece un bloque de comando, en ese orden    |
| `requestSamples`   | siete de doce      | Qué muestras de código ofrece el cliente de pruebas — ver [Muestras de peticiones](/reference/openapi/request-samples) |
| `sampleLanguages`  | **ninguna**        | Gramáticas de Shiki extra, para `x-codeSamples` en un lenguaje que ninguna muestra nombra |
| `poweredBy`        | activado             | `false` quita la línea «Powered by duxt» del pie          |

## Comportamiento

| Clave | Por defecto | Efecto |
| :-- | :-- | :-- |
| `copy.models` | ChatGPT, Claude | Destinos en orden de menú; la lista sustituye al valor inicial |
| `contributors.avatarUrl` | plantilla de GitHub | `{username}` se sustituye por colaborador |
| `toc.depth` | `3` | Encabezado más profundo del índice |
| `toc.scrollOffset` | `96` | Línea de activación bajo el borde de la ventana |
| `search.fuzzy` | `0.35`, `3`, `20` | Umbral Fuse, longitud mínima y límite |
| `search.recentPages` | `5` | Páginas recientes; `0` oculta la lista |
| `openapi.exampleDepth` | `6` | Profundidad máxima de ejemplos derivados |
| `openapi.schemaDepth` | `8` | Profundidad máxima del árbol de esquema |
| `shortcuts.singleCharacter` | `true` | Teclas globales `?`, `[` y `]`; `⌘/Ctrl+K` permanece |
| `analytics.track` | **ninguno** | Callback independiente del proveedor por interacción |

```ts [app/app.config.ts]
export default defineAppConfig({
  duxt: {
    copy: {
      models: [
        {
          label: 'Mi asistente',
          icon: 'simple-icons:openai',
          url: 'https://example.com/new?prompt='
        }
      ]
    },
    contributors: {
      avatarUrl: 'https://github.com/{username}.png?size=96'
    },
    toc: { depth: 4, scrollOffset: 112 },
    search: {
      fuzzy: { threshold: 0.3, minMatchCharLength: 3, limit: 15 },
      recentPages: 8
    },
    openapi: { exampleDepth: 6, schemaDepth: 8 },
    shortcuts: { singleCharacter: false }
  }
})
```

Las listas sustituyen sus valores iniciales. Una lista `copy.models` vacía
elimina destinos externos, no las acciones de copiar y ver la fuente.
`search.recentPages: 0` no guarda entradas nuevas. Las opciones fuzzy solo
afectan a las aproximaciones tras una búsqueda Content vacía. Sin callback de
analytics no se envía nada; consentimiento, proveedor y almacenamiento
pertenecen al sitio consumidor.

### `logo`

| Campo     | Tipo     | Notas                                                        |
| :-------- | :------- | :------------------------------------------------------------ |
| `src`     | `string` | La marca. Sin valor queda el icono genérico junto a `title`  |
| `srcDark` | `string` | Se intercambia por CSS bajo la clase `dark`, no por script   |
| `alt`     | texto    | Recae en `title`                                             |

Definir uno es [Dale identidad a tu sitio](/guides/brand-your-site).

### `organization`

El nodo `Organization` que los buscadores y los resúmenes de IA leen para nombrar
una fuente. Ausente hasta que lo definas: duxt renderiza la documentación de
otros y no adivina de quién.

| Campo  | Tipo     | Notas                                                           |
| :----- | :------- | :---------------------------------------------------------------- |
| `name` | texto    | El nombre de quien publica. Sin él no se publica nada           |
| `url`  | `string` | Su sitio. Recurre a `site.url`                                  |
| `logo` | `string` | Absoluto, o una ruta desde la raíz; cuadrado y de 112px o más   |


## Anuncios

Un aviso para todo el sitio: una versión, una migración o un mantenimiento. No
se renderiza ni se reserva espacio hasta que escribas una entrada.

```ts [app/app.config.ts]
announcements: [
  {
    text: { 'en-GB': 'Version 2 is out.', 'es-ES': 'La versión 2 ya está disponible.' },
    link: { label: 'Leer las notas', to: '/releases/v2' }
  },
  {
    id: 'march-maintenance',
    text: 'Mantenimiento programado el domingo desde las 09:00 UTC.',
    placement: 'below-header',
    startsAt: '2026-03-01T09:00:00Z',
    endsAt: '2026-03-08T09:00:00Z'
  }
],
announcementOptions: { maxVisible: 1 }
```

La lista se dibuja en orden: una versión y una incidencia son dos anuncios, y
el final de uno no debe retirar el otro.

| Campo       | Tipo                                               | Notas                                                           |
| :---------- | :------------------------------------------------- | :-------------------------------------------------------------- |
| `text`      | texto                                              | Literal, clave i18n o registro por locale                       |
| `link`      | enlace                                             | Una acción; las demás pertenecen a la página enlazada          |
| `placement` | `above-header`, `below-header` o `above-content`   | Ubicación; por defecto `above-header`                          |
| `startsAt`  | `string`                                           | Inicio; cualquier valor que analice `Date`                      |
| `endsAt`    | `string`                                           | Final                                                           |
| `id`        | `string`                                           | Identidad de cierre; se deriva del contenido y enlace si falta  |

### Dónde se dibujan

| Ubicación        | Lugar                                                            |
| :---------------- | :--------------------------------------------------------------- |
| `above-header`    | Ancho completo sobre la barra de navegación; es el valor inicial |
| `below-header`    | Ancho completo bajo la barra y sobre las barras laterales        |
| `above-content`   | En la columna del documento, junto a avisos de versión e idioma |

`above-content` pertenece a la columna del documento, por lo que aparece en
páginas de documentación y no en una landing sin esa columna.

`announcementOptions.maxVisible` limita cuántos anuncios muestra a la vez cada
ubicación. Un número limita **cada** ubicación, un objeto las limita por
separado (`{ 'above-header': 1 }`), y `null`, una clave omitida o la ausencia de
`maxVisible` significa sin límite. Al cerrar el anuncio superior, el siguiente
ocupa su lugar.

### Cuándo se muestran

`startsAt` y `endsAt` se evalúan en el **navegador** contra el reloj del lector.
El anuncio empieza y termina sin despliegue ni recarga. La ventana es
semiabierta: se muestra desde `startsAt` y hasta `endsAt`.

Incluye un offset cuando importe el instante exacto
(`2026-03-01T09:00:00Z`). Sin zona horaria se usa la hora local del lector. Una
fecha inválida se trata como ausente, para que un error tipográfico no esconda
el aviso silenciosamente.

### Cierre

Un anuncio cerrado sigue cerrado durante la navegación, las recargas y los
cambios de idioma. Se recuerda su **identidad**:

- con `id`, exactamente esa cadena independiente del locale; cambiarla vuelve
  a mostrar el anuncio a todos,
- sin `id`, un valor derivado de todas las variantes de `text` y `link`; cambiar
  el texto crea un anuncio nuevo, reordenar claves de locale no.

Usa `id` cuando quieras corregir el texto sin volver a mostrarlo. Los cierres
solo viven en `localStorage`; las claves de anuncios eliminados se descartan.
Como el reloj y los cierres pertenecen al navegador, la franja se dibuja tras
la hidratación.

La superficie del componente está en
[DuxtAnnouncements](/reference/components/layout/duxt-announcements).

## Navegación y enlaces

Una entrada de sección es un enlace más un campo propio:

| Campo      | Tipo     | Notas                                                              |
| :--------- | :------- | :------------------------------------------------------------------ |
| `pageIcon` | `string` | Icono para las páginas de esta sección que no definen uno. Prevalece sobre `duxt.pageIcon`; el frontmatter de una página sigue ganando |

Útil donde las páginas no pueden llevar icono — el frontmatter de un ADR está
fijado en `title`, `description`, `status` y `date`, así que el registro de
decisiones se representaría sin ninguno.

| Clave        | Por defecto     | Qué controla                                                  |
| :----------- | :-------------- | :------------------------------------------------------------- |
| `navigation` | una entrada     | Enlaces de la barra; una entrada con `children` es un desplegable |
| `sections`   | **vacío**       | La segunda fila de la barra — las partes de primer nivel        |
| `links`      | **vacío**       | Enlaces de icono a la derecha de la barra                       |
| `aside`      | solo el título  | `title` y `links` bajo la tabla de contenidos                   |
| `footer`     | **vacío**       | `copyright` y `legal`                                           |
| `landing`    | una acción | La página en `/` |

La mayoría se entregan vacíos, por dos razones distintas. `links`,
`aside.links`, `footer.legal`, `title`, `version` y el texto de la página de
inicio nombran un proyecto concreto — un repositorio, una comunidad, un aviso
legal, un nombre, una publicación, una frase sobre lo que hace — y pertenecen a
quien gestiona el sitio. `sections` nombra las
partes de un **árbol** de documentación, y el árbol es tuyo: cuatro pestañas
apuntando a nombres de carpeta que la capa hubiera adivinado no llevarían a
ninguna parte. Defínelo cuando tu documentación tenga secciones; hasta entonces
la fila no se representa y la barra lateral muestra el árbol completo, que es la
forma correcta para una documentación sin secciones.

**Una fila por fuente.** Una entrada pertenece a la fuente bajo cuyo prefijo de
URL se encuentra, y la fila muestra las entradas de la fuente en la que está el
lector — así, un sitio con la documentación en la raíz y otra cosa en `/demo`
escribe una sola lista `sections` y cada área dibuja su parte de ella. Una
sección generada bajo un área (`/demo/openapi`) pertenece a esa área en lugar de ser
un área propia. Un sitio con una sola fuente tiene un área, cada entrada está
dentro, y la fila es exactamente la lista tal como se escribió.

### La página de inicio

La página en `/` dibuja tres bandas, y cada una está ausente hasta que se define
su clave.

| Clave         | Por defecto                 | Qué dibuja                                           |
| :------------ | :-------------------------- | :--------------------------------------------------- |
| `badge`       | **ninguno**                 | La píldora sobre el titular — texto, o un objeto badge |
| `headline`    | **ninguno**                 | El `h1`; recae en `title`                             |
| `description` | **ninguna**                 | El párrafo bajo él, y la meta descripción de la página |
| `actions`     | una, «Lee la documentación» | Los botones del bloque principal; una acción sin `to` resuelve a la primera sección |
| `command`     | **ninguno**                 | Un comando de instalación copiable bajo los botones   |
| `preview`     | **ninguno**                 | Una página de este sitio, incrustada en una ventana de navegador |
| `features`    | **vacío**                   | La rejilla de tarjetas                                |

`command` es una `string` simple, no texto: un comando de shell es igual en
todos los idiomas, y uno traducido por error es uno que no se ejecuta. La capa
no entrega ninguno — no sabe cómo se llama tu proyecto, por la misma razón por
la que `links` está vacío.

No hay llamada a la acción final, deliberadamente. Los botones del bloque
principal son la llamada; repetirlos bajo una rejilla de tarjetas exige a un lector recién
llegado decidir dos veces.

`badge` se entrega vacío por la misma razón que `links`: una píldora sobre el
titular dice algo del estado de un proyecto — «beta», «v2 ya está» — y la capa
no sabe nada del estado del tuyo. Una cadena es la forma corta; el objeto añade
icono, color y destino.

| Campo de `badge` | Tipo      | Notas                                                     |
| :--------------- | :-------- | :--------------------------------------------------------- |
| `label`          | texto     | Obligatorio. `{version}` se sustituye por la `version` del sitio |
| `icon`           | `string`  | Cualquier nombre de Iconify                                |
| `variant`        | `string`  | `default`, `secondary`, `outline`, `success`, `destructive` |
| `to`             | `string`  | Convierte toda la píldora en un enlace                     |
| `external`       | `boolean` | La pestaña nueva                                           |

```ts [app/app.config.ts]
badge: {
  label: '{version} publicada',
  icon: 'lucide:rocket',
  variant: 'success',
  to: 'https://github.com/acme/sdk/releases/latest',
  external: true
}
```

`preview` es una ventana viva, no una imagen. Incrusta una página de **este**
sitio en un marco de navegador que el lector puede desplazar, navegar y cuyo
tema puede cambiar sin salir de la página de inicio. El marco se monta solo
cuando la banda entra en el viewport, y nunca durante el renderizado en
servidor — un iframe en el HTML inicial es una segunda carga completa
compitiendo con la primera.

| Campo de `preview` | Tipo     | Notas                                                     |
| :----------------- | :------- | :--------------------------------------------------------- |
| `to`               | `string` | La página a incrustar; por defecto, la primera sección      |
| `height`           | `string` | Cualquier longitud CSS. Por defecto `32rem`, `24rem` bajo `sm` |
| `src`              | `string` | Una captura EN LUGAR de la página viva                      |
| `srcDark`          | `string` | Sirve el modo oscuro; sin él, `src` sirve ambos             |
| `alt`              | texto    | El texto alternativo y el nombre accesible del marco        |

::callout{type="tip" title="Una captura es la opción más barata"}
El marco vivo carga la aplicación una segunda vez. Un sitio que prefiera no
pagar eso define `src` y obtiene la misma ventana alrededor de una imagen fija.
::

### Forma de un enlace

| Campo         | Tipo                | Notas                                            |
| :------------ | :------------------ | :------------------------------------------------ |
| `label`       | texto               | Obligatorio                                       |
| `to`          | `string`            | Una ruta de documentación se localiza; una URL no |
| `icon`        | `string`            | Cualquier nombre de Iconify, p. ej. `lucide:rocket` |
| `description` | texto               | Se muestra en un desplegable de la barra          |
| `external`    | `boolean`           | La pestaña nueva y la flecha — nunca el enrutado  |
| `children`    | `DuxtLink[]`        | Convierte una entrada de barra en un desplegable  |
| `variant`     | variante de botón   | Solo `landing.actions`                            |

Todo campo marcado como _texto_ admite un literal, una clave i18n o un registro
por locale — véase [Localización](/concepts/localisation).

## Fuentes

| Clave           | Se lee en          | Qué controla                                |
| :-------------- | :----------------- | :------------------------------------------ |
| `sources`       | tiempo de compilación | Las fuentes de documentación              |
| `sourceOptions` | tiempo de compilación | Cómo esas fuentes se convierten en prefijos |
| `versions`      | ejecución          | Sobrescribe las versiones derivadas cuando necesitan etiquetas o descripciones |

Ambas claves de fuente tienen [su propia página](/reference/sources). `locales`
es la tercera clave de compilación: cambiar cualquiera de las tres exige
recompilar.

## El feed

`/rss.xml` está vacío hasta que `feed.path` nombra una sección:

```ts
feed: { path: '/changelog', title: 'mi proyecto — publicaciones' }
```

Desactivado por defecto a propósito. Un feed es una lista de cosas que **han
pasado**, y una página de referencia editada no es un acontecimiento — un sitio
que publica cada edición como elemento enseña a sus lectores a darse de baja.
Los elementos se ordenan por el `date` propio de la página, recurriendo al
último commit que la tocó, y solo la versión predeterminada de cada fuente
contribuye, de modo que un changelog versionado no repite cada entrada una vez
por versión.


La sección a la que suele apuntar es un
[historial de versiones](/reference/changelog), cuyas páginas llevan cada una su
`date`.

## Generado, no escrito

| Clave              | Escrito por      | Contiene                                         |
| :----------------- | :--------------- | :----------------------------------------------- |
| `resolvedSources`  | la compilación   | El manifiesto: qué colección sirve qué prefijo    |
| `layerVersion`     | la compilación   | La versión de duxt, para el pie                   |
| `layerRepository`  | la compilación   | El repositorio de duxt, para el pie               |

Definir cualquiera de los tres a mano se sobrescribe en la siguiente
compilación.