# Formularios para plantillas de Typst

Dale un formulario a tus propias funciones de Typst, para que quien rellene un informe vea campos con etiqueta en el modo Visual en lugar de la sintaxis de una llamada a función.

Una plantilla de Typst suele traer sus propias funciones: una portada, un cuadro de información, un bloque de firma. Quien escribe el informe las llama, como en `#info-box(title: "Supply risk", tone: "risk")[...]`. Si mantienes la plantilla, puedes describir una sola vez los argumentos de cada función. El modo Visual muestra entonces cada llamada a esa función como un formulario: campos con etiqueta, una lista para las opciones fijas y un interruptor para el sí o el no. Al editar un campo se reescribe solo ese argumento en el archivo `.typ`, así que el informe sigue siendo código de Typst corriente que compila igual que antes.

## Lo que ve el lector

Una llamada en líneas propias se convierte en una **tarjeta**. El encabezado de la tarjeta muestra el nombre del formulario y, si lo eliges, un breve resumen como el título del cuadro. Cada argumento tiene una fila con su etiqueta y un control.

Una llamada dentro de una frase, como `#badge("review")`, se convierte en un **chip** dentro de la línea de texto. Al hacer clic en el chip se abren los mismos campos en una ventana emergente pequeña.

Los campos de texto y de marcado se guardan al pulsar Enter (⌘ Enter o Ctrl Enter en un campo de varias líneas) o al salir del campo. Esc recupera el valor anterior. Las listas y los interruptores se guardan al instante. Cada cambio es un paso de deshacer.

Al mover el cursor a una llamada se muestra su código, como con cualquier otro elemento del modo Visual. **Editar en el código** en la tarjeta hace lo mismo.

## Dónde declarar los formularios

Hay dos lugares, con el mismo formato:

- **En un paquete.** Añade una sección `[tool.oleafly]` al `typst.toml` del paquete. Typst reserva las secciones `[tool.*]` para otras herramientas, así que el paquete se sigue instalando y compilando en todas partes. Funciona con los paquetes `@local` y con los paquetes `@preview` de Typst Universe.
- **En el proyecto.** Para las plantillas que se guardan como archivos `.typ` normales dentro del proyecto, crea `oleafly-forms.toml` en la raíz del proyecto. Cada formulario nombra además el archivo que define la función, con `module`.

Un formulario de paquete:

```toml
[tool.oleafly]
schema = 1

[[tool.oleafly.forms]]
function = "info-box"
label = "Info box"
summary = "title"

  [[tool.oleafly.forms.fields]]
  arg = "title"
  kind = "text"
  label = "Heading"
  placeholder = "Short heading"
  required = true

  [[tool.oleafly.forms.fields]]
  arg = "tone"
  kind = "choice"
  label = "Tone"
  default = "note"
  options = [{ value = "note", label = "Note" }, { value = "risk", label = "Risk" }, "neutral"]

  [[tool.oleafly.forms.fields]]
  arg = 0
  kind = "content"
  label = "Text"
```

Un formulario de proyecto, en `oleafly-forms.toml`:

```toml
[tool.oleafly]
schema = 1

[[tool.oleafly.forms]]
module = "template/memo.typ"
function = "memo-header"
label = "Memo header"
fields = [
  { arg = "to", kind = "text", label = "To" },
  { arg = "subject", kind = "text", label = "Subject" },
]
```

## Las claves

`schema = 1` es obligatorio. Un archivo sin él, o con otro número, se ignora.

Cada entrada `[[tool.oleafly.forms]]` tiene:

| Clave | Obligatoria | Significado |
| --- | --- | --- |
| `function` | Sí | El nombre de la función, como un único identificador de Typst. Los guiones están permitidos. |
| `module` | Solo en `oleafly-forms.toml` | El archivo del proyecto que define la función, relativo a la raíz del proyecto, con `/` entre las carpetas. |
| `label` | No | El nombre del formulario en las tarjetas y los chips. El nombre de la función, de forma predeterminada. |
| `summary` | No | El argumento cuyo valor aparece junto a la etiqueta, escrito como el `arg` de un campo. |
| `fields` | Sí | Uno o más campos. |

Cada campo tiene:

| Clave | Obligatoria | Significado |
| --- | --- | --- |
| `arg` | Sí | Un nombre para un argumento con nombre (`"title"`), o un número para uno posicional (`0` es el primero). Un bloque `[...]` final cuenta como argumento posicional después de los que van entre paréntesis. |
| `kind` | Sí | `text`, `content`, `choice`, `number` o `toggle`. |
| `label` | Sí | La etiqueta del campo. |
| `help` | No | Una nota breve bajo el campo. |
| `placeholder` | No | Texto gris en un campo vacío. |
| `required` | No | `true` marca un campo vacío como que falta y oculta su botón **Borrar**. Oleafly nunca añade el argumento por sí mismo. |
| `default` | No | El valor que usa tu función cuando se omite el argumento. Se muestra, nunca se escribe. |

Los tipos y sus claves adicionales:

- `text` escribe una cadena de Typst. Añade `multiline = true` para una caja más grande.
- `content` edita marcado de Typst y lo escribe como un bloque `[...]`. Un argumento de tipo cadena sigue siendo una cadena.
- `choice` necesita `options`: una lista de cadenas, o tablas `{ value, label }` cuando la etiqueta difiere del valor. Los valores se escriben como cadenas.
- `number` admite `integer`, `min`, `max` y `step`. Los números no llevan unidades.
- `toggle` escribe `true` o `false`.

## Qué llamadas reciben un formulario

Oleafly sigue las líneas `#import` y `#let` del propio informe en el nivel superior del archivo:

- `#import "@local/acme-report:0.1.0" as acme` hace que `#acme.info-box(...)` sea un formulario.
- `#import "@local/acme-report:0.1.0": info-box, status-badge as badge` hace que `#info-box(...)` y `#badge(...)` sean formularios.
- `#import "@local/acme-report:0.1.0": *` hace que cada función que declara el paquete tenga un formulario.
- `#import "template/memo.typ": memo-header` coincide con un formulario de proyecto cuyo `module` es `template/memo.typ`. Las rutas se leen desde la carpeta del propio informe, o desde la raíz del proyecto cuando empiezan por `/`.

La versión del paquete debe coincidir exactamente. Un `#let` con el mismo nombre, más adelante en el archivo, sustituye el formulario a partir de ese punto. Las llamadas a las que se llega a través de otro archivo que vuelve a importar el paquete conservan su texto de código.

Una función con el nombre de un elemento de Typst que el modo Visual ya dibuja, como `table` o `figure`, conserva el aspecto integrado cuando se llama por su nombre sin más. `#acme.table(...)` o una importación con otro nombre sí recibe el formulario.

## Campos que se muestran de solo lectura

Algunos argumentos no se pueden editar de forma segura desde un formulario. Su fila muestra el código con el motivo, y **Editar en el código** salta hasta él:

- **Calculado en el código:** el valor es una expresión, como `title: data.title`.
- **Escrito como otro tipo de valor:** por ejemplo, un número donde el formulario espera texto.
- **No es una de las opciones:** un valor que tus `options` no enumeran.
- **Definido por un argumento de propagación:** una llamada con `..args` pasa valores que el formulario no ve, así que todos los campos son de solo lectura.

Cuando un campo no se puede añadir donde iría, por ejemplo un argumento posicional después de uno que falta, el formulario lo dice y ofrece **Añadir en el código**.

## Errores en una definición

Abre `oleafly-forms.toml`, o el `typst.toml` de un paquete incorporado, en Oleafly y cada problema aparece como una marca en su línea: un tipo desconocido, una etiqueta que falta, un formulario duplicado, un `module` fuera del proyecto. Un formulario defectuoso se omite y los demás siguen funcionando. Un archivo de más de 256 KB no se lee.

## Compartir plantillas

Oleafly lee los formularios de los paquetes en las mismas carpetas que usa el compilador. Un colaborador que no tiene el paquete `@local` ve código normal y ningún error. Para compartir los formularios con todos los de un proyecto, abre el cuadro de diálogo **Paquetes de Typst**, activa **Usar paquetes de la carpeta del proyecto** y elige **Incorporar los paquetes al proyecto**. El paquete, y sus formularios, viajan entonces en la carpeta `typst-packages` del proyecto (consulta [Proyectos de Typst](/docs/typst/)).
