Plank help · updated 2026-09-19
Как собрать клиентский документ: маршрут
Маршрут сборки клиентского документа A4 на одной короткой странице: сборщик уже в контейнере, в /opt/plank/doc, скрипт сборки кладут в /workspace/scripts/doc/, фирменный набор — один JSON, compose() переставляет повисший хвост, а проверка читает готовый PDF до выдачи.
Agents: fetch the raw markdown of this page at /ru/help/documents.md
Как собрать клиентский документ: маршрут
Документ читают в одиночку с расстояния вытянутой руки и отправляют PDF-ом или печатают на A4: бриф, предложение, отчёт, выжимка договора. Это не веб-страница и не презентация.
python3 -c "import plank_doc" # уже в контейнере? тогда работаем
# напишите /workspace/scripts/doc/build_acme.py с `from plank_doc import *`
python3 /workspace/scripts/doc/build_acme.py
python3 /opt/plank/doc/plank_doc_qa.py /workspace/acme.pdf # код возврата должен быть 0
1. Сборщик уже здесь, в /opt/plank/doc
Он приезжает внутри контейнера, потому что без WeasyPrint он не отрисует ничего, а
WeasyPrint едет в том же образе. import plank_doc работает из любого каталога. Скрипт
сборки всё равно кладите в /workspace/scripts/doc/ — это папка рабочего пространства: она
переживает перезапуски, и её видят и личный, и общий чат, чего домашняя папка чата не умеет.
Если import plank_doc не прошёл: поставьте его в /workspace/scripts/doc/
Этот контейнер собран раньше, чем появился сборщик. Загрузка — путь обновления и запасной вариант; каталог назначения в обоих случаях /workspace/scripts/doc/. Выполните:
mkdir -p /workspace/scripts/doc/plank_doc
K=https://plank.md/help/kits/client-documents/files/scripts/doc
curl -sSfL -o /workspace/scripts/doc/plank_doc_qa.py $K/plank_doc_qa.py
for f in __init__ scale color brand blocks css document; do
curl -sSfL -o /workspace/scripts/doc/plank_doc/$f.py $K/plank_doc/$f.py
done
Это пакет из семи модулей плюс проверка, а не один файл. Все файлы с размерами и хешами перечислены в plank.md/help/kits/client-documents/manifest.json, если нужно проверить загрузку. Скрипт сборки, лежащий в /workspace/scripts/doc/, ставит свой каталог первым в путь импорта — так что скачанная копия побеждает встроенную без единой настройки.
2. Никогда не пишите эти файлы сами
plank_doc/ и plank_doc_qa.py берутся из контейнера или из этого манифеста и больше
ниоткуда. Не пишите собственный файл с таким именем, чтобы разблокироваться.
Самодельный модуль под нашим именем импортируется без ошибок, не измеряет ничего, и все
последующие сборки в /workspace/scripts/doc/ молча берут его. Если настоящий получить не
удаётся — скажите об этом и соберите PDF иначе, под другим именем файла.
3. Фирменный набор
Один JSON в рабочем пространстве, который называет ваш скрипт сборки. Семь полей и закрытый набор логотипов:
{
"name": "ACME", "ink": "#101014", "ground": "#FFFFFF",
"accent": "#FFD400", "accent_ink": "#000000",
"typeface": "Inter", "confidentiality": "Конфиденциально",
"logos": { "light_on_dark": "assets/acme-white.png",
"dark_on_light": "assets/acme-black.png" }
}
accent_ink — то, что читается на акценте, когда акцент является заливкой. Это
решение бренда, его не угадывают. Сам акцент — заливка и линейка, но никогда не текст на
бумаге: затемнённый accent_text сборщик выводит сам. Нужны обе полярности логотипа
(исключение одно — mark_only, для марки, читаемой на любом фоне), а набор, объявивший
белый логотип как dark_on_light, отклоняется по пикселям самого файла. Отказ называет
поле и отношение контраста. Чините набор, а не порог.
4. Собирайте через compose, а не build
# /workspace/scripts/doc/build_acme_brief.py
from plank_doc import *
from plank_doc.document import compose
brand = Brand.from_kit("/workspace/scripts/doc/brand/acme.json")
doc = Document(brand=brand, kind="Бриф", footer_left="ACME · проект", footer_right="Конфиденциально")
doc.add(Masthead(kind="Бриф"), Title("Заголовок", "Подзаголовок"), ..., Colophon(["…"]))
compose(doc, "/workspace/acme-brief.pdf")
compose собирает, измеряет собственную последнюю страницу и переставляет хвост, если тот
повис. Блоки выбирают по СМЫСЛУ — SpecTable, Callout, Entry, Bullets, KeyFacts —
и никогда по виду: ни один из них не принимает ни цвет, ни кегль. Одиннадцать повторов
«жирный подзаголовок, потом абзац» — это ненаписанная таблица.
5. Прогоните проверку до того, как отдать файл
python3 /opt/plank/doc/plank_doc_qa.py /workspace/acme-brief.pdf
# или /workspace/scripts/doc/plank_doc_qa.py, если вы скачали его туда
Она читает готовый PDF — любой, от любого производителя — и возвращает ненулевой код при
ошибке: логотип на фоне, где его не видно; страница, оборвавшаяся на середине листа;
слово ниже порога WCAG AA (так выглядит пропущенный accent_ink или акцент, пущенный
текстом, к моменту, когда документ дошёл до читателя); краска в обрезном поле. Каждая означает, что читатель увидит
поломку. Почините и прогоните снова.
Если вы всё же отдаёте документ, забракованный проверкой, напишите об этом с числом и перечнем: «проверка показывает 5 ошибок — 4 пустые страницы и 1 невидимый логотип, — которые я не исправил». Отдать забракованный документ, не назвав число, — ровно то, ради чего этот шаг существует.
6. Навык рабочего пространства не заменяет этот маршрут
Навык, шаблон или AGENTS.md могут владеть внешним видом — цветами, шрифтом, составом
разделов, — и вы им следуете. Инструментом они не владеют: если там прямо не назван другой
сборщик и не объяснено почему, документ всё равно собирается через plank_doc из
/workspace/scripts/doc/ или /opt/plank/doc и всё равно проверяется через plank_doc_qa.py.
Навык, который молчит о способе сборки, не выбирает другой маршрут.
Экранные HTML-материалы — другая задача и другой шаблон: HTML-материалы. Презентация — третья: Как собрать презентацию.