# Формы для шаблонов Typst

Дайте своим функциям Typst форму, чтобы те, кто заполняет отчёт, видели в визуальном режиме подписанные поля вместо синтаксиса вызова функции.

Шаблон Typst часто поставляется со своими функциями: титульная страница, информационный блок, блок подписи. Тот, кто пишет отчёт, вызывает их, как в `#info-box(title: "Supply risk", tone: "risk")[...]`. Если вы сопровождаете шаблон, вы можете один раз описать аргументы каждой функции. Тогда визуальный режим показывает каждый вызов этой функции как форму: подписанные поля, список для фиксированных вариантов, переключатель для «да» или «нет». Правка поля переписывает в файле `.typ` только этот один аргумент, поэтому отчёт остаётся обычным исходником Typst, который компилируется так же.

## Что видит читатель

Вызов на отдельных строках становится **карточкой**. В заголовке карточки показано имя формы и, если вы его задали, краткое описание, например заголовок блока. Для каждого аргумента есть строка с подписью и элементом управления.

Вызов внутри предложения, например `#badge("review")`, становится **чипом** в строке текста. Щелчок по чипу открывает те же поля в небольшом всплывающем окне.

Текстовые поля и поля разметки сохраняются, когда вы нажимаете Enter (⌘ Enter или Ctrl Enter в многострочном поле) или выходите из поля. Esc возвращает прежнее значение. Списки и переключатели сохраняются сразу. Каждое изменение отменяется одним шагом.

Если переместить курсор в вызов, он покажет свой исходник, как любой другой элемент визуального режима. **Изменить в исходнике** на карточке делает то же самое.

## Где объявлять формы

Есть два места, формат одинаковый:

- **В пакете.** Добавьте раздел `[tool.oleafly]` в `typst.toml` пакета. Typst оставляет разделы `[tool.*]` для других инструментов, поэтому пакет по-прежнему устанавливается и компилируется везде. Это работает для пакетов `@local` и для пакетов `@preview` из Typst Universe.
- **В проекте.** Для шаблонов, которые лежат в проекте обычными файлами `.typ`, создайте `oleafly-forms.toml` в корне проекта. Каждая форма также называет файл, в котором определена функция, с помощью `module`.

Форма в пакете:

```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"
```

Форма проекта в `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" },
]
```

## Ключи

`schema = 1` обязателен. Файл без него или с другим числом игнорируется.

Каждая запись `[[tool.oleafly.forms]]` содержит:

| Ключ | Обязателен | Значение |
| --- | --- | --- |
| `function` | Да | Имя функции в виде одного идентификатора Typst. Дефисы допустимы. |
| `module` | Только в `oleafly-forms.toml` | Файл проекта, в котором определена функция, относительно корня проекта, с `/` между папками. |
| `label` | Нет | Имя формы на карточках и чипах. По умолчанию это имя функции. |
| `summary` | Нет | Аргумент, значение которого показано рядом с именем; записывается так же, как `arg` поля. |
| `fields` | Да | Одно или несколько полей. |

У каждого поля есть:

| Ключ | Обязателен | Значение |
| --- | --- | --- |
| `arg` | Да | Имя для именованного аргумента (`"title"`) или число для позиционного (`0` обозначает первый). Завершающий блок `[...]` считается позиционным аргументом после тех, что в скобках. |
| `kind` | Да | `text`, `content`, `choice`, `number` или `toggle`. |
| `label` | Да | Подпись поля. |
| `help` | Нет | Короткая заметка под полем. |
| `placeholder` | Нет | Серый текст в пустом поле. |
| `required` | Нет | `true` помечает пустое поле как отсутствующее и скрывает его кнопку **Очистить**. Oleafly никогда не добавляет аргумент сам. |
| `default` | Нет | Значение, которое использует ваша функция, когда аргумент опущен. Оно показывается, но никогда не записывается. |

Виды и их дополнительные ключи:

- `text` записывает строку Typst. Добавьте `multiline = true` для поля большего размера.
- `content` редактирует разметку Typst и записывает её как блок `[...]`. Строковый аргумент остаётся строкой.
- `choice` требует `options`: список строк или таблицы `{ value, label }`, когда подпись отличается от значения. Значения записываются как строки.
- `number` принимает `integer`, `min`, `max` и `step`. У чисел нет единиц измерения.
- `toggle` записывает `true` или `false`.

## Какие вызовы получают форму

Oleafly следует собственным строкам `#import` и `#let` отчёта на верхнем уровне файла:

- `#import "@local/acme-report:0.1.0" as acme` делает формой `#acme.info-box(...)`.
- `#import "@local/acme-report:0.1.0": info-box, status-badge as badge` делает формами `#info-box(...)` и `#badge(...)`.
- `#import "@local/acme-report:0.1.0": *` делает формами все функции, для которых пакет объявляет форму.
- `#import "template/memo.typ": memo-header` соответствует форме проекта, у которой `module` равен `template/memo.typ`. Пути читаются от папки самого отчёта или от корня проекта, если начинаются с `/`.

Версия пакета должна совпадать точно. `#let` с тем же именем позже в файле заменяет форму с этого места. Вызовы, доступные через другой файл, который заново импортирует пакет, сохраняют свой исходный текст.

Функция, названная как элемент Typst, который визуальный режим уже рисует, например `table` или `figure`, сохраняет встроенный вид, когда вызвана по голому имени. `#acme.table(...)` или переименованный импорт всё равно получают форму.

## Поля только для чтения

Некоторые аргументы нельзя безопасно изменять из формы. Их строка показывает исходник с причиной, а **Изменить в исходнике** переходит к нему:

- **Вычисляется в исходнике:** значение является выражением, например `title: data.title`.
- **Записано как значение другого вида:** например, число там, где форма ожидает текст.
- **Не входит в список вариантов:** значение, которого нет в ваших `options`.
- **Задано разворачиваемым аргументом:** вызов с `..args` передаёт значения, которых форма не видит, поэтому все поля доступны только для чтения.

Когда поле нельзя добавить туда, где оно должно быть, например позиционный аргумент после отсутствующего, форма сообщает об этом и предлагает **Добавить в исходнике**.

## Ошибки в описании

Откройте `oleafly-forms.toml` или `typst.toml` скопированного в проект пакета в Oleafly, и каждая проблема отобразится маркером на своей строке: неизвестный вид, отсутствующая подпись, дублирующаяся форма, `module` за пределами проекта. Сломанная форма пропускается, а остальные продолжают работать. Файл больше 256 КБ не читается.

## Передача шаблонов

Oleafly читает формы пакетов из тех же папок, что и компилятор. Соавтор, у которого нет пакета `@local`, видит обычный исходник и никаких ошибок. Чтобы поделиться формами со всеми участниками проекта, откройте диалог **Пакеты Typst**, включите **Использовать пакеты из папки проекта** и выберите **Скопировать пакеты в проект**. Пакет и его формы затем передаются в папке `typst-packages` проекта (см. [Проекты Typst](/docs/typst/)).
