Превью лейаута
/_system/renderlayout рендерит любой Jet-шаблон без загрузки файлов в ядро системы. Ничего не сохраняется — HTML хранится только в небольшом буфере в памяти. Используйте для отладки правок в шаблонах и быстрого превью до того, как что-либо попадёт в продакшн-vault.
Для кого
- Разработчики — редактируют лейауты локально перед деплоем
- AI-агенты — тестируют Jet-шаблоны, читают ошибки компиляции/рантайма из ответа и показывают промежуточный результат человеку, с которым работают
- Все, кто хочет увидеть, как заметка выглядит с другим лейаутом
Быстрый старт
curl -X POST https://yoursite.com/_system/renderlayout \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"layout": {
"path": "_layouts/article.html",
"src": "<html><body><h1>{{ note.Title() }}</h1>{{ note.HTMLString() }}</body></html>"
},
"note": { "src": "# Привет\n\nЭто тест." }
}'
Ответ:
{
"previewID": "a3f9b2c1",
"previewURL": "/_system/renderlayout?preview_id=a3f9b2c1",
"warnings": {"layout": [], "note": [], "files": []}
}
Откройте previewURL в браузере — увидите результат рендера.
POST /_system/renderlayout
Авторизация: заголовок X-API-Key или сессия администратора.
Тело запроса
{
"layout": {
"path": "обязательно — серверный путь к шаблону",
"src": "опционально — Jet HTML; заменяет серверный файл по path"
},
"note": {
"path": "опционально — путь к заметке на сервере (posts/hello.md)",
"src": "опционально — markdown (альтернатива path)"
},
"overrideFiles": [
{"path": "путь/к/компоненту.html", "src": "замена содержимого для include-файлов"}
]
}
Поля
| Поле | Обязательно | Описание |
|---|---|---|
layout.path |
да | Серверный путь к лейауту. Определяет стартовый шаблон рендера. |
layout.src |
нет | Inline Jet HTML. Заменяет серверный файл по layout.path только для этого рендера. |
note.path |
нет | Путь существующей заметки на сервере. Используется как переменная note в шаблоне. |
note.src |
нет | Inline markdown. Рендерится и используется как заметка. |
overrideFiles |
нет | Массив объектов {path, src}. Переопределяет любой серверный файл лейаута при разрешении include/extends. |
note.path и note.src взаимоисключающие. Если не указано ни то ни другое, шаблон получает пустую заглушку-заметку.
Ответ
{
"previewID": "a3f9b2c1",
"previewURL": "/_system/renderlayout?preview_id=a3f9b2c1",
"warnings": {
"layout": ["compile: line 5: undefined variable 'note.Tags'"],
"note": [],
"files": []
}
}
warnings— объект с тремя ключами:layout,note,files. Каждый содержит массив ошибок компиляции и рантайма Jet для соответствующей области. Все массивы пусты = чистый рендер.- Рендер с ошибками всё равно сохраняется в буфер (частичный HTML доступен по ссылке).
previewIDдействует пока буфер не перезапишется (по умолчанию: 10 последних рендеров).
GET /_system/renderlayout
| URL | Авторизация | Возвращает |
|---|---|---|
?preview_id=xxx |
нет | HTML для данного ID |
| (без параметров) | API key / админ | Последний рендер |
?live |
API key / админ | Последний HTML + скрипт авто-перезагрузки |
?longpolling&since=N |
нет | {action, version} через ≤ 30 с |
Авто-обновление (?live)
Откройте ?live в браузере. Страница содержит polling-скрипт, который вызывает ?longpolling и перезагружает её автоматически при новом рендере. В связке с локальным file watcher получаете live preview по мере редактирования.
Комбинации body и сценарии использования
1 — Тестировать серверный лейаут с существующей заметкой
{
"layout": {"path": "_layouts/article.html"},
"note": {"path": "posts/hello.md"}
}
Проверяем, что уже задеплоенный лейаут корректно рендерит конкретную заметку.
2 — Тестировать локальные правки лейаута (ещё не загружен)
{
"layout": {
"path": "_layouts/article.html",
"src": "...изменённый Jet HTML..."
},
"note": {"path": "posts/hello.md"}
}
Серверный _layouts/article.html заменяется вашим локальным src только для этого рендера. Все include/extends по-прежнему разрешаются с сервера.
3 — Полностью inline рендер (без серверных файлов)
{
"layout": {
"path": "_layouts/test.html",
"src": "<html><body><h1>{{ note.Title() }}</h1>{{ note.HTMLString() }}</body></html>"
},
"note": {"src": "# Заголовок\n\nАбзац."}
}
Полностью самодостаточный запрос. Подходит для AI-агентов, тестирующих шаблон с нуля.
4 — Переопределить компонент (например, _blocks.html)
{
"layout": {"path": "docs/_layouts/index.html"},
"note": {"path": "docs/intro.md"},
"overrideFiles": [
{"path": "docs/_layouts/_blocks.html", "src": "...новое содержимое blocks..."}
]
}
docs/_layouts/index.html запускается на сервере как есть. Когда он вызывает include "docs/_layouts/_blocks.html", серверная версия заменяется вашим файлом из overrideFiles.
5 — Тестировать frontmatter и свойства лейаута
{
"layout": {"path": "_layouts/article.html"},
"note": {
"path": "some/note.md",
"src": "---\nlayout: article\ntags: [go, testing]\n---\n# Тест frontmatter"
}
}
Серверный путь заметки используется для контекста; src заменяет содержимое и frontmatter.
6 — Поймать Jet-ошибки до деплоя
{
"layout": {
"path": "_layouts/article.html",
"src": "{{ note.NonExistentMethod() }}"
},
"note": {"src": "# Тест"}
}
В массиве warnings.layout ответа появится Jet-ошибка. AI-агент или разработчик читают её прямо из JSON — без браузера.
CLI-инструмент
В репозитории есть scripts/renderlayout.py — обёртка, которая автоматически читает API-ключ из .obsidian/plugins/trip2g/data.json:
# запускать из директории vault (где лежит .obsidian/)
python3 ../scripts/renderlayout.py \
--layout-file _layouts/mesh/index.html \
--note-src "# Привет"
# → http://localhost:8081/_system/renderlayout?preview_id=abc123
# получить HTML сразу
python3 ../scripts/renderlayout.py \
--layout-src "{{ note.M().Debug() }}" --layout-path "/_debug.html" \
--note-path /my-note \
--fetch
Аргументы: --layout-path, --layout-file, --layout-src, --note-path, --note-file, --note-src, --fetch.
Предупреждения и ошибки — в stderr; код выхода 1 при сбое.
Инструкция для агентов и советы по отладке шаблонов (включая функцию debug()) — в docs/skills/check_templates.md.
Рендер отдельного BEM-компонента
Чтобы просмотреть один компонент отдельно, импортируйте его файл явно и вызовите HTML- и стилевые блоки.
Соглашения об именовании — в ru/user/bem.
python3 ../scripts/renderlayout.py \
--layout-path "/_layouts/mesh/_preview.html" \
--layout-src '{{ import "_blocks" }}{{ import "bar" }}{{ import "button" }}<style>{{ yield _style_mesh_bar() }}{{ yield _style_mesh_button() }}</style>{{ yield mesh_bar() }}' \
--note-src "hello"
Нюансы:
- Пути импортов — относительные:
"bar", не"/_layouts/mesh/bar". Резолвятся от нормализованного ID:/_layouts/mesh/_preview.html→/mesh/_preview, поэтому"bar"→/mesh/bar. yield_blocks()возвращает пустую строку в preview — фаза wire пропускается. Вызывайте стилевые блоки напрямую:{{ yield _style_myblock() }}.- Зависимости компонентов (например,
buttonвнутриbar) нужно импортировать вручную. GET /_system/renderlayoutбез параметров всегда отдаёт последний рендер — держите в браузере открытым во время работы.
Сценарий работы с AI-агентом
Агент, работающий над лейаутом совместно с человеком, может показывать промежуточный результат мгновенно — без деплоя и без загрузки в vault:
- Агент вносит правки в лейаут или заметку локально.
- Агент делает POST на
/_system/renderlayout. В vault ничего не записывается. - Ответ содержит
previewURLиwarnings. - Если все массивы в
warningsпусты — агент пишет пользователю: «Вот промежуточный результат: [ссылка]» - Если в каком-либо массиве
warningsесть ошибки — агент читает Jet-ошибку, исправляет шаблон и делает POST снова. - Пользователь открывает ссылку, видит рендер и даёт обратную связь — до того как хоть один файл был загружен.
Буфер хранит последние 10 рендеров. Ссылки перестают работать после перезапуска сервера или когда буфер заполняется.
Live превью с file watcher
Установите watchexec, затем запустите:
watchexec -e html -- sh -c '
curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"layout\": {
\"path\": \"_layouts/article.html\",
\"src\": $(cat _layouts/article.html | jq -Rs .)
},
\"note\": {\"path\": \"posts/hello.md\"}
}" \
https://yoursite.com/_system/renderlayout
'
Держите https://yoursite.com/_system/renderlayout?live открытым в браузере. Страница перезагружается мгновенно после получения POST — long-poll соединение уже ждёт на сервере.
Демо-vault
Тестовые файлы находятся в docs/demo/:
| Файл | Назначение |
|---|---|
docs/demo/_layouts/article.html |
Базовый лейаут статьи |
docs/demo/_layouts/with_component.html |
Лейаут с include |
docs/demo/_layouts/components/header.html |
Включаемый компонент header |
docs/demo/hello.md |
Пример заметки |
Используйте как отправную точку при тестировании собственных лейаутов.