Фреймы
Фрейм — готовая разметка, загруженная через API. Он задаёт вид страницы целиком: оболочку статьи, шапку и подвал внутри неё, блок вопросов, лендинг, выгруженный из конструктора. Разметка хранится ровно такой, какой её прислали — ничего не переписывается и не чистится, инлайновый <script> из конструктора доезжает байт в байт. Подстановки делаются только при сборке страницы: блоки <!-- onpress/… --> и атрибуты data-op-bind.
Не путайте фрейм с iframe: фрейм выводится в саму страницу, а не во встроенное окно.
Три свойства, которых у фрейма нет
- Типа. Нет «шапки», «подвала» и «страницы» как категорий: назначение говорит слаг, место — явная ссылка. Поле
typeво входе игнорируется с предупреждениемtype_ignored. - Адреса. Фрейм — шаблон, сам по себе он не открывается. Всё, что открывается по адресу, — страница, которая называет свой фрейм. Поле
urlво входе —400 frame_url_not_supported: молчаливое игнорирование навело бы на мысль, что лендинг опубликован. - Человеческого имени. Фрейм называется слагом. Поле
nameигнорируется с предупреждениемname_ignored.
Умолчаний сайта («фрейм шапки по умолчанию») тоже нет: GET/PUT /frames/defaults и POST /frames/{ref}/default отвечают 410 gone.
Имя по БЭМ
Слаг — блок__элемент--модификатор. Внутри части слова соединяются одиночным дефисом.
| Слаг | Блок | Элемент | Модификатор |
|---|---|---|---|
article | article | — | — |
article__faq | article | faq | — |
article__faq--simple | article | faq | simple |
site__header | site | header | — |
site-simple__header | site-simple | header | — |
docs__code-cards | docs | code-cards | — |
Блок site — общее для всего сайта (шапка, подвал). Остальные блоки называются по сущности, которой служат: article, docs, landing. Элемент — часть блока, модификатор — его вариант.
Регулярное выражение:
^[a-z0-9]+(?:-[a-z0-9]+)*(?:__[a-z0-9]+(?:-[a-z0-9]+)*)?(?:--[a-z0-9]+(?:-[a-z0-9]+)*)?$
Заглавные буквы приводятся к строчным. Одиночное подчёркивание, второй __ или -- — 400 invalid_slug с подсказкой схемы. Поля block, element, modifier в ответах вычисляются из слага.
Загрузить фрейм
curl -s -X POST "$API/site/$SITE/frames" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "$(jq -n --rawfile html site__header.html '{slug: "site__header", html: $html}')" \
| jq '{success, updated, frame: .frame | {id, slug, block, element}, inserts, binds, renders_on_request, warnings, bake: .bake.status}'
site__header.html:
<header class="site-header">
<a class="site-header__logo" data-op-bind='{"href":"site.home_url"}'>
<img data-op-bind='{"src":"site.logo_url","alt":"site.name"}' width="24" height="24">
<span data-op-bind='{"text":"site.name"}'></span>
</a>
<!-- frame/lang-switcher--dropdown /-->
<nav class="site-header__nav"><!-- frame/menu {"place":"header","depth":1} /--></nav>
</header>
Ответ — отчёт о том, что найдено в разметке, и результат пересборки:
{
"success": true,
"updated": false,
"frame": { "id": "cb7ccee58078d1fb", "slug": "site__header", "block": "site", "element": "header" },
"inserts": { "op/lang-switcher": 1, "op/menu": 1 },
"binds": 3,
"renders_on_request": false,
"warnings": [],
"bake": "ok"
}
| Поле отчёта | Что значит |
|---|---|
inserts, inserts_total | блоки по именам и их общее число |
binds | сколько тегов с data-op-bind |
sources | какие наборы и источники читают повторители |
frame_refs | слаги вложенных фреймов (ссылки по id приводятся к слагам) |
renders_on_request, live_reasons | фрейм зависит от страницы и собирается на каждом запросе; почему — в live_reasons |
has_doctype, has_body | фрейм — целый документ |
warnings | что стоит поправить; запись при этом проходит |
frame_refs_missing | вложенные фреймы, которых на сайте нет — такой блок выводит пустое место, пока фрейм не появится |
bake | пересборка копии на стороне сайта: ok, unavailable, failed, skipped |
rebaked | какие фреймы, вкладывающие этот, пересобраны вслед за ним |
Предупреждения, на которые стоит реагировать:
data-op-bind is written with double quotes— атрибут в двойных кавычках оборвётся на первой кавычке JSON. Пишите его в одинарных.the frame contains a whole document—<!doctype>или<body>уместны у фрейма-документа, но вложенный вызовомframe/…он даст второй<body>внутри чужой страницы.frame_ref_missing: …— вложенного фрейма нет на сайте.
dry_run: true проверяет разметку и возвращает тот же отчёт, ничего не сохраняя. bake: false сохраняет без пересборки копии.
Предел — 2 МБ разметки на фрейм (413 frame_too_large). Сайт целиком с картинками и скриптами во фрейм не помещается.
Слаг занят — 409 slug_taken; менять разметку существующего фрейма нужно через PUT.
Прочитать
# список без разметки; ?block=article — один блок, ?q=head — подстрока слага, ?include_html=1 — с разметкой
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames" | jq '.frames[] | {slug, renders_on_request, pages: (.pages // [] | length)}'
# один фрейм с разметкой: ref — слаг или id
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames/site__header" | jq '.frame | {slug, referenced_by, pages, html}'
У фрейма в списке и в карточке есть pages — id страниц, которые в нём собираются, и referenced_by — слаги фреймов, которые его вкладывают. Адресов у фрейма нет, его адреса — это адреса его страниц.
Заменить разметку
curl -s -X PUT "$API/site/$SITE/frames/site__header" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "$(jq -n --rawfile html site__header.html '{html: $html}')" | jq '{updated, warnings, rebaked: .rebaked.status}'
PUT заменяет разметку целиком и может заодно сменить slug (с теми же последствиями, что у PATCH). После записи фреймы, которые вкладывают этот, пересобираются транзитивно — ответ перечисляет их в rebaked. Системный фрейм после PUT с другой разметкой становится своим — см. системные и свои.
Переименовать
curl -s -X PATCH "$API/site/$SITE/frames/shapka-sajta" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"slug": "site__header"}' | jq '{frame: .frame.slug, renamed}'
{ "frame": "site__header", "renamed": { "from": "shapka-sajta", "to": "site__header", "frames_updated": ["article", "landing"], "details": [ { "slug": "article", "replaced": 1 } ] } }
Переименование переписывает ссылки: во всех фреймах сайта вызовы <!-- frame/<старый слаг> --> получают новый слаг. Остальная разметка не трогается ни на байт, изменённые фреймы пересобираются. Страницы не трогаются вовсе: они хранят id фрейма, а не слаг. Слаг — единственное поле, которое правит PATCH; пустое тело — 400 nothing_to_update.
Поставить на страницу
Три равноправных способа, одна и та же мета страницы:
# при публикации — свойство frame
# frame: "article"
# со стороны страницы
curl -s -X PATCH "$API/site/$SITE/pages/tarify" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"frame": "article"}' | jq '{changed, static}'
# со стороны фрейма
curl -s -X POST "$API/site/$SITE/frames/article/assign" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"page": "/tarify/"}' | jq '{page, static, republish}'
У страницы ровно один фрейм; новый заменяет прежний. assign с "clear": true снимает фрейм. Страница хранит id, поэтому переименование фрейма ей не страшно. Ответ несёт static — что стало со статической копией этой страницы после смены фрейма (переписана, снята, не нужна) — подробно в сборке.
Вложить фрейм во фрейм
Единственная связь между фреймами — вызов <!-- frame/<имя> /-->. Например, оболочка статьи с шапкой, вопросами и подвалом:
<!-- frame/site__header /-->
<main class="article">
<h1 data-op-bind='{"text":"page.title"}'></h1>
<!-- onpress/content /-->
<!-- frame/article__faq /-->
</main>
<!-- frame/site__footer /-->
Имя после frame/ — слаг или id фрейма. Парный вызов закрывается <!-- /frame -->, параметры идут JSON-объектом после имени. Вложенный фрейм собирается из своего исходника в контексте той же страницы и того же языка. Кольцо ссылок не зацикливается: фрейм, который уже собирается, второй раз не выводится, на его месте остаётся комментарий с цепочкой. Глубина вложения — до 8, вызовов фреймов за одну сборку — до 64. Атрибут class оборачивает вложенный фрейм в <div class="…">.
Другая шапка на одном виде страниц — это другая оболочка, которая ссылается на другой фрейм шапки (landing → site-simple__header), а не переопределение на странице.
Фрейм может исчезнуть, если его список пуст. Повторитель с "empty":"frame" при пустом списке гасит весь фрейм, в котором стоит: article всегда вкладывает article__faq, а на странице без вопросов блока просто нет — ни заголовка, ни пустой секции. Подробно — в повторителе.
Вызов работает и в теле страницы: <!-- frame/article__cta /--> в markdown выводит общий блок, который правится в одном месте.
Параметры и слот
Вызов фрейма может передать ему данные. Параметры — все атрибуты вызова, кроме служебных (name, ref, frame, slug, id, type, class); внутри фрейма они читаются как frame.<ключ>:
<!-- frame/assistant__chat {"agent":"canva","agent_view":"profile"} /-->
<div id="ai-assistant-mount" data-op-bind='{"data-agent":"frame.agent"}'></div>
Слот — содержимое парного вызова. Markdown между открытием и закрытием остаётся в теле страницы, а фрейм ставит его блоком onpress/slot или разбирает на вопросы и ответы источником slot.faq:
<!-- frame/faq -->
### Сколько это стоит?
Бесплатно.
<!-- /frame -->
Параметром можно передать и источник списка: повторитель с "source":"frame.source" выводит тот набор, который назвал вызов (<!-- frame/site__bento {"source":"home_bento"} /-->). Значения по умолчанию у параметра нет: без него на странице список пуст, а в превью и на холсте собирается из демо-элементов.
Фрейм с параметрами собирается на месте вызова из исходника — своей копии с чужими параметрами у него нет. Все правила — в справочнике блоков, готовый FAQ — в рецепте FAQ. Как взять готовый фрейм и переделать его разметку под себя — свой фрейм из готового.
Фрейм — целый документ
Фрейм может быть полным HTML-документом с <!doctype html>, <head> и <body> — так выглядит лендинг, выгруженный из конструктора. Страница с таким фреймом отдаётся полноценной страницей сайта: в его <head> и <body> встраивается обвязка сайта — SEO-разметка (canonical, robots, Open Graph, JSON-LD), иконки, счётчики аналитики, hreflang. Если у страницы заполнены seo_title и seo_description, они заменяют <title> и description автора; если нет — авторские остаются. Свой счётчик Метрики или GA4 в разметке не дублируется обвязкой.
Два правила для фрейма-документа:
- не вкладывайте его в другой фрейм вызовом
frame/…— получится второй<body>; - держите его независимым от страницы (без
onpress/content, биндовpage.*и прочего из списка в сборке): тогда страница получает статическую копию и отдаётся файлом. Зависимый от страницы фрейм-документ собирается внутри документа темы и даёт два<html>.
Как опубликовать лендинг целиком — рецепт лендинга.
Удалить
curl -s -X DELETE "$API/site/$SITE/frames/article__faq" -H "Authorization: Bearer $OP_TOKEN" | jq '{deleted, unbound_pages, referenced_by, warnings}'
Удаляются разметка и запись реестра, у всех страниц этого фрейма снимается привязка (их id — в unbound_pages), их статические копии снимаются. Такие страницы на сайте v2 после этого отвечают 404 — сначала переставьте их на другой фрейм. Фреймы, которые вкладывали удалённый, не правятся: они перечислены в referenced_by, и блок выводит пустое место, пока фрейм с таким слагом не появится снова.
Системные и свои
Сайт, созданный из шаблона, получает фреймы шаблона системными: фрейм выводит общую версию из библиотеки платформы, и новая версия библиотеки сразу доходит до сайта. Правка разметки на сайте делает фрейм своим — правки остаются, обновления библиотеки его больше не касаются, а в списке у него появляются customized_at_gmt и, когда в библиотеке выйдет версия новее, system_update_available: true. Вернуть системный вид — сбросить фрейм:
curl -s -X POST "$API/site/$SITE/frames/site__header/reset" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' -d '{}' | jq '{reset, origin: .frame.origin}'
Тип фрейма — поле origin (system или custom) в GET /frames; фрейм, заведённый на самом сайте, всегда свой, и сбросить его не к чему (409 frame_not_system).
Обновления доходят сами. Новая версия системного фрейма доходит до всех сайтов, где он системный: страницы пересобираются и отдают новую версию без ручного сброса кэша. Фрейм, вложенный в новую версию и отсутствующий на сайте, доставляется вместе с ней. Фрейм из библиотеки, которого на сайте ещё нет, ставит POST /site/$SITE/frames/system/{ref}/install, список библиотеки — GET /frames/system. Все эндпоинты — в справочнике.
В ответе GET /frames видно, можно ли менять сайт: writable: false — сайт другого контура (стенд и боевой контур разведены), его можно читать, но не менять.
Файлы фрейма
Стили и скрипты фрейма можно держать рядом с разметкой — файлами style.css, script.js и любыми другими .css и .js, до 16 файлов по 512 КБ:
curl -s -X PUT "$API/site/$SITE/frames/site__cta/files/style.css" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d "$(jq -n --rawfile css style.css '{content: $css}')" | jq '{file, bake: .bake.status}'
Тема подключает файлы фрейма страницы и всех вложенных в него: одним <link> в <head> и одним <script> перед </body> на фрейм, адрес с хешем содержимого — правка видна сразу. Опись файлов — поле files фрейма. Файлы ведут себя как разметка: правка у системного фрейма делает его своим, сброс возвращает файлы библиотеки. Картинки и шрифты кладут в медиатеку. Ручки — в справочнике.
Где фрейм выводится
У фрейма в списке есть preview_page — первая опубликованная страница, где он реально выводится: своя, страница фрейма, который его вкладывает, или страница, вставившая его в тело. По нему дашборд открывает вложенные фреймы вроде site__header, у которых своих страниц нет.
Где лежат
В опциях своего сайта: реестр onpress_frames (JSON: id → слаг, отчёт, опись файлов, даты), исходник каждого фрейма в onpress_frame_<id> и его файлы рядом; разметка и файлы системного фрейма — в библиотеке сети. Id — 16 шестнадцатеричных символов, выдаётся при создании; у фреймов одного шаблона на разных сайтах id общий. Фреймы сайтов не делятся напрямую: id или слаг чужого сайта здесь не найдётся (404 frame_not_found). Перенос фрейма на другой сайт — GET на одном и POST на другом.
Дальше
- Блоки onpress/ — все блоки с атрибутами.
- Бинды data-op-bind — подстановка значений в атрибуты.
- Сборка страницы и статическая копия — когда фрейм собирается на запросе, когда страница становится файлом, как работает пересборка.
- Свой фрейм из готового — копия фрейма с другой разметкой при той же логике.
- Готовые фреймы — фреймы с живых сайтов: assistant с чатом, article, docs, faq, outline, шапка и подвал.
- Справочник: фреймы — все параметры и коды.