Vizen Shop

Components: one piece of markup, many insertions

/docs/componentsdraftМашинный перевод с английского· проверено 2026-09-13

Статус: draft · проверенный: 2026-09-13, стенд (создать → zip) → publish → page; TypeScript → app.js); 2026-10-27, §4.7.1 own зоны на стенде (классические и main-only pages, Обязанность, чтение, наследование, откат order) владелец: гладиатр API документация · Служить: GET /docs/components связанный: own-markup.md (твой own HTML в shop), transfer.md (законченный site как папка)

1.Что вы можете сделать здесь

Корабль с одной наценкой, что shop owner Можно много раз поместить в разные content, Без прикосновений code. Вы пишете папку и schema; Они выбирают values. А. new Выпуск папки обновляет каждую вставку сразу.

Это NOT Второй способ поставить HTML на page. Компонент является html document с kind=component: Та же папка, те же релизы, те же самые /_html/{doc}/{release}/ gateway, Те же правила изоляции. Добавлено это schema и замещение.

2. Решать first

Если ты хочешь…Возьми это pathСтоимость
это exact Накладка на этот pagehtml widget (own-markup.md)none — Редактировать - значит редактировать code
Одинаковая накладка на многие pages с разными contentкомпонент (этот доктор)Вы должны написать component.json
Целое завершенное site как естьtransfer.md—
live каталог data внутри разметкиvz- ключи и те page scope (own-markup.md) — Они также работают внутри компонента.параметры являются values, Не вопросы: data Вытекает из ключей, см. §7
Отзывы, Q&A, galleries сохранено в виде записи setsа level-3 Компонент, читающий public GET /record-sets (/docs/record-sets §4.4)text появляется after JavaScript; Открыть set покупателям first

Приходит из Shopify

Компонентом является theme section: component.json это {% schema %}, params это settings, {{key}} - это та же скоба. Имена типов отображают один на один:

Шоппинг settings[].typeздесьзаменяются как
text, textareatextHTML-сбежавшая нитка
richtext, htmlhtmlтот author's разметка через level‑1 санитар
image_pickerimageа file_id из медиа-библиотеки → public URL (затем рециркулирующий)
urllinkhttp(s)://, /path, mailto:, tel: — что-либо else считать отсутствующим
colorcolor#rgb…#rrggbbaa, else отсутствие
number, rangenumberтот number
checkboxbooltrue/false, и {{#key}}…{{/key}} показывает block — см. ниже
select, radioselectодного из options, else отсутствие
product—не на шаге 1, см. § 7
collection—не на шаге 1, см. § 7

Ан отсутствие value (Пропавший, неправильный type, Вне белого списка) показывает параметр's default; Это empty Только когда нет default.

Условный blocks: {{#key}}…{{/key}}

Все, что находится между двумя маркерами, сохраняется. key это true и удаляется когда это false — Разметка, вложенные замены и все такое. drop а Вся колонна, card или row Вместо того, чтобы оставить empty один:

{{#show4}}<li><img src="{{icon4}}" alt=""><h3>{{head4}}</h3></li>{{/show4}}

Три правила, все соблюдаются, когда вы publish освобождение:

  • `key` должны быть объявлены в `component.json` и должны быть `type: "bool"`. Нет. другой type открывается a block; а text параметр не "правда"".
  • Маркеры должны быть парными. {{#a}} нуждается в own {{/a}}.
  • Блоки гнезда, они не перекрываются. {{#a}}…{{#b}}…{{/b}}…{{/a}} Хорошо на любой глубине; {{#a}}…{{#b}}…{{/a}}…{{/b}} Нет, не поворачивать внешнюю сторону. block off Также удаляет все вложенное внутри него.

Разбейте любую из трех, и освобождение будет отклонено. COMPONENT_MANIFEST_INVALID, Назвать маркер и file. Маркер написал внутри HTML comment являются прозой, а не разметкой и не проверяются.

Не взято из Shopify: Жидкие фильтры и теги (замена здесь набрана), не template язык), blocks внутри section (Не гнездиться на шаге 1), limit/max_blocks, и theme как единица delivery — Здесь единица является один document.

3. Объекты и где они находятся live

The document. POST /html-documents с kind: "component". The value set это "" | widget | component; theme (а layout's Сервисная папка) не здесь можно. kind Вернемся к каждому прочтению document и является только после прочтения.

В папке. Именно так. html-проектная папка: index.html В корневой, CSS, JS, images рядом с ним, загруженный POST /html-documents/{id}/releases/zip и повернулся live через .../releases/{rid}/publish. Освобождение компонента также должно содержать `component.json` — Публикация без отказа.

`component.json`.

{
  "name": "Баннер акции",
  "params": [
    { "key": "title", "type": "text",  "label": "Заголовок", "default": "Скидки недели", "max": 80 },
    { "key": "image", "type": "image", "label": "Картинка" },
    { "key": "link",  "type": "link",  "label": "Ссылка" },
    { "key": "sale",  "type": "bool",  "label": "Показать бейдж", "default": false },
    { "key": "size",  "type": "select", "options": ["s", "m", "l"], "default": "m" }
  ],
  "presets": [ { "name": "Осень", "params": { "title": "Осенняя распродажа" } } ]
}

Все правила, соблюдаемые в publish с обидчиком field названных в докладе (error code COMPONENT_MANIFEST_INVALID):

  • key матчи ^[a-z][a-z0-9_]{0,31}$ и является уникальным;
  • в лучшем случае 32 параметры, file в лучшем случае 16 KB;
  • type Один из восьми вышеперечисленных — unknown type является error, Потому что type Именно это и решает побег.;
  • select Требуется не-empty options (≤ 64) и ничего else может нести;
  • default должен быть параметром's own type (Струна для text, а number для number, true/false для bool, а #hex для color, член options для select);
  • max Плотность длин text/html (в персонажах, нарезанных на персонажа граница);
  • unknown ключи Объекты являются ignored Нарочно — папка, написанная для позже schema Публикуется и сегодня.

presets это optional. Шаг 1 - читать first Предустановленный как base values из вставки, на top в соответствии с параметром defaults.

разорвавшийся schema хранится на выпуске row, Таким образом, регресс восстанавливает оба разметка и параметр set из этого version — И он будет прочитан обратно из API, never Из папки: см. §4.5.

Вставка. Обычный page section. Писать path:

{ "type": "text",
  "payload": { "v": 2, "kind": "component",
               "props": { "component": { "ref": 42, "params": { "title": "Своё", "sale": true } } } } }

type это text Потому что бэкэнд section Белый список image|slideshow|text|video и каждый widget Поездка на нем — The widget's реальный идентичность payload.kind, именно так, как kind: "html". ref Это document id; params это plain {key: value} объект (≤ 8) KB).

4. Рецепты

4.1 Создавать, загружать, publish

API=https://api.vizen.shop; T="Bearer $VIZEN_TOKEN"

# 1. the document
DOC=$(curl -s -X POST "$API/html-documents" -H "Authorization: $T" \
  -H 'Content-Type: application/json' \
  -d '{"item":{"name":"Баннер акции","kind":"component","level":1}}' | jq -r .result.id)

# 2. the folder (index.html + component.json + style.css) in one zip, published at once
cd banner && zip -qr ../banner.zip . && cd ..
curl -s -X POST "$API/html-documents/$DOC/releases/zip?publish=1" \
  -H "Authorization: $T" -H 'Content-Type: application/zip' --data-binary @banner.zip

The publish Ответьте на имена schema он принял:

{ "document_id": 42, "release_id": 17, "active": true, "kind": "component",
  "component": { "name": "Баннер акции",
                 "params": [ {"key":"title","type":"text","default":"Скидки недели"}, … ] } }

Отказ является 422 с {"error":"RELEASE_INVALID","report":{"errors":[…]}}; а schema error перевозить code: COMPONENT_MANIFEST_INVALID, ref = тот field (params[0].key) и message = тот reason.

4.2 Поставьте его на page, дважды, с разными values

GET /content-blocks/{id}, добавить два sections, PUT /content-blocks/{id} с тот целое список (sections являются заменой-set):

{"item":{"sections":[
  {"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
     "ref":42,"params":{"title":"Осень","sale":true}}}}},
  {"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
     "ref":42,"params":{"title":"Зима"}}}}}
]}}

Для существующего block, Сохранить другой sections в result.sections и Отправить слившийся список в item.sections. отказываться item недействительным; а empty item.sections: [] не ясно sections (повторный-field семантика). POST /docs/validate только принимать {"sections":[…]}, без item конверт.

проверять: Открыть page и охапать HTML в двух заголовках. Два добавления один document должны показать два разных texts; тот image должен пройти через рециркулятор (/w/1024/webp/files/…), Не как оригинал file.

4.3 Прочитать собранный HTML непосредственно

curl -s -G "$API/html-documents/42/content" \
  --data-urlencode 'company_id=12' \
  --data-urlencode 'params={"title":"Осень","sale":true}'

Ответ такой же, как и для html widget — {html, level, updated_at} — специально: витрина магазина не должна быть в состоянии отличить компонент от widget, Или будет два погрузчика на одного kind из block. params это JSON объект URL-закодированный в query; участвует в кэше key, В двух вставках never видеть друг друга's HTML. static=true ведет себя точно так же, как и для widget (payment/cart/account Поверхности получают разметку без author's code).

4.4 Корабль new version

Загрузить а new zip с ?publish=1. Каждая вставка поднимает его after чистка — магазин вставок values, never Ценности, которые key исчезнувший из new schema есть ignored; параметры вставки never set Отступать назад к new default. POST /html-documents/{id}/rollback Возвращает предыдущий релиз Вместе с его own schema.

А. PAT's contour это fixed от key паспорт (GET /v1/account/token); X-Vizen-Contour cannot Переключай. header Приведенный ниже пример относится к уполномоченного заседания, а не на PAT.

История выпуска - это разделяемый между собой draft и live site — только указатель на active Освобождение на contour. Публикуется релиз с X-Vizen-Contour: dev Появляется в списке А live Сеанс также, несущий was_live: false. Активировать его там будет draft работы над live site За спиной owner's Ответ: Так что ответ 409 RELEASE_DRAFT_ONLY: publish тот draft (POST /v1/dev/publish) И освобождение продолжается live Через эту дверь. draft Ничего не меняется.

4.5 Читать далее schema назад

Ты не приносишь component.json Чтобы узнать, что за компонент берет. schema Возвращается на document И на каждом выпуске, который имеет:

# the document — the schema of its ACTIVE release
curl -s "$API/html-documents/42" -H "Authorization: $T" \
  | jq -r '.result | {active_release_id, manifest}'
# { "active_release_id": "17", "manifest": "eyJuYW1lIjoi…" }

# the releases — each with its OWN schema, which is what a rollback restores
curl -s "$API/html-documents/42/releases" -H "Authorization: $T" \
  | jq '.items[] | {id, active, manifest}'

Две формы для одного schema, Потому что два ответа приходят off Два транспорта:

гдеfieldформа
GET /html-documents/{id}, GET /html-documentsresult.manifestпрото bytes → основание 64 из сырых JSON (расшифровать его before разбор)
GET /html-documents/{id}/releases, …/releases/{rid}items[].manifestа plain JSON объект

manifest это empty, never {}, Когда нет никакого schema Чтобы показать: a html widget, а layout папка, а draft релиз, компонент, папка которого имеет never Издана. "Пустота" "" на document (ан empty bytes) и key Пропавший без вести. empty *объект* будет читаться как "компонент, не имеющий Параметры», что совсем другое — так что ни один ответ не посылает ни одного..

active_release_id Возвращается на document И это тоже, и это честно. test для "Можно ли еще разместить этот компонент": нет active Отпуск, без разметки и без schema. The field только для чтения — publish откат move это, PUT /html-documents/{id} не.

4.6 Исправить один file без new застежка

# read the source of one file of a release (text only, ≤ 2 MB)
curl -s "$API/html-documents/42/releases/17/file?path=style.css" \
  -H "Authorization: $T" | jq -r .text > style.css
# {"release_id":17,"status":"published","path":"style.css",
#  "mime":"text/css","size":812,"sha256":"…","text":"…"}
# ship it back: one file, the rest inherited from the active release
curl -s -X POST "$API/html-documents/42/releases" -H "Authorization: $T" \
  -d '{"base":"active","note":"edit","files":[{"path":"style.css","sha256":"'$SHA'","size":'$SZ'}]}'
curl -s -X PUT "$UPLOAD_URL" --data-binary @style.css
curl -s -X POST "$API/html-documents/42/releases/$RID/publish" -H "Authorization: $T"

ошибки: 404 FILE_NOT_FOUND, 415 FILE_NOT_TEXT (Бинарный — Reupload It), 413 FILE_TOO_LARGE. index.html Возвращаться publish-очищенный, Не то bytes Вы загрузили: конвертер переписывает его на месте, оригинал не сохраняется.

4.7 Наденьте его ONE product или ONE category

Три пути; для одного page Первый — это почти всегда то, что вы хотите.:

Тебе нужноДелайThe page Сохраняет shop шаблон?
Ваш блок before или after тот body это page наследоватьДобавить его в page's own зоны — §4.7.1да
а Разная структура для этого page's bodyкопировать body группировать и связывать копию - §4.7.2Нет, он перестает наследовать
тот то же самое блокировать каждый product одного или нескольких sectionsодин feed привязка к поддереву каждого раздела - §4.7.3да

значения, которые различаются на product (material, sizes, Состав не является blocks: keep В них, в product's attributes и description И пусть template шоу Они. Иная структура для целого section это body variant привязанный к category (category_children/<id>), не blocks на всех product.

4.7.1 Добавить block вокруг наследуемого body

Каждый product и category page имеет свой own blocks Рядом с ним body наследует. Они live на службе blocks названный __page:<zone> — а manifest section kind:"zone" чей refs список block ids в display order — и в явном виде и blocks Они вписаны в список page с PUT /products/{id}/content-blocks (PUT /categories/{id}/content-blocks для category). Именно это и есть shop's page editor пишет, так что owner видеть и редактировать ваши block Там и там, и там body Продолжает следовать за shop template.

Сначала выясните, какие зоны page рисовать. Его геометрия исходит от ближайшей к нему Три слоя: The page's own __page:top (page.layout или page.layoutConfig в его section), затем template (design.page.body_page), затем layout (design.page.theme_page); Последние два кодируются как base64. JSON объекты в GET /products/{id} — Читайте те же самые ключи.

Ближайший layer иметьЗоны, вытянутыеПеред тем как bodyПосле этого body
нет layout и нет layoutConfig в любом местеклассический top · body · bottom__page:top__page:bottom
предустановленный (main-only, stacked, content-aside, content-aside-left) или layoutConfig.rowsтот zoneIds в его rows; тот body Колонна с которой "body": true — main В каждой предустановке__page:main, zoneFlow side:"before"__page:main, side:"after"

Используйте только зоны, которые находятся в геометрии. zone Внешне он не нарисован на layoutConfig v:2 сетки и земли за пределами сетки иначе — либо не Где ты имел в виду (__page:bottom на main-only page).

API=https://api.vizen.shop; T="Bearer $VIZEN_TOKEN"

# 1. what the page already has: content_blocks[] sits NEXT TO result
curl -s "$API/products/2505" -H "Authorization: $T" > page.json
# {"result":{…}, "content_blocks":[{"id":"3600","name":"__page:top",…}, {"id":"3601",…}],
#  "design":{"page":{"body_page":"<base64>","theme_page":"<base64>",…}}, …}
jq -r '.design.page.body_page // empty' page.json | base64 -d | jq '{layout, layoutConfig}'

# 2. your insertion as a block — here an existing component; an html block or any
#    other block goes in the same way, a finished folder is not turned into a component
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"item":{
  "name":"Promo banner (Volstreet 120)",
  "sections":[{"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
     "ref":42,"params":{"title":"Осень"}}}}}]}}'
# → {"result":{"id":"3701", …}}

# 3. the zone after the body. Classic page without "__page:bottom" in step 1 → create it:
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"page_design_version":1,
  "item":{"name":"__page:bottom",
  "sections":[{"type":"text","payload":{"v":2,"kind":"zone","refs":[3701]}}]}}'
# → {"result":{"id":"3702", …}}
#    Grid page → the same call with "__page:main" and a placement:
#    "payload":{"v":2,"kind":"zone","refs":[3701],
#      "props":{"zoneFlow":{"v":1,"entries":[{"entryId":"promo-3701","ref":3701,"side":"after"}]}}}
#    The manifest exists → GET /content-blocks/{id}, add 3701 to refs (and its zoneFlow entry),
#    PUT the whole sections list back with "page_design_version":1.

# 4. bind the manifest AND the block — replace-set: also send every id from step 1
curl -s -X PUT "$API/products/2505/content-blocks" -H "Authorization: $T" -d '{"items":[
  {"block_id":"3600","sort_order":0}, {"block_id":"3601","sort_order":1},
  {"block_id":"3702","sort_order":2}, {"block_id":"3701","sort_order":3}]}'

Правила зон:

  • `refs` есть JSON номера ([3701], не ["3701"]). Сервер проверяет zoneFlow против числовых refs, и его "block "Сторонники" номера матчей Только: Струна ref Позволь block быть deleted Из-под него page.
  • А. ref должны быть привязаны к page (Шаг 4. А). ref Это не является обязательным skipped, Это не является error.
  • `__page:top` Также держится page settings в его section's page объект (hideSiteHeader, hideSiteFooter, hideSystemBlock, тот layout). Редактировать это manifest — Читать, менять refs, Отправляйте каждый section назад — never воссоздать его.
  • `zoneFlow` это {"v":1,"entries":[…]} с одной записью на ref, В том же самом order: entryId Уникальный не-empty струна, ref то же самое number, side это before или after тот body Внутри этого zone. Если manifest иметь zoneFlow, добавлять Вход для каждого ref Добавить: Несоответствие ответов 400 PAGE_ZONE_FLOW_INVALID. Без zoneFlow каждый ref считать как after.
  • Отправить "page_design_version":1 В корне каждой буквы а __page:* manifest: Сетка для письма data (page.layoutConfig, page.zoneProps, zoneId в zoneFlow), или редактировать manifest Что уже есть, ответы 400 PAGE_DESIGN_WRITER_UNSUPPORTED без него.
  • Утвержденный product Поделиться на рынке (is_shared) возвращается к умеренность after Шаг 4, как after Любые изменения покупателя content.

проверять: GET /products/2505 Список manifest и block в content_blocks, и manifest's refs Возвращайтесь в числа; GET /design/bindings/product/2505 Ответы на вопросы body source из shop, category:<id> или default, не own — тот template Потом наследуется. тот page.

откат, в этом order:

  1. PUT /products/2505/content-blocks с точно таким items Ступень 1.
  2. The manifest ты created → DELETE /content-blocks/3702; а manifest Это было там → PUT его sections с шагом 1 назад (с "page_design_version":1).
  3. DELETE /content-blocks/3701. Хотя любой live manifest Все еще перечисляет block, Ответы на эти вопросы 400 DESIGN_BLOCK_IN_USE — Это то, что why Последний.

4.7.2 Дайте один page другой body

А. page body Один из них design group (chrome.md §3.2) и a group принадлежащий Кто бы это ни был, изменить структуру одного page's body ты копировать body group это page наследовать, Измените копию и свяжите копию к этой resource.

Если group Вы копируете, что пришло из page Строитель (его) section иметь props.zoneFlow или page.zoneProps), копировать его sections целое, keep его group_type (category для category page), наносить "page_design_version": 1 В корневой части Запрос и изменение refs в обоих widgets и zoneFlow.entries (/docs/chrome §3.1 Сохранить карту связывания before Шаг 4: Восстановить THAT карта на откат назад (§4.8 шаг 6)) — {"slots":{}} При этом правильнее будет только тогда, когда resource иметь нет own связывание.

API=https://api.vizen.shop; T="Bearer $VIZEN_TOKEN"

# 1. what does this page use today, and where does it come from?
curl -s "$API/design/bindings/product/2505" -H "Authorization: $T"
# {"own":[], "resolved":{"body":{"block_id":"3410","source":"shop"}, …}}
#                                             ^ inherited from the shop template

# 2. read that group (4.2 endpoint — a group is a content block)
curl -s "$API/content-blocks/3410" -H "Authorization: $T" > body.json

# 3. a group does NOT hold sections: it holds ONE section kind:"group" whose
#    props.widgets lists widget BLOCKS. So first make your insertion a block…
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"item":{
  "name":"Promo banner insertion",
  "sections":[{"type":"text","payload":{"v":2,"kind":"component","props":{"component":{
     "ref":42,"params":{"title":"Осень"}}}}}]}}'
# → {"result":{"id":"3701", …}}

#    …then create YOUR copy of the group: the widgets from step 2 plus yours
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"item":{
  "name":"Product card + promo banner (Volstreet 120)",
  "group_type":"product",
  "sections":[{"type":"text","payload":{"v":2,"kind":"group",
     "props":{"widgets":[ …ids from step 2…, 3701 ]}}}]}}'
# → {"result":{"id":"3702", …}}
#    Sections put straight into the group answer 400 DESIGN_SLOT_NEEDS_GROUP at step 4.

# 4. bind the copy to this product only
curl -s -X PUT "$API/design/bindings/product/2505" -H "Authorization: $T" \
  -d '{"slots":{"body":3702}}'

category/<id> вместо product/<id> Делает то же самое для одного category page; category_children/<id> для каждого product из субдерево. group_type должен соответствовать тот page kind (product, category) — Лестница в §3.2 chrome.md выбор groups через type.

проверять: GET /design/bindings/product/2505 должен отвечать "body":{"block_id":"3702","source":"own"}, и соседней product должен Ответить "source":"shop". Откройте оба pages. The product's own address Находится на product само собой — GET /products/2505 возврат url (/catalog/beds/krovat-loft-volstrit-120) и slug; список return url слишком.

Сколько стоит копия. Это копия, а не наложение:

  • тот page Прекратить наследовать shop template. Изменить shop-широкий product body Позже и это page Старый держит - никто не предупреждает. связывать resource Возвращение к наследованию, когда исключение больше не требуется;
  • замена на CATEGORY listing меняет свою capabilities: фильтры для запасов, Сортировочные средства и их ItemList JSON-LD Не появляется только потому, что компонент рисует плитки. Собственная разметка поддерживает products_page ломка для page's section. Выберите сознательно, capabilities то keep; Замена только одного listing в существующем group Рецепт в §4.8;
  • тот product feed 48. items за page (own-markup.md §2): page's own section pages с ?page=N и products_page, а section названный в вставке (productsCategoryId) получает first page Только в том section's own order. The Вставка также может попросить выбранного attributes (productsAttributes) и одной плиткой на product group с переключаемым members (productsMode: "collapse", paging через groups) — рядом с ref и params, never внутри params (own-markup.md §2).

откат, в этом order:

curl -s -X PUT "$API/design/bindings/product/2505" -H "Authorization: $T" -d '{"slots":{}}'
curl -s -X DELETE "$API/content-blocks/3702" -H "Authorization: $T" -d '{}'
curl -s -X DELETE "$API/content-blocks/3701" -H "Authorization: $T" -d '{}'

{"slots":{}} является заменой-set Это разъединяет все и ставит page назад Наследственная лестница (chrome.md §3.2) — сделать before удалять group, или page рендеринг с висячим связыванием. {"slots":{"body":0}} это Не откат: 0 Это значит "это" page не имеет body В любом случае" (§3.3 там)).

4.7.3 Один и тот же блок на каждом product одного или нескольких sections

Один баннер, записка о доставке или промо-стрип на всех стульях и на всех полках: поставьте его в feed (chrome.md §3.4 и связать feed в поддрево каждой секции. The body Продолжает следовать за shop Шаблон, блок существует один раз, и один редактировать Каждый меняет page. Копировать body Для этого (§ 4.7.2) также работает, но копии перестают наследовать шаблон до тех пор, пока они связаны.

# 1. the block itself — any widget or html block; here an existing one, 3801
# 2. a design group holding it
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"page_design_version":1,
  "item":{"name":"Promo","group_type":"design",
  "sections":[{"type":"text","payload":{"v":2,"kind":"group","props":{"widgets":[3801]}}}]}}'
# → {"result":{"id":"3802", …}}

# 3. the feed: the body marker -1 and your group in display order (here after the body)
curl -s -X POST "$API/content-blocks" -H "Authorization: $T" -d '{"page_design_version":1,
  "item":{"name":"Promo feed","group_type":"layout",
  "sections":[{"type":"text","payload":{"v":2,"kind":"group","props":{"widgets":[-1,3802]}}}]}}'
# → {"result":{"id":"3803", …}}

# 4. per section: PUT is a replace-set, so read the subtree's own slots first
#    and send them back together with feed
curl -s "$API/design/bindings/category_children/48" -H "Authorization: $T"
# → {"own":[], "resolved":{"feed":{"block_id":"0","source":"default"}, …}}
curl -s -X PUT "$API/design/bindings/category_children/48" -H "Authorization: $T" \
  -d '{"slots":{"feed":3803}}'
# the same for category_children/49 …

проверять: GET /design/bindings/product/<a product of section 48> ответы "feed":{"block_id":"3803","source":"category:48"} и его body Что это было before. Открыть product После этого из каждого раздела один раз изменить блок 3801 и перезагрузить оба.

Что? feed ты стоишь. Это заменять тот feed эти products наследуемый Родительский отдел, то есть layout (theme) или shop. Если шаг 4 показывает resolved.feed кроме других 0, Начните свой список с этого feed'Список и добавить Ваша группа или эти страницы теряют то, что она показала. product с его own feed Обязательный сохраняет свою own. Заголовок, body, footer и каждый page's own зоны (§4.7.1)) Не меняйся.

откат, в этом order: PUT каждый category_children/<id> Вернемся к own Вы читаете шаг 4 (own: [] → {"slots":{}}), затем DELETE /content-blocks/3803 и 3802 (и 3801, если вы сделали это. Все еще указывая на feed ответы 400 DESIGN_GROUP_IN_USE, Список все еще удерживая группу 400 DESIGN_BLOCK_IN_USE — Сначала сцепление, затем Одинокий неиспользованный layout или design Группа может быть удалена. DELETE мягкий (deleted_after +30 дней, но нет звонка для восстановления и Удаленный блок больше не читается: сохраните то, что вы читаете before удаление.

4.8 Заменить category's Сеть через сетку через PAT, с откатом

Этот рецепт заменяет собой listing внутри существующего category body group, Сохранять его banner и другие blocks. Это не то, что product-body Копия рецепта §4.7.2 Если resolved group это inherited/shared и только один page должны меняться, first копировать и связывать его с этим category (§4.7.2); кроме предыдущего связывания. Никогда не редактируйте общий group Если предположить, что он принадлежит только одному page.

  1. удостоверять подлинность first использовать Ключи и разрешения, включая обязательную проверку паспорта before пишет. Читать GET /design/bindings/category/<id>, затем GET тот resolved body group. инспектировать GET /content-blocks/{id}/usage: total считать прямым design связывание плюс manifest ссылки (groups/layouts/zones); refs Возврат максимум 100 Из них не все pages достигаемый по наследству. В ответе на связывание, resolved.body.source: "own" означает прямой body выбор; другой source (Например, "shop") Наследственность. own Только связывание не доказывает исключительность: проверка usage И спаси меня. GET ответ локально и подготовить запрос на откат before писать:

``bash API=https://api.vizen.shop GROUP=YOUR_RESOLVED_BODY_ID # resolve for your target category curl --fail-with-body -sS "$API/content-blocks/$GROUP" \ -H "Authorization: Bearer $VIZEN_TOKEN" > group-before.json jq '{page_design_version:1,item:(.result|{name,group_type,sections})}' \ group-before.json > group-rollback.json ``

Уже заменены? Если body это own, запас listing отсутствует и Ваша вставка blocks уже присутствует, keep Обновить эти ссылки. существующий вставной settings или publish а new освобождение его document (§4.4); не создавать дубликаты вставок или запускать замену снова. Ожидаемый старый listing И ваши вставки отсутствуют, проверьте body before Писать вместо того, чтобы угадывать замену target.

  1. Подготовить компонент. Проверяйте каждый scope field Ваша наклейка читается против GET /docs/vz-keys.json → scope. А. field отсутствует cannot быть передан Замена сервера: не publish , в зависимости от того, изобрели ли они key; О необходимости создания платформы owner. рекорд sets не находится в scope — Прочитайте их вместе с public GET /record-sets (/docs/record-sets §4.4). Upload/publish Компонент по §4.1 и проверить принятого schema. Доступ покупателя для attributes и топоры: /docs/catalogue §8.3; правила разметки и images: /docs/own-markup §§4,6; theme выравнивание: /docs/chrome §3.7 Не трудно-code каталожные цены или вычисления combo цитаты.
  1. Создать вставку blocks без привязки к ним. Использовать POST /content-blocks В двух полных телах в семья cards §3. Это и есть каноническая вставка. content, включая show_catalog:false для тот combo вставки и их full-width, ноль-padding wrapper. Заменить все иллюстративный ids с твоим own. Спасти семью block's result.id как FAMILIES, и optional combo block's id как COMBOS. Последний выбирает set ids и петли над p.combo_items. Держите оба несвязанными до тех пор, пока не проверят. Семантика выбора: /docs/catalogue §4.1 и /docs/own-markup §2.
  1. Создайте замену из snapshot. Определить запас listing block id в group's props.widgets, Читать далее block, и проверить его kind Это listing Ты собираешься заменить, сохранить все остальные. block, page собственности и zone Настройка. /docs/chrome §3.1 для двух списков Инвариант и писатель version. Пример требует OLD Происходит ровно один раз; повторяющийся refs, target конкретный entryId Вместо этого, пример. Заменять один listing с двумя blocks:

``bash OLD=YOUR_LISTING_BLOCK_ID # verified listing id, not the whole group # FAMILIES and COMBOS are the new result.id values from step 3 jq --argjson old "$OLD" --argjson a "$FAMILIES" --argjson b "$COMBOS" ' .item.sections |= map( if .payload.kind == "group" then .payload.props.widgets |= map(if . == $old then $a,$b else . end) | if .payload.props.zoneFlow then .payload.props.zoneFlow.entries |= map( if .ref == $old then (.ref=$a | .entryId=("pat-"+($a|tostring)+"-families")), (.ref=$b | .entryId=("pat-"+($b|tostring)+"-combos")) else . end) else . end else . end)' group-rollback.json > group-next.json ``

Проверьте сгенерированную запись ids Уникальность в этом group, Оба списка имеют совпадение order/count, И только предполагаемое событие было заменено. Пример сохраняет старое явление's zone/side для обоих new Если вы не против. Хочется другого zone, select явно не падая page.zoneProps. подтверждать item.sections с POST /docs/validate (§4.2; не проверяется существование refs, писатель version или group Потребители. Перечитайте group before PUT; Если он изменился с тех пор, как snapshot, Помириться вместо переписывания.

  1. Переключай один раз, потом проверь.

``bash curl --fail-with-body -sS -X PUT "$API/content-blocks/$GROUP" \ -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \ --data-binary @group-next.json ``

GET Опять же: подтвердить оба списка ссылок, new ids и сохраненный zone свойства. Проверьте сервер HTML, Затем браузер (/docs/troubleshooting §2): first без панелей JS, size/colour переключатели, недоступная комбинация, SKU/price/ image/cart Аргументы вместе, combo состав, mobile выравнивание и images. Проверь еще category Только 200 не принимаются, это не должно оставаться неизменным..

  1. откат использует один и тот же автор и exact спасенный sections, не является empty Удаление или переплетение старого listing:

``bash curl --fail-with-body -sS -X PUT "$API/content-blocks/$GROUP" \ -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \ --data-binary @group-rollback.json ``

Перечитайте и проверьте page. Если шаг 1 created Эксклюзивная копия, также восстанавливается Сохраненная карта (не слепо) slots:{}). Сохраните компонентный выпуск Rollback (§4.4 отдельно: восстановление group не восстанавливает высвобождение разделяемого компонента. Не удаляйте старые blocks или snapshot files во время принятия.

Учебник использует один многоразовый level-1 компонент с семейной вставкой и ан optional combo Решить каждый вопрос target category's body независимо; пример ids являются локаторами, never Платформы по умолчанию.

5. TypeScript

Поставить app.ts в папке и указать разметку в составленный file:

<div class="promo">{{title}}</div>
<script type="module" src="app.js"></script>
// app.ts
type Options = { root: string };

const opts: Options = { root: ".promo" };

export function init(): void {
  const el = document.querySelector<HTMLElement>(opts.root);
  if (el) el.dataset.ready = "yes";
}

init();

Публикация релиза собирает все *.ts Отделение в сибирской *.js и вносит его в освобождение Рядом с ним source. The source оставаться: Следующий выпуск построен из base=active, и папку, которая .ts был Выброшенный не может быть отредактирован без его загрузки снова. и ничто не минируется — типы стираются, то есть все.

Что составляется

  • каждый *.ts На любой глубине папки — app.ts, ui/menu.ts;
  • ничего else. *.tsx нуждается в JSX Завод, где нет выбора, *.d.ts компиляции в один empty file, и node_modules/ кто-то есть else's delivery. Те, кто files Оставайтесь в освобождении нетронутыми и неиспользованными.

Как? result Загруженный определяется вашим тегом, а не компилятором. The модульный синтаксис source Сохраняется (target ES2020): а source с import/export Остается модуль и работает под <script type="module" src="app.js">; а source Без них остается обыкновенным script С теми же самыми глобалами, что и у него, onclick="init()" в накладной держит Работает. IIFE Обернут вокруг вашего code За спиной.

Импортные пути - это не Переписывается также: Пишите import { x } from "./util.js" — тот file Браузер действительно будет. "./util.ts" публикует с предупреждением, а затем 404s в браузере; не имеющий расширения "./util" Отклоняется как сломанная ссылка, точно так же, как в рукописный .js.

Только `kind=component`. Ан html widget и а layout папка publish точно так же, как before — а .ts В них есть только file Что никто не компилирует.

Отказы — 422 с {"error":"RELEASE_INVALID","report":{"errors":[…]}}, той же формы, что и schema error:

codeкогдаfile · ref
COMPONENT_TS_ERRORтот source не поддается компиляции; а также size limit или тайм-ауттот source · app.ts:3:19 — line 1-я колонка на основе
COMPONENT_TS_CONFLICTapp.js находится в папке рядом с app.tsтот .js · тот .ts сталкиваясь с

Готовый app.js это never Скриншоты из игры If You Compiled the script себя, drop тот .ts или одно из двух (построено)'s own warnings приходить обратно report.warnings с code: COMPONENT_TS_WARNING и не block тот publish.

пределы: 512 KB за source, 4 MB из TypeScript за освобождение, 10 s из Компонент - это часть разметки, а не приложение. — а bundle это hits Они принадлежат к html проект (transfer.md).

⚠️ В level 1. page never получает script. составленный file обслуживается тот gateway В любом случае (/_html/{doc}/{release}/app.js, text/javascript), но тот level‑1 санитарная полоска <script> из собранных HTML. Для Браузер для запуска его document должен быть level 3 (author-принадлежащих ему, как и для html Проект — тот же rule который ест <link rel="stylesheet">, См. § 6.

⚠️ Две вставки выполняются script дважды. Каждая вставка a level‑3 Компонент несет в себе own <script>, И каждый бег видит EVERY копия на нем page. А. script которая связывает все .my-block nodes Каждый дарит каждому button два хозяина — а text Обновление скрывает его, переключатель дважды загорается и выглядит мертвым. инициализировать и пропустить:

document.querySelectorAll('.my-block:not([data-ready])').forEach(function (root) {
  root.setAttribute('data-ready', '')
  // bind inside root only
})

6.Безмолвно ignored

  • А. key Не в том schema — в обоих params сбрасывается (сбрасывается) и в разметке ({{whatever}} становится empty Нет сырой нити. {{ }} Для компонента: a placeholder либо имеет type Или это ничто.
  • А. value Неправильное type — Отсутствующий, поэтому default шоу. А. 200 на PUT /content-blocks Ничего не доказывается о value принимать effect; читать page.
  • А. `link`/`image` вне белого списка (javascript:, data:, //other-host) — тот value становится empty, и ан empty URL value означает атрибутом является Не написаны вообще, не написанный empty. Писать CSS против img:not([src]) и a:not([href]), не против img[src=""]. Белый список это https://, http://, mailto:, tel:, и тот же-site path — так #anchor, form:<id> и modal:… делать не выжить в link параметр. Чтобы сделать компонент button Открытый form или быстро-view окно, Объявить параметр text и поместить его value на витрине магазина key вместо того, чтобы href: <a class="btn" href="{{btnlink}}" vz-modal="{{openmodal}}">{{btnlabel}}</a>, с openmodal груз modal:product:2808?size=full. The value это Сбежавший, как любой text, и витрина магазина строго разбирает его. form Открывается тем же способом — text параметр, заменяемый на href, с тех пор level-1 санитар принимает form:<id> там.
  • А. `select` value снаружи `options`, а `color` Это не то, что `#hex` — empty.
  • Писать `params` Неправильное гнездование level (props.params, или ref рядом с kind) — тот section Только с неустойкой. path это props.component.ref / props.component.params И ничего else.
  • `kind` в `PUT /html-documents/{id}` — тот document's kind это fixed в Творение, field Не читается на редактировании.
  • Уровень 1-1 CSS/attribute дезинфицирующий — канонические правила и внутренняя <style> Рецепт находится в /docs/own-markup §4.А. CSS file в компоненте ZIP Сам по себе не означает, что page грузить его.
  • `alt` / `title` нести маркировку — эти attributes разрешены только для plain text, так text параметр, чей value В ней содержатся теги, в которых тот body но от этого атрибута. Это санитар, а не человек, который его оскорбляет. замена.

7. Пределы этапа 1

  • Нет. `products` / `category` параметры. Замена - это строка Операция; а product Список есть data, и data принадлежит к page сборщик ("а widget И только его own"). The type Он появится вместе с этим path, не before — И до тех пор, пока не будет в schema и а manifest использовать Отказано, громко, в publish.
  • Внутри компонента нет компонента, и нет <vz-component> звонить кто-то else's HTML.
  • Нет патрона. TypeScript Составлено (§5), но ничего не сложено: нет npm импорта, нет JSX/.tsx, нет source Карты. Все import должны решиться на a file из папки.
  • Платформа `vz-` Ключи остаются одни, не заменяются: {{ product.name }} проходит через это stage нетронутым и расширенным витриной's own двигатель с помощью page scope. Только голый {{key}} принадлежит компоненту.

Исходник: https://api.vizen.shop/docs/components?full=1

Работает в демо-режиме