Документация Maketa
Maketa — конструктор экранов приложений и лендингов. Рисуете мышкой, делитесь ссылкой вместо ТЗ. А ещё — программно: ИИ и код читают и меняют дерево экрана, а экраны Flutter выгружаются в макет автоматически.
Эта страница — для тех, кто интегрируется с Maketa: разработчиков, ИИ-агентов и любопытных. Просто порисовать можно в редакторе без всякой документации.
Три способа управлять макетом программно:
- MCP-сервер — Claude, ChatGPT, Cursor, VS Code или Cline читает дерево экрана, добавляет и переносит узлы, делает ветки и синхронизирует экраны из кода.
- maketa_sync — Flutter-пакет выгружает экран из работающего приложения в макет структурой, а не картинкой.
- REST API — прямые HTTP-запросы для своих сценариев.
Быстрый старт
- Откройте редактор и нарисуйте экран (или вставьте скриншот через Ctrl/⌘+V).
- Нажмите «🔗 Поделиться» — макет сохранится на сервере, появятся ссылки на просмотр и редактирование.
- Чтобы подключить ИИ или код — там же откройте «🔑 Ключи для ИИ и кода» и создайте проектный ключ.
- Дальше: MCP-сервер для ИИ или maketa_sync для Flutter.
Аккаунт не обязателен, но с ним макеты собираются в «Мои макеты» и не теряются. Клиенты demda.pro входят той же учётной записью — вкладка «Клиент demda» в окне входа.
Основные понятия
Доска (проект) и экраны
Доска — единица хранения (один макет). В ней несколько экранов. Модель сцены — наш собственный JSON maketa.board.v2, единый источник истины для редактора, ИИ и нативных клиентов. Экран в нём — дерево узлов, а не список фигур с координатами.
Документ несёт "v": 2. Макеты, сохранённые до перехода на дерево, приходят без этого поля и читаются по старой плоской модели maketa.board.v1 (objects: […]); она оставлена только ради них и здесь больше не описывается. Новый код всегда шлёт "v": 2.
Экраны «из кода» и «дизайн»
| Тип | Что это | Правка руками |
|---|---|---|
| design | Обычный экран, нарисованный в редакторе или ИИ. | Да |
| code | Выгружен из кода (Flutter). Источник истины — код. Каждый пуш — новая версия («коммит из кода»). | Нет (заблокирован) |
Ветки
У code-экрана нельзя менять узлы руками — но можно создать ветку: редактируемую design-копию, связанную с оригиналом (как в GitHub). Навигация двусторонняя: с оригинала — к веткам, из ветки — «← Оригинал». Когда код обновляется, ветки от старой версии помечаются «⚠ база обновилась». Всё дерево видно по кнопке «🌳 Дерево».
Доступ к макету: ссылки, права, соавторы
У доски одна ссылка для людей — https://maketa.pro/app/?b=BOARD_ID. Что по ней можно, решают права доски, а не сама ссылка.
Три режима доступа по ссылке
| Режим | Кто смотрит | Кто правит |
|---|---|---|
view — «Только просмотр» (по умолчанию) | любой, у кого есть ссылка; прототип кликается | владелец, соавторы, держатель edit-токена |
edit — «Может редактировать» | любой | любой, у кого есть ссылка |
none — «Только приглашённые» | владелец и соавторы | владелец и соавторы |
Переключается в «Поделиться» → «Доступ по ссылке» или запросом POST /board/link-access с телом {id, edit?, access}. Менять режим могут владелец (по сессии) и держатель edit-токена.
Четыре режима: edit — править по ссылке; comment — смотреть и оставлять замечания прямо на экранах, без регистрации (гость называет только имя); view — только смотреть; none — только приглашённые. Замечания живут отдельно от макета: GET/POST /board/comments, ответы /board/comments/reply, статусы и удаление — владельцу. Пин привязан к узлу дерева и переживает перекладку макета; координаты — доли [0,1] от рамки узла и экрана.
Соавторы по e-mail
До 50 адресов на доску. Соавтор входит своим аккаунтом, видит доску в «Моих макетах» и правит её при любом режиме ссылки; его правки объединяются с вашими, а не затирают их. GET /board/collab?id=&edit= — список, POST /board/collab с {id, edit?, action: "add" | "remove", email} — управление.
Анонимная доска
Доска, созданная без входа, по умолчанию открывается на просмотр; править её может только тот, у кого есть edit-токен из ссылки редактирования (?b=…&e=…). Попытка сохранить чужую доску по одному лишь id — 403. Это проверяется автотестом против прода при каждом выпуске.
Что даёт каждый токен
| Токен | Откуда | Что можно |
|---|---|---|
| edit-токен доски | ссылка «Может редактировать» | правка, права, ключи, соавторы этой доски |
| view-токен | старые ссылки просмотра | только просмотр — оставлен для совместимости, новым ссылкам не нужен |
mk_pk_… проектный ключ | кабинет владельца | headless-правка одной доски: код, CI, MCP |
mk_sb_… песочный ключ | POST /agent/register | доски песочницы агента, 14 дней |
| OAuth-токен MCP | вход в коннекторе | все доски аккаунта, 1 час (refresh — 30 дней) |
Перевыпуск ссылок
Edit-токен доски перевыпустить пока нельзя — он действует, пока существует доска, а «Удалить» в «Моих макетах» убирает макет только из вашего списка. Если ссылка редактирования попала не к тому человеку — откройте макет, сделайте копию (у новой доски свои токены) и дальше работайте в ней.
Проектные ключи
Проектный ключ (mk_pk_…) разрешает ИИ и коду пушить экраны в конкретную доску — без вашего пароля и сессии. Отдельно от ссылок просмотра/редактирования.
Создать в кабинете
В редакторе: «🔗 Поделиться» → «🔑 Ключи для ИИ и кода». Кнопка «Создать ключ» — ключ показывается один раз, скопируйте сразу. Там же список ключей и «Отозвать».
Храните ключ в секрете — не коммитьте в git. Передавайте через переменные окружения / --dart-define. Скомпрометированный ключ отзовите в кабинете.
Или через API
curl -X POST https://maketa.pro/maketa/api/project/key \
-H 'content-type: application/json' \
-d '{"id":"BOARD_ID","edit":"EDIT_TOKEN","label":"CI"}'
# → {"key":"mk_pk_..."}
MCP-сервер (для ИИ)
MCP даёт нейросети инструменты читать и менять объекты макета, создавать экраны, делать ветки и синхронизировать экраны из кода. Сервер удалённый — ставить нечего, один URL:
https://maketa.pro/maketa/api/mcp
Работает с Claude (Code, веб, десктоп), ChatGPT, Cursor, VS Code, Cline и любым клиентом с поддержкой удалённых MCP-серверов. Английская версия этого раздела — Maketa MCP Server (English).
1. Удалённый коннектор с OAuth 2.1 — рекомендуется
Клиент открывает вход Maketa: авторизуйтесь единым аккаунтом (Maketa или клиент demda) и подтвердите доступ. Дальше ИИ работает только с вашими досками — начните с maketa_list_boards. Ключи прописывать не нужно, доступ отзывается в аккаунте.
- Authorization code + PKCE S256, публичный клиент, refresh-токен на 30 дней (токен доступа — 1 час).
- Динамическая регистрация клиента (RFC 7591):
POST /maketa/api/oauth/register— клиент регистрируется сам, как у коннектора Figma. - Метаданные для строгих клиентов:
https://maketa.pro/.well-known/oauth-protected-resourceи…/oauth-protected-resource/maketa/api/mcp(RFC 9728 §3.1),…/.well-known/oauth-authorization-server(RFC 8414). - Запрос без токена отвечает
401с заголовкомWWW-Authenticate: Bearer resource_metadata="…"— клиент находит вход сам.
Подключение в один клик
| Клиент | Как |
|---|---|
| Claude Code | claude mcp add --transport http maketa https://maketa.pro/maketa/api/mcp |
| Claude — веб и десктоп | Settings → Connectors → Add custom connector → вставить URL |
| Cursor | Добавить в Cursor или Settings → Tools & Integrations → New MCP Server и URL вручную |
| VS Code | Добавить в VS Code (нужен Copilot Chat в агентном режиме) |
| ChatGPT | Settings → Connectors → режим разработчика → Create → URL. Только на платных тарифах |
| Cline и остальные | {"mcpServers":{"maketa":{"type":"streamableHttp","url":"https://maketa.pro/maketa/api/mcp"}}} |
Те же кнопки с копированием — на главной.
Инструменты — 13
| Инструмент | Что делает |
|---|---|
maketa_list_boards | Доски, доступные этому аккаунту или ключу. Всегда первый вызов |
maketa_list_screens | Экраны доски со сводкой: тип (code/design), блокировка, число узлов, слоты, ветки, версия. Поле model говорит, дерево это (v2) или старый плоский экран (v1) |
maketa_get_screen | Один экран целиком, как он лежит на диске: slots и плоская карта nodes |
maketa_get_tree | Дерево экрана вложенным видом по слотам — читать структуру так дешевле и понятнее, чем картой. Отдаёт ещё detached: узлы, ни к кому не привязанные |
maketa_create_screen | Новый design-экран модели v2. Сразу получает корневой блок в слоте body — его id приходит в bodyId, первые узлы кладите в него |
maketa_add_node | Добавить узел в дерево: node плюс parentId (внутрь блока) или slot (top/body/bottom/over) и необязательный index |
maketa_update_node | Частичный патч узла: name, w, h, layout, look, content, action, hidden, repeat. layout, look и content сливаются по полям: {"content":{"text":"Новый"}} не сотрёт размер и цвет; "action": null снимает действие |
maketa_move_node | Перенести узел к другому родителю или в слот и сменить порядок среди детей. В v2 это и есть «переместить»: координат у узла нет |
maketa_remove_node | Удалить узел вместе со всем поддеревом; ссылки на удалённое в действиях и пунктах снимаются сами |
maketa_branch_screen | Редактируемая ветка экрана — единственный способ править code-экран |
maketa_push_code_screen | Upsert code-экрана из исходного кода по стабильному ключу; каждый пуш — новая версия |
maketa_list_branches | Ветки экрана |
maketa_get_parent | Родитель ветки — вернуться к оригиналу из кода |
Только читают: list_boards, list_screens, get_screen, get_tree, list_branches, get_parent. Остальные пишут в доску. Инструментов *_element больше нет: узел не двигают в координаты — его переносят к другому родителю. Ошибки инструментов приходят как isError: true с текстом по-русски; имена инструментов и схемы аргументов — английские, описания в tools/list пока на русском.
2. Headless — без OAuth и браузера
Тот же URL принимает ключи напрямую: Authorization: Bearer mk_pk_… (проектный ключ — инструменты в рамках своей доски, boardId обязателен) или Bearer mk_sb_… (песочный ключ агента — в рамках досок песочницы). Ключ принимается и в заголовке x-maketa-key.
curl -s -X POST https://maketa.pro/maketa/api/mcp \
-H "Authorization: Bearer mk_sb_…" -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
3. Локальный сервер (stdio) — для CI и своих сценариев
Zero-dependency файл на Node ≥18 — mcp/maketa-mcp.mjs, конфигурируется env: доска, edit-токен, проектный ключ. Доска задаётся заранее, поэтому инструмента maketa_list_boards в нём нет — остальные 12 те же, с теми же именами и схемами аргументов.
Вопросы по интеграции — support@maketa.pro. Код сервера открыт под MIT: github.com/demdas/maketa-mcp. Maketa есть и в официальном реестре MCP под именем pro.maketa/maketa — домен подтверждён DNS-записью.
{
"mcpServers": {
"maketa": {
"command": "node",
"args": ["/path/to/mcp/maketa-mcp.mjs"],
"env": {
"MAKETA_BOARD": "BOARD_ID",
"MAKETA_EDIT": "EDIT_TOKEN",
"MAKETA_PKEY": "mk_pk_..."
}
}
}
}
Что стоит знать до первой записи
- Code-экраны не правятся напрямую (источник — код): ИИ вызывает
maketa_branch_screenи правит ветку-предложение. - Координат у узла нет. Место ребёнка задаёт родитель порядком в
childrenи своей раскладкой; «подвинуть» — этоmaketa_move_nodeс новымparentIdилиindex. - Кликабельным макет делает
action—{t:"goto",screen,mode}у узла илиlinkу пунктаtabs. Без переходов макет — набор картинок. - Нижнее меню — один узел
tabs, а не пять прямоугольников; кнопка —button, а не прямоугольник с текстом; поле ввода —input; группа переключателей — одинchoice. - Десять одинаковых строк — это
repeat: 10на одном узле, а не десять копий. - Целостность дерева — отказ, а не тихая починка. Висячая ссылка, узел у двух родителей, цикл и глубина больше 12 возвращают ошибку с указанием узла:
unknown_node,node_two_parents,tree_cycle,too_deep. - Транспорт — Streamable HTTP без SSE: только
POST, ответapplication/json, сессий нет. Версию протокола сервер возвращает ту, что прислал клиент вinitialize(по умолчанию2024-11-05). - Лимит — 600 запросов в час на ключ или пользователя.
Автономные агенты
Автономный ИИ-агент проходит путь «регистрация → доска → экран → ссылка → повторное чтение» без человека. «Без регистрации» на лендинге относится к браузеру; агенту нужен песочный ключ — он выдаётся мгновенно, без почты и оплаты.
Полная машиночитаемая схема всех ручек и ошибок — /openapi.json (OpenAPI 3.1).
Путь агента (curl)
# 1. Регистрация (лимит 3/сутки с IP; ключ показывается один раз, живёт 14 дней)
curl -s -X POST https://maketa.pro/maketa/api/agent/register \
-H 'content-type: application/json' \
-d '{"name":"my-agent","purpose":"макет для клиента"}'
# → {"key":"mk_sb_…","expiresAt":…,"quotas":{"boards":5,"savesPerDay":200},…}
# 2. Создать доску (доска привязывается к ключу). Пустой экран v2 — это слот body
# с корневым блоком и карта nodes, а не objects: [].
curl -s -X POST https://maketa.pro/maketa/api/create \
-H 'content-type: application/json' -H 'x-maketa-key: mk_sb_…' \
-d '{"doc":{"v":2,"name":"Демо","screens":[{"id":"s_1","name":"Главная","device":"iphone-15","w":393,"h":852,"bg":"#FFFFFF","slots":{"body":"n_body"},"nodes":{
"n_body":{"id":"n_body","type":"stack","name":"Содержимое","w":"fill","h":"fill","layout":{"dir":"down","pad":16,"gap":12}}}}]}}'
# → {"id":"BOARD_ID","edit":"…","view":"…"}
# 3. Наполнить экран деревом (edit-токен не нужен — своя доска по ключу).
# Порядок и место задают children и layout родителя; пять одинаковых
# карточек — это repeat:5, а не пять копий узла.
curl -s -X POST https://maketa.pro/maketa/api/save \
-H 'content-type: application/json' -H 'x-maketa-key: mk_sb_…' \
-d '{"id":"BOARD_ID","baseRev":1,"doc":{"v":2,"name":"Демо","screens":[{"id":"s_1","name":"Главная","device":"iphone-15","w":393,"h":852,"bg":"#FFFFFF",
"slots":{"top":"n_hdr","body":"n_body","bottom":"n_nav"},"nodes":{
"n_hdr":{"id":"n_hdr","type":"stack","w":"fill","h":56,"look":{"bg":"#FFFFFF"},"layout":{"dir":"right","pad":[0,16,0,16],"gap":8,"cross":"center"},"children":["n_ttl"]},
"n_ttl":{"id":"n_ttl","type":"text","content":{"text":"Меню","size":22,"weight":700}},
"n_body":{"id":"n_body","type":"stack","w":"fill","h":"fill","layout":{"dir":"down","pad":16,"gap":12,"scroll":true},"children":["n_card"]},
"n_card":{"id":"n_card","type":"stack","w":"fill","repeat":5,"look":{"bg":"#F7F8FA","radius":14},"layout":{"dir":"down","pad":12,"gap":4},"children":["n_name"]},
"n_name":{"id":"n_name","type":"text","content":{"text":"Пицца «Маргарита»","size":16,"weight":600}},
"n_nav":{"id":"n_nav","type":"tabs","w":"fill","h":82,"content":{"variant":"bottom","active":0,"items":[{"text":"Меню","icon":"🍕"},{"text":"Корзина","icon":"🛒"}]}}}}]}}'
# 4. Ссылка для людей: https://maketa.pro/app/?b=BOARD_ID
# 5. Чтение: curl -s 'https://maketa.pro/maketa/api/load?id=BOARD_ID'
# Свои доски: GET /agent/boards; сверка ревизии: GET /rev?id=BOARD_ID
Квоты и ошибки
| Ситуация | Ответ |
|---|---|
| 6-я доска на ключ | 403 {"error":"quota_boards","hint":…} |
| 201-е сохранение за сутки | 429 {"error":"quota_saves","hint":…} |
| Просроченный ключ | 401 {"error":"key_expired","hint":…} — регистрируйте новый |
| Неизвестный/отозванный ключ | 401 {"error":"bad_key","hint":…} |
| Удалённая доска | 410 {"error":"gone","deletedAt":…,"reason":…} |
Каждая ошибка API несёт машинный код error и русскую подсказку hint — агент понимает, что делать дальше, без чтения документации.
Политика времени жизни
- Песочный ключ — 14 дней; квоты: 5 досок, 200 сохранений/сутки. ИИ-функции (
/ai/*) и оплата песочному ключу недоступны. - Доски песочницы живут, пока жив ключ, плюс 30 дней; затем удаляются (навсегда, ответ 410). Доска, которую забрал живой аккаунт, становится обычной и не удаляется.
- Повышение до постоянного доступа — проектный ключ
mk_pk_: его выдаёт владелец доски в кабинете, он не истекает, пока не отозван. - Доски по заданиям студии demda.pro при миграциях сохраняются.
Flutter · maketa_sync
Dev-пакет maketa_sync обходит render-дерево текущего экрана Flutter и пушит его в Maketa как code-экран — каждый виджет отдельным объектом (не скриншот).
Подключение
# pubspec.yaml
dependencies:
maketa_sync:
path: ../path/to/maketa/sync/dart # репозиторий github.com/demdas/maketa
final _sync = MaketaSync(
apiBase: 'https://maketa.pro/maketa/api',
boardId: const String.fromEnvironment('MAKETA_BOARD'),
projectKey: const String.fromEnvironment('MAKETA_PKEY'),
);
// на корне экрана:
KeyedSubtree(key: _sync.rootKey, child: MyScreen());
// пуш (dev-кнопка / хоткей / хук на hot-reload):
await _sync.capture(screenKey: '/search', name: 'Поиск', route: '/search');
flutter run \
--dart-define=MAKETA_BOARD=BOARD_ID \
--dart-define=MAKETA_PKEY=mk_pk_...
Таблица соответствий
| Flutter render-узел | Объект Maketa |
|---|---|
RenderParagraph | text (строка, размер, цвет, вес) |
RenderDecoratedBox + BoxDecoration | rect / ellipse (заливка, скругление, обводка) |
| Material / Card / PhysicalShape | rect (цвет, скругление) |
RenderImage | rect-плейсхолдер |
Работает на Android и iOS. Для Flutter Web (dart:io недоступен) — см. Веб-приложения.
Оба экстрактора (Flutter и веб) пока отдают экран в старом плоском формате — /screen/push и maketa_push_code_screen принимают и его, и дерево v2 (screen.slots + screen.nodes); формат различается по наличию карты узлов. Перевод экстракторов на дерево — отдельный этап.
Веб-приложения
Для веба Flutter не нужен — большинство сайтов нативные (React, Vue, обычный DOM). maketa-sync-web — небольшой JS без сборки: обходит DOM и getComputedStyle, превращает элементы страницы в экран и пушит тем же /screen/push по проектному ключу (CORS разрешён).
<script src="https://maketa.pro/maketa-sync-web.js"></script>
<script>
MaketaSyncWeb.push({
boardId: 'BOARD_ID', projectKey: 'mk_pk_...',
screenKey: location.pathname, name: document.title
});
</script>
Или букмарклетом — на любом своём экране одним кликом. Круглые элементы → ellipse, текст → text, картинки → плейсхолдер. Подробнее — sync/web/README.md в репозитории demdas/maketa.
REST API
База: https://maketa.pro/maketa/api. Ответы — JSON. Доступ к доске — по токенам edit/view (из ссылки «Поделиться») или проектному ключу (заголовок x-maketa-key).
Доски
| Метод | Путь | Назначение |
|---|---|---|
| POST | /create | Создать доску → {id, edit, view} |
| POST | /save | {id, edit, doc, baseRev?} — сохранить сцену целиком |
| POST | /save/patch | {id, edit, baseRev, opId?, ops[]} — сохранить дельту: операции {op: set|del, path, value?} над узлами (screens/<sid>/nodes/<nid>), метаданными экрана (screens/<sid>), сквозными блоками (shared/slots, shared/nodes/<nid>), именем (name) и порядком экранов (screensOrder). Только v2; baseRev обязателен, 409 приходит с текущим doc; повтор с тем же opId не применяется дважды. ≤ 2000 операций, тело ≤ 1 МиБ |
| GET | /load?id=&edit=|view= | Загрузить сцену |
| GET | /rev?id= | Лёгкая сверка ревизии → {rev, updatedAt} |
| POST | /board/link-access | {id, edit?, access: edit|comment|view|none} — режим доступа по ссылке |
| GET | /board/comments | ?id&edit? — замечания к макету: треды с пинами, canComment, canManage |
| POST | /board/comments | {id, edit?, screenId, anchor?|floating, text, name, t0} — новое замечание (гость — по ссылке с правом comment); /reply, /status, /delete, /settings, /ban — ответ, статус, удаление, закрытие приёма, блокировка гостя |
| GET POST | /board/collab | Соавторы по e-mail: список / {id, edit?, action, email} |
| POST | /board/flex-used | {id, edit?, scope?} — засчитать применение «Сделать гибким» (счётчик на макет живёт на сервере) → {used, max} |
| GET | /plans | Каталог тарифов: цены за месяц и год, пределы каждого тарифа, enabled. Без авторизации |
Тарифы и ответ 402 plan_limit. Когда тарифы включены (GET /plans → enabled:true), у бесплатного тарифа есть пределы: 3 макета, 10 экранов и 2 МБ на макет, 1 соавтор-редактор, 3 применения «Сделать гибким»; проектные ключи и запись через удалённый MCP — в «Про». Просмотр по ссылке, чтение через API, экспорт экрана и заявка в студию не ограничиваются никогда. При упоре любая ручка записи (/create, /save, /save/patch, /screen/push, /board/collab, /project/key, инструменты MCP) отвечает 402 {error:"plan_limit", limit, used, max, plan, upgrade, hint}, где limit — boards | screens | bytes | collaborators | flex | mcpKeys | mcpWrite, а hint — подсказка по-русски. Проверяется только рост: макет, уже превысивший предел, сохраняется в прежнем размере. После окончания оплаты макеты сверх пределов «Бесплатно» открываются в /load с access:"view" и полем readOnly — ничего не удаляется, апгрейд открывает правку сразу. В MCP предел приходит как JSON-RPC error {code:402, data:{limit, upgrade, hint}}. Песочный ключ mk_sb_ работает на любом тарифе в своих квотах.
Автономные агенты
| Метод | Путь | Назначение |
|---|---|---|
| POST | /agent/register | Песочный ключ mk_sb_… на 14 дней (см. Автономные агенты) |
| GET | /agent/boards | Доски ключа со ссылками. Заголовок x-maketa-key |
Полная схема запросов, ответов и ошибок ({error, hint}, 410 для удалённого) — /openapi.json.
Экраны из кода и ветки
| Метод | Путь | Назначение |
|---|---|---|
| POST | /screen/push | Upsert code-экрана по key. Заголовок x-maketa-key |
| POST | /screen/branch | {id, edit, screenId} — создать ветку |
curl -X POST https://maketa.pro/maketa/api/screen/push \
-H 'content-type: application/json' \
-H 'x-maketa-key: mk_pk_...' \
-d '{"id":"BOARD_ID","key":"/search",
"screen":{"name":"Поиск","w":393,"h":852,"bg":"#F4F4EF","slots":{"body":"n_root"},"nodes":{...}},
"meta":{"route":"/search","commit":"abc","method":"widget-tree"}}'
# → {"ok":true,"screenId":"s_...","version":1}
Проектные ключи
| Метод | Путь | Назначение |
|---|---|---|
| POST | /project/key | {id, edit, label} → ключ (один раз) |
| GET | /project/keys?id=&edit= | Список ключей (без секретов) |
| POST | /project/key/revoke | {id, edit, keyId} — отозвать (keyId — поле id из списка ключей) |
Лимиты: макет ≤ 8 МБ, ≤ 200 экранов, ≤ 1000 узлов на экран, ≤ 5000 узлов на макет; размеры экрана 40–4000 px. Есть rate-limit.
window.Maketa (в редакторе)
В открытом редакторе доступен объект window.Maketa — им пользуются встроенные ИИ-сценарии и автотесты. Основное:
Maketa.schema // "maketa.board.v2"
Maketa.getDocument() // вся сцена целиком
Maketa.setDocument(doc) // заменить сцену
Maketa.listScreens() // [{id,name,kind,locked,nodes,objects,parentId,branches,...}]
Maketa.setActiveScreen(id)
Maketa.createScreen(name, dev) // → screenId
Maketa.branchScreen(id) // ветка экрана
Maketa.importAiTree(screen, asScreen, name) // дерево {slots,nodes} → в макет
Maketa.exportPNG()
Методы listElements, addElement, updateElement, moveElement, removeElement остались от плоской модели и работают только на экранах старого формата: у экрана-дерева объектов нет. Дерево правится через MCP (maketa_add_node и родня) или целиком через setDocument.
Модель maketa.board.v2
Экран — это дерево, а не список фигур. В прошлой модели каждый объект знал свои x и y, и человек расставлял их на глаз. Теперь у узла нет координат вовсе: он лежит внутри родителя, а родитель решает, где ребёнок окажется и сколько места ему дать. Правило дословно взято у Flutter, Compose и SwiftUI:
Ограничения идут вниз. Размеры идут вверх. Родитель задаёт положение.
Свободное размещение никуда не делось — это раскладка over, в которой ребёнок прижимается к одной из девяти точек родителя со смещением dx/dy.
Экран: слоты и карта узлов
У экрана четыре места. top — шапка, она не прокручивается. bottom — нижнее меню, оно тоже не прокручивается. body — всё остальное, оно занимает остаток высоты и прокручивается. over — слой поверх экрана: плавающая кнопка, подсказка, шторка.
Сами узлы лежат в плоской карте nodes, а дерево внутри неё задают массивы children. Карта, а не вложенные объекты, потому что узел адресуется по id — из действий, из панели структуры, из MCP и из истории отмен; искать его обходом дерева на каждый чих дорого.
{
"v": 2,
"name": "Доставка еды",
"screens": [{
"id": "s_menu", "name": "Меню",
"device": "iphone-15", "w": 393, "h": 852, "bg": "#FFFFFF",
"safe": true, // учитывать вырез и полосу-домой
"kind": "design", // design | code
"slots": {
"top": "n_hdr", // шапка — не прокручивается
"body": "n_body", // содержимое — прокручивается
"bottom": "n_nav", // нижнее меню — не прокручивается
"over": ["n_fab"] // слой поверх
},
"nodes": {
"n_hdr": {"id":"n_hdr","type":"stack","name":"Шапка","w":"fill","h":56,
"look":{"bg":"#FFFFFF","border":"#ECEDF2","borderW":1},
"layout":{"dir":"right","pad":[0,16,0,16],"gap":8,"cross":"center"},
"children":["n_ttl"]},
"n_ttl": {"id":"n_ttl","type":"text","content":{"text":"Меню","size":22,"weight":700}},
"n_body": {"id":"n_body","type":"stack","w":"fill","h":"fill",
"layout":{"dir":"down","pad":16,"gap":12,"scroll":true},
"children":["n_card"]},
"n_card": {"id":"n_card","type":"stack","name":"Карточка блюда","w":"fill",
"repeat":5, // пять одинаковых карточек одним узлом
"look":{"bg":"#F7F8FA","radius":14,"shadow":"soft"},
"layout":{"dir":"down","pad":12,"gap":4},
"children":["n_name","n_price"],
"action":{"t":"goto","screen":"s_dish","mode":"modal"}},
"n_name": {"id":"n_name","type":"text","content":{"text":"Пицца «Маргарита»","size":16,"weight":600}},
"n_price":{"id":"n_price","type":"text","content":{"text":"590 ₽","size":14,"color":"#6B7280"}},
"n_nav": {"id":"n_nav","type":"tabs","w":"fill","h":82,
"content":{"variant":"bottom","active":0,
"items":[{"text":"Меню","icon":"🍕"},
{"text":"Корзина","icon":"🛒","link":"s_cart"}]}},
"n_fab": {"id":"n_fab","type":"button","w":56,"h":56,
"at":"bottom-right","dx":-16,"dy":-100,
"look":{"bg":"#3A5BD9","radius":28},
"content":{"text":"+","kind":"filled"}}
}
}]
}
Узел
Общие поля: id (1–40 символов [A-Za-z0-9_-], уникален в пределах экрана), type, name (человеческое имя — его видно в панели структуры), children (id детей по порядку, только у контейнера), w и h, пределы wMin/wMax/hMin/hMax, grow, layout, look, content, action, hidden, repeat. Внутри контейнера с dir: "over" добавляются at и dx/dy.
Полей x, y, margin, flex, zIndex не существует: положение — дело родителя, внешний отступ — это gap родителя, порядок отрисовки — позиция в children.
Размер по каждой оси — три значения: "hug" — по содержимому (умолчание, поле можно не писать), "fill" — занять всё свободное место родителя, число — фиксированный размер в px. Высоту надписи и картинки числом задавать не нужно: у надписи это "hug" плюс content.lines, у картинки — "hug" плюс content.ratio. Число, снятое с одного телефона, на другом ломает экран.
Пределы wMin, wMax, hMin, hMax (число px, 0–10000) — «не уже» и «не шире». Это надстройка над w/h, а не четвёртый режим: сначала считается размер по правилу w/h, потом он зажимается пределами (при противоречии сильнее минимум — как в CSS), и только потом — местом родителя. Предел сужает, но не перебивает родителя: «не уже 200» внутри рамки 150 даст 150. Колонка текста, читаемая и на телефоне, и на 1440 px, — это w: "fill" с wMax: 640.
grow (целое 0–100, умолчание 1) — вес, по которому делится свободное место между детьми "fill" одного ряда или столбца: поиск с grow: 3 рядом с фильтром grow: 1 займёт втрое больше. grow: 0 — «взять свою базовую долю и не тянуться дальше». Без веса «поиск шире фильтров» приходилось писать пикселями, а от пикселей мы и уходим.
Девять типов
Карточка, список, сетка, лента, шапка, разделитель, чип, аватар — не типы, а заготовки палитры, собранные из этих девяти.
| Тип | Что это | content |
|---|---|---|
stack | Контейнер: блок, который раскладывает детей вниз, вбок, поверх или сеткой с переносом. Из него собирается всё остальное | slots: {header} — id надписи-заголовка секции, more: true — ссылка «Все →», ratio — пропорция блока вместо высоты числом |
text | Надпись | text (≤5000), size 8–200, weight 100–900 сотнями, color, align left|center|right, lines — предел числа строк 1–50 (высота при этом "hug", лишнее прячется многоточием), emoji: true — текст-иконка |
image | Картинка или её место | src — только data:image/… (≤2 МБ), fit cover|contain|fill, ratio — отношение ширины к высоте (16/9 ≈ 1.78) вместо высоты числом |
button | Кнопка (тип, а не прямоугольник с текстом) | text, icon — эмодзи, kind filled|tonal|text, size, weight, color, align, ratio |
input | Поле ввода | placeholder, value, search: true — лупа слева, kind text|password|multiline |
choice | Переключатели или галочки одной группой | items — до 20 × {text, on}, multi: true — галочки вместо «один из», active |
tabs | Вкладки и нижнее меню одним узлом | items — до 8 × {text, icon, link — id экрана, shows — id блока, action}, active (0-based), variant bottom|pill|underline|segmented, color, muted, activeText |
item | Пункт списка: значок, заголовок, подпись, элемент справа | slots: {lead, title, sub, trail} — это роли над уже существующими узлами экрана («вот этот здесь за значок»), а не отдельные дети: один и тот же узел законно стоит и в children, и в роли; ratio — пропорция вместо высоты числом |
screen | Сам экран в рамке устройства | — |
Раскладка (layout) — только у контейнера
| Поле | Значения | Что делает |
|---|---|---|
dir | down · right · over · wrap | Вниз (умолчание), вбок, поверх (свободное размещение), сеткой с переносом |
pad | число или [верх, право, низ, лево] | Внутренние отступы блока, 0–400 |
gap | число | Зазор между детьми. Внешних отступов у ребёнка нет — зазор задаёт только родитель. В ленте зазор съедает место, а не ширину карточки: сколько карточек видно, столько и видно |
runGap | число 0–400 | Зазор между рядами в сетке с переносом (dir: "wrap"). Не задан — как gap |
main | start · center · end · between · around · evenly | Как дети распределяются вдоль оси dir |
cross | start · center · end · stretch | Выравнивание поперёк; stretch распрямляет детей с размером hug на всю ширину |
scroll | true | Содержимое прокручивается по оси dir |
cols | 1–12 | Колонок в сетке (dir: "wrap") |
visible | 2 · 2.5 · 3 … | Ширина карточки в горизонтальной ленте задаётся так, а не пикселями: сколько карточек видно в кадре. Половинка означает «край следующей виден» — подсказка, что список листается. Единственная формулировка, переживающая смену устройства |
При dir: "over" ребёнок ставится полем at — одна из девяти точек (top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right) — и смещением dx, dy.
Ещё два значения at держат узел за оба края сразу: left-right — тянуться от левого края до правого, top-bottom — от верхнего до нижнего. Это Figma-констрейнты «Left and Right» / «Top and Bottom» и Positioned(left:, right:) во Flutter; при растяжке смещение становится отступом с обеих сторон. Само смещение dx/dy — либо число px, либо доля родителя строкой: "12%" (от −200 % до 200 %). Пиксельная привязка к левому верхнему углу — худший из вариантов: на узком экране правый край уезжает.
Оформление (look)
bg, border (hex, none или transparent), borderW 0–12, radius — число или [tl,tr,br,bl], shadow — none | soft | card, opacity 0–1. Тень — три состояния, а не число: человеку нужно «плоско / чуть приподнято / карточка», а elevation в dp в вебе означает другое.
Действия, скрытые блоки и повторитель
action:{"t":"goto","screen":"s_cart","mode":"push|modal|dialog|replace"}— открыть экран целиком, шторкой снизу, окном по центру или заменой без возврата. Отдельного поляlinkModeв v2 нет: режим лежит вaction.mode.{"t":"back"}— закрыть экран и вернуться на предыдущий: крестик «✕» и стрелка «‹». Куда именно вернуться, решается в момент нажатия, поэтому один и тот же экран можно открывать из разных мест.{"t":"show","show":["n_paneB"],"hide":["n_paneA"]}— переключить блоки на том же экране: вкладки, шаги формы, раскрытие. Копии экрана для этого не нужны.hidden: true— блок лежит в дереве, но не показывается, пока его не откроет такое действие.repeat: N(до 50) — нарисовать узел N раз подряд внутри родителя. Список из десяти строк — один узел сrepeat: 10: правится образец, меняются все.
Правила целостности
Ошибки дерева не видны глазами и ломают экран уже у зрителя, поэтому они проверяются на входе и возвращаются отказом с указанием узла, а не чинятся молча: «исправленный» сервером экран человек увидит не своим.
| Код | Что нарушено |
|---|---|
bad_node_type | Тип не из девяти (в том числе rect, ellipse, path, note из старой модели) |
unknown_node | Ссылка на узел, которого нет: в children, в слоте, в действии, в роли пункта |
node_two_parents | Узел встречается ребёнком дважды или стоит и в слоте, и внутри блока |
tree_cycle | Узел оказался собственным предком |
too_deep | Вложенность глубже 12 уровней |
unknown_screen | Действие или пункт меню ведёт на несуществующий экран |
bad_nodes | У документа "v": 2, а у экрана вместо карты nodes лежит старый objects |
А вот негодные значения полей отказом не считаются: цвет «не-цвет», visible: 300, repeat: 999 и лишние поля молча заменяются умолчанием или обрезаются по границе — ронять сохранение всего макета из-за одного числа незачем.
Лимиты: макет ≤ 8 МБ, ≤ 200 экранов, ≤ 1000 узлов на экран, ≤ 5000 узлов на макет, глубина ≤ 12, размеры экрана 40–4000 px.
Полная спецификация модели и обоснование решений — в репозитории: docs/research/constructor/SPEC-model-v2-2026-09-04.md. Движок раскладки, который считает из дерева координаты, — /layout-engine.js (без зависимостей, работает и в браузере, и в Node).
Для ИИ-агентов
Если вы ИИ и нашли эту страницу: Maketa — программируемый конструктор экранов. Вы можете:
- Работать полностью автономно: песочный ключ через
POST /agent/register— без человека, почты и оплаты. - Подключиться MCP-сервером (OAuth или headless по ключу) и вызывать инструменты
maketa_*. - Ходить прямо в REST API — схема
/openapi.json; постоянный проектный ключ выдаёт владелец доски в кабинете. - Читать и писать сцену в модели
maketa.board.v2— экран деревом узлов.
Краткая машиночитаемая сводка — /llms.txt, подробная — /llms-full.txt. Правило: code-экраны не редактируются напрямую — создавайте ветку.
Поддержка
Вопросы, доступ к проектам, нативный веб-адаптер: Telegram @demda · сайт студии demda.pro.
Maketa — продукт студии заказной разработки demda.pro. Нужен не только макет, а готовое приложение — приходите к нам.