# Escribir la documentación (/dev/es/writing-docs)



Todas las apps de Remy guardan su documentación de la misma manera, de modo que las tareas compartidas y la CLI de Fumadocs funcionan en cada una.
Hay dos sitios de documentación, servidos por el Worker de documentación (la app `docs/`, aparte del Worker de la app): la guía
para los usuarios de la app en `/docs`, y esta documentación para desarrolladores en `/dev`.
Cada uno tiene sus propios idiomas, búsqueda, `llms.txt` y carpeta de Ask AI.

## A quién se dirige cada parte [#who-each-part-is-for]

La documentación tiene tres partes, y cada una está escrita desde el punto de vista de un solo lector. El modo IA de Google, Gemini y cualquier
otro asistente solo las distinguen por lo que dicen las páginas, así que mantenlas separadas:

| Parte                                                       | Lector                                                 | Trata de                                   | Nunca                                             |
| ----------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------- |
| Guía del producto, `content/users/` (`/docs`)               | alguien que usa la app                                 | lo que ve y hace en la app                 | código, herramientas, el repositorio, despliegues |
| Documentación para desarrolladores, `content/dev/` (`/dev`) | un desarrollador que construye Remy o una app sobre él | código, tareas, herramientas, reglas       | cómo usar la app, más allá de un enlace a la guía |
| Referencia de la API (`/reference`)                         | un desarrollador que llama a la API                    | endpoints, generados a partir del contrato | prosa; está generada                              |

* Una página que necesita a la otra parte enlaza a ella en una sola línea («¿La estás construyendo? La documentación para desarrolladores.»).
* La página de inicio de cada parte empieza diciendo a quién se dirige; sus títulos terminan con el nombre de la parte (`docs.config.ts`,
  `titles`), y lo mismo ocurre con su `llms.txt` y su servidor MCP ([Documentación en tus herramientas de IA](./ai-tools.md)).
* Las partes, sus lectores, los archivos llms y los servidores MCP forman una sola lista, `src/lib/sections.ts`.

## Estructura [#layout]

<Files>
  <Folder name="docs/content">
    <Folder name="users">
      <File name="i18n.json" />

      <File name="meta.json" />

      <File name="index.mdx" />

      <File name="formats.mdx" />
    </Folder>

    <Folder name="dev">
      <File name="i18n.json" />

      <File name="meta.json" />

      <File name="index.md" />

      <File name="index.es.md" />

      <File name="writing-docs.mdx" />
    </Folder>
  </Folder>
</Files>

* `i18n.json` contiene los idiomas del sitio: `defaultLanguage` y `languages`. Las URL del sitio, el menú de idiomas,
  la búsqueda y las tareas de traducción lo leen. Las listas de los dos sitios difieren: la documentación para desarrolladores se traduce
  solo a los idiomas en los que ofrecemos soporte a desarrolladores.
* `meta.json` es la navegación del sitio: `pages` fija el orden, `"---Heading---"` inicia una sección
  y `"[Title](https://…)"` es un enlace externo.
* Una traducción está junto a su página: `index.es.md` junto a `index.md`, en `/dev/es/`.
* Las imágenes y vídeos de la guía están en `docs/public/media` y se referencian por URL (`/media/formats.png`); la documentación para desarrolladores tiene diagramas Mermaid y tablas de tipos;
  la referencia de la API es una sección propia en `/reference`, generada a partir del contrato de oRPC.

## Cómo llega una página al lector [#how-a-page-reaches-the-reader]

<Mermaid
  chart="flowchart LR
  A[&#x22;docs/content/*.md, *.mdx&#x22;] --> B[&#x22;fumadocs-mdx macro&#x22;]
  B --> C[&#x22;loader: src/lib/source.ts&#x22;]
  C --> D[&#x22;Docs pages: /docs, /dev&#x22;]
  C --> E[&#x22;Search: /api/search/dev&#x22;]
  C --> F[&#x22;/dev/llms.txt and .md pages&#x22;]
  C --> G[&#x22;MCP server: /api/mcp/dev&#x22;]
  C --> H[&#x22;Social images: /og/dev/...&#x22;]
  I[&#x22;oRPC contract&#x22;] --> J[&#x22;fumadocs-openapi&#x22;] --> K[&#x22;API reference: /reference&#x22;]"
/>

## Frontmatter [#frontmatter]

Toda página empieza con un título y una descripción. La descripción es el resumen de la página en los resultados
de búsqueda, en `llms.txt` y en las vistas previas de enlaces.

```md title="docs/content/users/formats.mdx"
---
title: "Formats"
description: "How dates, numbers, currency and direction change with the language you choose."
---
```

## Imágenes y vídeos [#pictures-and-videos]

<CalloutContainer type="info">
  <CalloutDescription>
    Captúralos con la herramienta del propio proyecto, para que muestren la app real:
    `npm run shots -- /formats --out docs/public/media` para una imagen, y añade `--video`
    para una grabación corta de la página.
  </CalloutDescription>
</CalloutContainer>

<Tabs items="['Picture', 'Video']">
  <Tab value="Picture">
    ```md
    ![The Formats page in English](/media/formats.png)
    ```

    Fumadocs ajusta el tamaño, y al hacer clic se amplía.
  </Tab>

  <Tab value="Video">
    ```mdx
    <video src="/media/formats-tour.webm" controls muted playsInline preload="metadata" />
    ```

    Un vídeo requiere una página `.mdx` (es un elemento JSX).
  </Tab>
</Tabs>

## Componentes [#components]

En páginas `.mdx`: `<Callout>` (o `:::note`, `:::warning` en cualquier página), `<Cards>` y `<Card>`, `<Tabs>`
y `<Tab>`, `<Steps>` y `<Step>`, `<Accordions>` y `<Accordion>`, `<Files>`, `<Folder>` y
`<File>`, un diagrama ` ```mermaid ` y una tabla de tipos leída de nuestro TypeScript:

<TypeTable
  id="type-table-source.server.ts-DocsPageData"
  type="{
  &#x22;id&#x22;: &#x22;source.server.ts-DocsPageData&#x22;,
  &#x22;name&#x22;: &#x22;DocsPageData&#x22;,
  &#x22;description&#x22;: &#x22;Everything a docs page needs, all serializable.&#x22;,
  &#x22;entries&#x22;: [
    {
      &#x22;name&#x22;: &#x22;site&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;\&#x22;dev\&#x22; | \&#x22;docs\&#x22;&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;union&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;slug&#x22;,
      &#x22;description&#x22;: &#x22;The page's slugs joined ('' for the site's index).&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;path&#x22;,
      &#x22;description&#x22;: &#x22;The page's file in its collection (the translation, else English), which the browser loads.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;lang&#x22;,
      &#x22;description&#x22;: &#x22;The language of the page (the URL's).&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;languages&#x22;,
      &#x22;description&#x22;: &#x22;The site's languages.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string[]&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;array&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;langs&#x22;,
      &#x22;description&#x22;: &#x22;The languages this page has its own text in, the default first: its hreflang alternates.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string[]&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;array&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;title&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;description&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;lastModified&#x22;,
      &#x22;description&#x22;: &#x22;The page file's last commit date (fumadocs-mdx `lastModified`), ISO.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string | undefined&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: false,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;headings&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;DocsHeading[]&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;array&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;pageTree&#x22;,
      &#x22;description&#x22;: &#x22;The page tree in the language, serialized for DocsLayout (useFumadocsLoader).&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;SerializedPageTree&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;object&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;links&#x22;,
      &#x22;description&#x22;: &#x22;English file path in the collection → this language's URL, for relative links.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;Record<string, string>&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;Record<string, string>&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;origin&#x22;,
      &#x22;description&#x22;: &#x22;The request's origin (https://…), for the head's absolute URLs: canonical, hreflang, social image.&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;url&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;markdownUrl&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    },
    {
      &#x22;name&#x22;: &#x22;githubUrl&#x22;,
      &#x22;description&#x22;: &#x22;&#x22;,
      &#x22;tags&#x22;: [],
      &#x22;type&#x22;: &#x22;string&#x22;,
      &#x22;simplifiedType&#x22;: &#x22;string&#x22;,
      &#x22;required&#x22;: true,
      &#x22;deprecated&#x22;: false
    }
  ]
}"
/>

## La CLI de Fumadocs [#the-fumadocs-cli]

`npm run docs:cli -- feature <name>` (solo en remy-auth) ejecuta la CLI de Fumadocs fijada en el Worker
compartido de documentación, `packages/ui/src/docs`; su `cli.json` asigna las carpetas de allí, y lo que
escribe es un cambio en el paquete, revisado como cualquier otro. Las apps toman el Worker del paquete y
nunca lo ejecutan.

| Función  | Lo que aportó a este sitio                                                                                                                  |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `llms`   | `/<site>/llms.txt`, `/<site>/<lang>/llms-full.txt`, `/<site>/<lang>/<page>.md`                                                              |
| `mcp`    | un servidor MCP por parte, `/api/mcp/docs`, `/api/mcp/dev`, `/api/mcp/reference` ([Documentación en tus herramientas de IA](./ai-tools.md)) |
| `og`     | una imagen social por página                                                                                                                |
| `webmcp` | la documentación como herramientas para agentes en el navegador                                                                             |

## Traducciones [#translations]

Escribe solo en inglés. `dev:land` traduce en main después del landing, un solo escritor a la vez
([traducciones: un solo escritor](./how-we-work.md#translations-one-writer)); `npx remy i18n check docs` enumera lo que aún
necesita cada idioma.
