Синтаксис markdown
Что понимает конвертер markdown, во что превращает каждую конструкцию и где молча делает не то, что ждёшь.
Тело документа, отправленного в POST /pages/markdown, проходит через небольшой построчный конвертер: каждая конструкция становится одним ядровым блоком Гутенберга. Это побайтовый порт PHP-конвертера v1, поэтому он намеренно простой и не сообщает о том, чего не понял, — неподдержанная конструкция молча превращается в текст абзаца. Правила ниже — это вся грамматика.
Заголовки
Строка, начинающаяся с одного–шести # и пробела, становится core/heading соответствующего уровня. Инлайн-разметка внутри работает. Заголовок первого уровня не пишите: заголовок страницы печатает фрейм из name, поэтому тело начинается с ##. # без пробела — обычный текст.
Абзацы и переводы строк
Идущие подряд непустые строки образуют один абзац и склеиваются через <br>. Абзац заканчивается пустой строкой или строкой, с которой начинается другой блок, — заголовком, пунктом списка, цитатой, оградой кода, строкой таблицы, картинкой, разделителем или сырым блоком. Поэтому абзац пишется одной строкой: абзац, разбитый по ширине, так и остаётся разбитым на странице.
Первая строка абзаца.
Вторая строка — приклеится к первой через <br>.
Новый абзац.
Списки
Строка, начинающаяся с - , * или с числа с точкой и пробелом (1. ), открывает core/list — маркированный или нумерованный. Каждый пункт — одна строка; список заканчивается на первой строке, которая не пункт. Вложенности нет: пункт с отступом список не открывает и становится текстом абзаца. Нумерованный список всегда считает с единицы, какие бы числа ни были написаны.
Таблицы
Идущие подряд строки, начинающиеся с |, образуют core/table. Первая строка — шапка. Вторая строка только из дефисов и двоеточий — разделитель, она пропускается; двоеточия выравнивания игнорируются. Крайние вертикальные черты срезаются, в ячейках работает инлайн-разметка.
Вертикальная черта внутри ячейки всегда режет ячейку — инлайн-код её не защищает, экранирования нет. Пишите «или» либо выносите содержимое из таблицы.
Цитаты и разделители
Идущие подряд строки с > в начале образуют одну core/quote, строки склеиваются через <br>. Внутри работает только инлайн-разметка: ни списков, ни заголовков. Строка из трёх и более -, * или _ и ничего больше — core/separator.
Цитата годится для короткого предупреждения. Для всего длиннее —
op/callout, ниже.
Картинки
Строка, в которой только картинка, становится core/image; заголовок в кавычках после адреса становится подписью, с инлайн-разметкой:

Картинка с пустым адресом выбрасывается. Картинка с текстом после неё на той же строке или внутри абзаца не конвертируется. Берите path, который возвращает POST /media, а не абсолютный адрес.
Код
Ограда из трёх обратных кавычек с именем языка открывает блок кода, строка из трёх обратных кавычек без ничего закрывает. Всё между ними экранируется и сохраняется как есть. Язык в нижнем регистре уходит в класс тега code:
<!-- wp:core/code -->
<pre class="wp-block-code"><code class="language-bash">curl -s "$BASE/health"</code></pre>
<!-- /wp:core/code -->
Сайт подсвечивает код Prism, и раскрашиваются такие языки: bash, json, html, css, js, php, yaml. Любое другое имя тоже попадает в класс, но остаётся без цвета; ограда без языка даёт голый code и не подсвечивается вовсе. Markdown-документ с frontmatter — yaml, разметка фрейма и сырые блоки — html, ответы — json, вызовы — bash.
Ограды не вкладываются: первая же строка из трёх обратных кавычек внутри блока его закрывает, поэтому пример кода не может содержать ограду. Ограды из тильд и код с отступом в четыре пробела не поддерживаются — они становятся абзацами.
Инлайн-разметка
| Markdown | Результат |
|---|---|
**жирный**, __жирный__ | <strong> |
*курсив*, _курсив_ | <em> |
| текст в одинарных обратных кавычках | <code>, содержимое экранируется |
[текст](/ru/docs-ru/stranicy/) | <a href> |
Сначала вынимается инлайн-код, потом по порядку заменяются жирный, курсив и ссылки. Всё, что вне обратных кавычек, уходит на страницу как есть, и отсюда ловушки:
- Двойное подчёркивание где угодно — жирный, даже внутри слова: имя фрейма вроде
site__header__navбез кавычек превращает свою середину в жирный. Одинарное подчёркивание безопасно только между буквами. - Одинарная звёздочка находит пару на любом расстоянии, и две случайные звёздочки в предложении делают курсивом всё между ними.
- Текст не экранируется:
<tag>вне обратных кавычек становится настоящим HTML, а незакрытый тег ломает остаток страницы. - Обратную кавычку внутри инлайн-кода не написать; автоссылок, ссылок-сносок и примечаний нет.
Правило простое: любой идентификатор, ключ меты, токен, имя фрейма, тег и путь — в обратные кавычки.
Сырые блоки
Строка, начинающаяся с <!-- wp:, уходит на страницу нетронутой — так в текст попадают блоки, которые markdown не выражает, в том числе вставки op/frame и op/callout. Самозакрывающийся блок — одна строка, заканчивающаяся на /-->:
<!-- wp:op/frame {"ref":"article__faq"} /-->
Парный блок тянется до своего закрывающего комментария вместе со всеми вложенными. Содержимое из markdown не конвертируется, поэтому само должно быть разметкой блоков:
<!-- wp:op/callout {"variant":"warning","title":"Перевыпуск отзывает"} -->
<!-- wp:paragraph -->
<p>Выпуск нового токена сразу убивает прежний.</p>
<!-- /wp:paragraph -->
<!-- /wp:op/callout -->
После <!-- нужен пробел, и открывающий комментарий должен стоять в начале строки. Атрибуты — JSON, двойную кавычку внутри значения надо экранировать, иначе JSON блока ломается и атрибуты молча откатываются к значениям по умолчанию. Сырые блоки можно дописать и полем blocks запроса: {"type": "raw", "content": "…"}.
Автосборка FAQ
С "faq_autodetect": true в теле запроса конвертер ищет заголовок второго уровня, текст которого — один из маркеров FAQ: FAQ, FAQs, Frequently asked questions, Частые вопросы, Вопросы и ответы, Часто задаваемые вопросы и несколько на других языках, — и сворачивает вопросы под ним в один блок onpress/faq-items со схемой FAQPage. Каждый заголовок третьего или четвёртого уровня — вопрос, абзацы и списки после него — ответ.
## Частые вопросы
### Токен привязан к одному сайту?
Нет, он несёт права владельца на всех сайтах сети.
### Можно ли восстановить потерянный токен?
Нет, хранится только его хеш. Выпустите новый.
<!-- wp:onpress/faq-items {"items":[{"question":"Токен привязан к одному сайту?","answer":"Нет, он несёт права владельца на всех сайтах сети."},{"question":"Можно ли восстановить потерянный токен?","answer":"Нет, хранится только его хеш. Выпустите новый."}]} /-->
Сам заголовок остаётся над блоком. Секция заканчивается на следующем заголовке второго уровня или на первом блоке, который не вопрос и не ответ. Меньше двух вопросов или вопрос без ответа — и секция остаётся обычными заголовками и абзацами. faq_markers в теле запроса заменяет список маркеров. По умолчанию опция выключена и читается только из тела запроса, не из frontmatter.