Архитектура шаблона документов Scientia
Системный контекст
┌────────────────────┐ редактирует ┌──────────────────────────────────┐
│ Автор документа │ ────────────▶ │ main.typ + chapters/ + assets/ │
└─────────┬──────────┘ └────────────────┬─────────────────┘
│ читает │ один import фасада
▼ ▼
┌────────────────────┐ ┌──────────────────────────────────┐
│ docs/ │ │ .template/lib/ │
│ инструкции и │ │ domain → application → │
│ примеры │ │ infrastructure → presentation │
└────────────────────┘ └────────────────┬─────────────────┘
│
┌────────────────────┐ условный import │ PDF
│ .private/ │ ────────────▶ main.typ ─────────┤
│ PNG + settings.typ │ один boolean ▼
┌────────────────────┐
│ document.pdf │
└────────────────────┘
┌────────────────────┐ сопровождает ┌─────────────────────────────────┐
│ Разработчик │ ─────────────▶ │ .template/development/ │
│ шаблона │ │ docs + modules + tests + tools │
└────────────────────┘ └─────────────────────────────────┘
Публичная и developer-документация физически разделены. Автору не требуется открывать .template/, а разработчик не использует docs/ как описание внутренних контрактов.
Архитектурные принципы
- Один файл ежедневной настройки. Компания, режим, metadata и
#include находятся в main.typ.
- Режим — значение, а не entrypoint.
final, draft и clean-copy являются вариантами одной переменной.
- Публичное видно. Инструкции и примеры находятся в
docs/, который VS Code не скрывает.
- Разработка скрыта. Библиотека, tests, ADR и tools находятся в
.template/.
- Пример является исполняемой документацией. Один исходник одновременно обучает, компилируется в CI и устанавливается как тип документа.
- Важные параметры явные. Даже отключённые значения показаны как
none, false или ().
- Приватное не отслеживается. Вся папка
.private/, включая настройки offsets, исключена из Git; tracked placeholders никогда не заменяются реальными файлами.
- Один переключатель. При
false условный import не читает .private; при true обычный preview и обычная build task используют её настройки.
- Фасад скрывает реализацию. Пользователь импортирует только
/.template/lib/index.typ.
- Domain не зависит от layout. Presentation использует domain contracts, но обратной зависимости нет.
Целевая структура
README.md # короткий маршрут автора
main.typ # единственная точка входа и настройки
chapters/ # пользовательский текст
assets/ # изображения, данные и bibliography
docs/ # публичная документация
├── README.md
├── documents.md
├── formatting.md
├── vscode.md
├── git.md
├── private-assets.md
├── writing-style.md
├── troubleshooting.md
└── examples/
├── README.md
├── private/
│ └── settings.typ
├── documents/
│ ├── report/
│ ├── letter/
│ ├── commercial-offer/
│ └── contract/
└── formatting/
.vscode/ # tracked workspace configuration
├── extensions.json
├── settings.json
└── tasks.json
.private/ # ignored settings, private media и backups
├── settings.typ
├── executors/
├── scientia/
├── technology/
└── too/
.template/ # скрытая реализация
├── lib/
├── companies/
└── development/
├── docs/
├── modules/
├── tests/
├── tools/
└── vscode/
В чистом fork каталог .private/ не обязателен. Обычная компиляция использует placeholders из .template/lib/assets/placeholders/.
Компоненты
| Компонент |
Ответственность |
Публичный интерфейс |
| Рабочее пространство автора |
Один входной файл, главы и ресурсы |
main.typ, chapters/, assets/ |
| Публичная документация |
Обучение без знания реализации |
README.md, docs/*.md |
| Публичные примеры |
Исполняемые примеры и источники выбора типа |
docs/examples/**/main.typ |
| VS Code workspace |
Рекомендации, автосохранение и задачи |
.vscode/*.json |
| Публичный фасад |
Единственный пользовательский Typst import |
.template/lib/index.typ |
| Document Domain |
Профиль, context и render options |
document-profile(), render-options() |
| Company Domain |
Реквизиты и firm resources |
company-profile() |
| Parties Domain |
Адресаты, подписанты и стороны |
recipient(), signer(), party() |
| Attachments Domain |
Порядок и идентичность приложений |
attachment(), attachment-set() |
| Render Application |
Resolve, normalize, validate, render |
render-document() |
| Company Adapter |
JSON profiles и resource overrides |
load-company() |
| Presentation |
Foundation, components и четыре renderer |
profiles.report/letter/commercial_offer/contract |
| Employee/Private Adapter |
Справочник сотрудников, private media и offsets |
report-executor(), private-company-media() |
| Test Harness |
Static, compile, semantic и visual gates |
run-tests.py |
Data flow
Обычная сборка
- Автор выбирает
company-id и document-mode в main.typ.
main.typ создаёт profile, company overrides, bibliography и attachments.
#show: document.with(...) передаёт последующие #include как тело документа.
- Facade вызывает application use case.
- Application разрешает компанию, нормализует и валидирует profile metadata.
- Renderer применяет foundation, нумерацию, media policy и компонует страницы.
- При
none для подписи или печати final renderer использует круг или крест.
- Typst создаёт
document.pdf.
Выбор типа документа
- Задача VS Code получает
report, letter, commercial-offer или contract.
- Tool сохраняет текущие
main.typ и chapters/ в .private/starter-backups/<timestamp>/.
- Tool копирует соответствующий публичный пример из
docs/examples/documents/.
- Автор проверяет явно перечисленные параметры нового
main.typ.
Приватная сборка
- Пользователь копирует готовую папку
.private с settings.typ и PNG.
- В
main.typ значение use-private-assets меняется с false на true.
- Условный import загружает
.private/settings.typ.
- Adapter сопоставляет публичный идентификатор сотрудника с фиксированным именем PNG и применяет private offset.
- Отсутствующая или отключённая запись возвращает
none; строка подписи остаётся пустой.
- Обычная build task и Tinymist preview используют один и тот же
main.typ.
Ключевые интерфейсы
// Единственный пользовательский вход.
#let company-id = "scientia" // scientia | technology | too
#let document-mode = "final" // final | draft | clean-copy
#let use-private-assets = false // true, если скопирована .private
// Порядок и состав глав видны внизу main.typ.
#include "chapters/00-introduction.typ"
#pagebreak()
#include "chapters/10-main.typ"
// Отсутствующая .private не читается при false.
#let private-settings = if use-private-assets {
import "/.private/settings.typ": settings
settings
} else {
empty-private-settings
}
#show: document.with(
company: company,
profile: profiles.report(..),
options: (
mode: document-mode,
watermark: if document-mode == "draft" { "ЧЕРНОВИК" } else { none },
media-policy: if document-mode == "final" { "placeholder" } else { "reserve-space" },
diagnostics: true,
),
)
Публичная и developer-документация
| Слой |
Расположение |
Содержит |
Не содержит |
| Публичный |
README.md, docs/ |
первый запуск, Git, типы, formatting, папку .private, troubleshooting |
DDD, ADR, snapshots, migration internals |
| Developer |
.template/development/docs/, modules/ |
архитектуру, решения, границы и тестирование |
обязательный маршрут обычного автора |
Корневой README обязан ссылаться на каждую публичную тему. Developer README доступен одной отдельной ссылкой и не конкурирует с пользовательской навигацией.
Публичные примеры
| Пример |
Обязательное покрытие |
| Report |
титул, stage/volume, executors, includes, рисунки, таблица, formula, references, bibliography, appendix |
| Letter |
recipient, исходящий номер, основной текст, attachment list, signer |
| Commercial offer |
recipient, subject, price, tax, сроки, payment, scope appendix |
| Contract |
parties, representatives, sections, requisites, signing, appendix |
| Formatting |
варианты изображений, grid, простые/сложные таблицы, CSV, формулы, labels, lists |
Публичный пример не должен ссылаться на скрытый developer asset. Допустим только импорт фасада /.template/lib/index.typ.
Политика приватных ресурсов
| Ресурс |
Git |
Поведение |
| Публичные реквизиты и логотипы |
tracked |
Загружаются из .template/companies/ |
| Векторные placeholders |
tracked |
Используются обычной сборкой по умолчанию |
docs/examples/private/settings.typ |
tracked |
Полный безопасный пример с enabled: false |
.template/lib/infrastructure/employees.typ |
tracked |
ФИО, обычные роли и фиксированные имена PNG |
.private/ |
ignored |
Настройки доступности, offsets, реальные изображения и backups |
Typst 0.15 не предоставляет проверки существования файла. Поэтому отсутствие подписи моделируется отсутствующей записью или enabled: false; только включённая запись создаёт private path.
Технологические решения
| Решение |
Выбор |
Обоснование |
| Compiler |
Typst 0.15.1+ |
Проверенный baseline и path type |
| Архитектура |
Модульный монолит |
Один процесс сборки без лишней инфраструктуры |
| Root entry |
Один main.typ |
Минимум выбора и все параметры в одном месте |
| Режимы |
Переменная document-mode |
Варианты видны комментариями, нет дублирования файлов |
| Public docs |
Видимый docs/ |
Автор находит примеры в Explorer |
| Examples |
Executable documentation |
Код и объяснение не расходятся |
| Private activation |
Literal use-private-assets + conditional import |
Один понятный параметр, clean fork не читает отсутствующий каталог |
| Internal boundary |
.template/ |
Реализация и developer docs не мешают автору |
| Testing |
Python + Typst + Poppler |
Контракты, PDF semantic и визуальный layout |
Режимы отказа
| Сбой |
Влияние |
Митигация |
Автор меняет режим не в main.typ |
Ожидаемый вариант PDF не получается |
README и comments показывают единственную переменную |
| Пример использует скрытый developer asset |
После очистки development сборка падает |
Static path audit и compile всех public examples |
| Выбор типа уничтожает текст |
Потеря работы |
Timestamp backup до удаления chapters/ |
| Подпись ещё не получена |
Private compile падает при прямом path |
Запись отсутствует или enabled: false, resolver возвращает none |
| Включённая запись не имеет PNG |
Ошибка Typst с точным path |
Включать запись только после копирования PNG; troubleshooting |
| Реальный файл попадает в Git |
Утечка подписи |
.gitignore, ignored .private/, no overwrite tracked placeholders |
| Сложная таблица переполняет страницу |
Нарушение layout |
Formatting example, stress fixture, repeated header tests |
| Публичная ссылка устарела |
Автор теряет маршрут |
Automated Markdown link audit |
| Изменение Typst меняет layout |
Тихая регрессия |
Version gate и snapshot review |
Вне области видимости v1
- Поддержка старых root-файлов
document.typ, draft.typ и clean-copy.typ.
- Автоматическое определение существования private files внутри Typst.
- Хранение реальных подписей и печатей в Git или Git LFS.
- Публикация внутренних VSIX в этом репозитории.
- Юридическая экспертиза договора.
- Научная верификация пользовательского содержания.
- Генерация DOCX.
- Публичная публикация в Typst Universe и open-source лицензирование.