Files
2026-10-09 04:24:38 +00:00

252 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектура шаблона документов Scientia
## Системный контекст
```text
┌────────────────────┐ редактирует ┌──────────────────────────────────┐
│ Автор документа │ ────────────▶ │ 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/` как описание внутренних контрактов.
## Архитектурные принципы
1. **Один файл ежедневной настройки**. Компания, режим, metadata и `#include` находятся в `main.typ`.
2. **Режим — значение, а не entrypoint**. `final`, `draft` и `clean-copy` являются вариантами одной переменной.
3. **Публичное видно**. Инструкции и примеры находятся в `docs/`, который VS Code не скрывает.
4. **Разработка скрыта**. Библиотека, tests, ADR и tools находятся в `.template/`.
5. **Пример является исполняемой документацией**. Один исходник одновременно обучает, компилируется в CI и устанавливается как тип документа.
6. **Важные параметры явные**. Даже отключённые значения показаны как `none`, `false` или `()`.
7. **Приватное не отслеживается**. Вся папка `.private/`, включая настройки offsets, исключена из Git; tracked placeholders никогда не заменяются реальными файлами.
8. **Один переключатель**. При `false` условный import не читает `.private`; при `true` обычный preview и обычная build task используют её настройки.
9. **Фасад скрывает реализацию**. Пользователь импортирует только `/.template/lib/index.typ`.
10. **Domain не зависит от layout**. Presentation использует domain contracts, но обратной зависимости нет.
## Целевая структура
```text
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
### Обычная сборка
1. Автор выбирает `company-id` и `document-mode` в `main.typ`.
2. `main.typ` создаёт profile, company overrides, bibliography и attachments.
3. `#show: document.with(...)` передаёт последующие `#include` как тело документа.
4. Facade вызывает application use case.
5. Application разрешает компанию, нормализует и валидирует profile metadata.
6. Renderer применяет foundation, нумерацию, media policy и компонует страницы.
7. При `none` для подписи или печати final renderer использует круг или крест.
8. Typst создаёт `document.pdf`.
### Выбор типа документа
1. Задача VS Code получает `report`, `letter`, `commercial-offer` или `contract`.
2. Tool сохраняет текущие `main.typ` и `chapters/` в `.private/starter-backups/<timestamp>/`.
3. Tool копирует соответствующий публичный пример из `docs/examples/documents/`.
4. Автор проверяет явно перечисленные параметры нового `main.typ`.
### Приватная сборка
1. Пользователь копирует готовую папку `.private` с `settings.typ` и PNG.
2. В `main.typ` значение `use-private-assets` меняется с `false` на `true`.
3. Условный import загружает `.private/settings.typ`.
4. Adapter сопоставляет публичный идентификатор сотрудника с фиксированным именем PNG и применяет private offset.
5. Отсутствующая или отключённая запись возвращает `none`; строка подписи остаётся пустой.
6. Обычная build task и Tinymist preview используют один и тот же `main.typ`.
## Ключевые интерфейсы
```typst
// Единственный пользовательский вход.
#let company-id = "scientia" // scientia | technology | too
#let document-mode = "final" // final | draft | clean-copy
#let use-private-assets = false // true, если скопирована .private
```
```typst
// Порядок и состав глав видны внизу main.typ.
#include "chapters/00-introduction.typ"
#pagebreak()
#include "chapters/10-main.typ"
```
```typst
// Отсутствующая .private не читается при false.
#let private-settings = if use-private-assets {
import "/.private/settings.typ": settings
settings
} else {
empty-private-settings
}
```
```typst
#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 лицензирование.