From 425eae717079c866f9a0a60be188044ffcbe6ee7 Mon Sep 17 00:00:00 2001 From: Sergey Filkin Date: Sat, 18 Apr 2026 11:29:36 +0300 Subject: [PATCH] phase0: archive Python implementation under archive/ --- ...docker.yml => release-docker.yml.disabled} | 0 .gitignore | 15 ++ .dockerignore => archive/.dockerignore | 0 Dockerfile => archive/Dockerfile | 0 archive/README.md | 1 + {app => archive/app}/__init__.py | 0 {app => archive/app}/api.py | 0 {app => archive/app}/converter.py | 0 {app => archive/app}/streamlit_app.py | 0 {app => archive/app}/version.py | 0 archive/md/.DS_Store | Bin 0 -> 6148 bytes archive/md/01-01-pretask.md | 41 ++++ archive/md/01-02-plan.md | 226 ++++++++++++++++++ archive/md/01-03-answers.md | 65 +++++ archive/md/example.html | 210 ++++++++++++++++ md_to_html.py => archive/md_to_html.py | 0 requirements.txt => archive/requirements.txt | 0 screen.png => archive/screen.png | Bin start.py => archive/start.py | 0 template.html => archive/template.html | 0 20 files changed, 558 insertions(+) rename .github/workflows/{release-docker.yml => release-docker.yml.disabled} (100%) rename .dockerignore => archive/.dockerignore (100%) rename Dockerfile => archive/Dockerfile (100%) create mode 100644 archive/README.md rename {app => archive/app}/__init__.py (100%) rename {app => archive/app}/api.py (100%) rename {app => archive/app}/converter.py (100%) rename {app => archive/app}/streamlit_app.py (100%) rename {app => archive/app}/version.py (100%) create mode 100644 archive/md/.DS_Store create mode 100644 archive/md/01-01-pretask.md create mode 100644 archive/md/01-02-plan.md create mode 100644 archive/md/01-03-answers.md create mode 100644 archive/md/example.html rename md_to_html.py => archive/md_to_html.py (100%) rename requirements.txt => archive/requirements.txt (100%) rename screen.png => archive/screen.png (100%) rename start.py => archive/start.py (100%) rename template.html => archive/template.html (100%) diff --git a/.github/workflows/release-docker.yml b/.github/workflows/release-docker.yml.disabled similarity index 100% rename from .github/workflows/release-docker.yml rename to .github/workflows/release-docker.yml.disabled diff --git a/.gitignore b/.gitignore index e133e60..5baa61b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,18 @@ __pycache__/ # Local markdown workspace md/ docs/ + +# Go +/md-to-html +/bin/ +/dist/ +/tmp/ +*.test +*.out + +# Web build artifacts +/web/static/dist/ +/node_modules/ + +# Air live-reload +.air.log diff --git a/.dockerignore b/archive/.dockerignore similarity index 100% rename from .dockerignore rename to archive/.dockerignore diff --git a/Dockerfile b/archive/Dockerfile similarity index 100% rename from Dockerfile rename to archive/Dockerfile diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 0000000..34263eb --- /dev/null +++ b/archive/README.md @@ -0,0 +1 @@ +Архивная Python-реализация md-to-html v0.1.2. Для истории. diff --git a/app/__init__.py b/archive/app/__init__.py similarity index 100% rename from app/__init__.py rename to archive/app/__init__.py diff --git a/app/api.py b/archive/app/api.py similarity index 100% rename from app/api.py rename to archive/app/api.py diff --git a/app/converter.py b/archive/app/converter.py similarity index 100% rename from app/converter.py rename to archive/app/converter.py diff --git a/app/streamlit_app.py b/archive/app/streamlit_app.py similarity index 100% rename from app/streamlit_app.py rename to archive/app/streamlit_app.py diff --git a/app/version.py b/archive/app/version.py similarity index 100% rename from app/version.py rename to archive/app/version.py diff --git a/archive/md/.DS_Store b/archive/md/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0

Title 2

+

Hello world

+``` +Нужен простой python скрипт, который будет: +1. Принимать на вход путь к markdown файлу `..path/example.md` +2. Через Github API конвертировать его в html +3. Формировать новый html файл по шаблону `md\template.html` и сохранять результат рядом в `..path/example.html` + +=== + +У меня есть готовый пайтон скрипт md_to_html.py, который умеет конвертировать markdown в html с помощью Github API. + +Мне нужно переделать его в простое Streamlit приложение, которое будет иметь следующий интерфейс: +1. Поле для загрузки markdown файла +2. Кнопка для конвертации +3. Поле для отображения результата в виде HTML и возможность скачать результат в виде HTML файла + +Так же хочу этот проект запускать в докере. + +Было бы классно еще иметь один публичный API ендпоинт, который будет принимать markdown текст и возвращать html результат, чтобы можно было использовать этот сервис в других приложениях. + +Задай вопросы, если что-то не понятно или есть неоднозночности или неопределенности. + +Создай репозиторий на GitHub для этого проекта. +Введи версии релизов. +Настрой на гитхаб Actions для автоматической сборки и публикации докер образа при каждом релизе. + + diff --git a/archive/md/01-02-plan.md b/archive/md/01-02-plan.md new file mode 100644 index 0000000..25b8f1c --- /dev/null +++ b/archive/md/01-02-plan.md @@ -0,0 +1,226 @@ +# План: md-to-html — Streamlit UI + публичный API в Docker + +## Context + +Сейчас в проекте есть CLI-скрипт `md_to_html.py`, который через GitHub Markdown API конвертирует `.md` файл в самодостаточный HTML (с CSS-шаблоном `md/template.html`). Нужно превратить его в сервис с двумя интерфейсами: + +1. **Streamlit UI** — загрузить `.md`, нажать кнопку, увидеть превью HTML и скачать результат. +2. **Публичный REST API** — принимает markdown-текст, отдаёт готовый HTML. Для интеграции с другими приложениями. + +Всё это должно упаковываться в Docker-образ. + +Решения, зафиксированные по ходу обсуждения: +- FastAPI и Streamlit живут в **одном контейнере** (два процесса, запуск через стартовый скрипт). +- Возвращается **полная HTML-страница** с применённым `template.html` — и в API, и в превью Streamlit. +- Читаем `GITHUB_TOKEN` из env (опционально, для обхода лимита 60/час). +- API **без аутентификации и rate-limiting** — минимальный стартовый вариант. + +## Архитектура + +``` +md-to-html/ +├── md_to_html.py # оставить как есть (работающий CLI) +├── md/template.html # без изменений, используется как раньше +├── app/ +│ ├── __init__.py +│ ├── converter.py # общая логика (вынесена из md_to_html.py) +│ ├── api.py # FastAPI приложение +│ └── streamlit_app.py # Streamlit UI +├── requirements.txt +├── Dockerfile +├── start.py # Python-супервизор: запускает uvicorn + streamlit +├── .dockerignore +└── README.md # короткая инструкция запуска +``` + +Порты в контейнере: FastAPI — 8000, Streamlit — 8501. Оба пробрасываются наружу. + +## Что делать + +### 1. `app/converter.py` — общий модуль + +Вынести из `md_to_html.py` переиспользуемые функции (без изменения логики): + +- `render_markdown(markdown_text: str) -> str` — вызов GitHub API. Добавить чтение `GITHUB_TOKEN` из env: если задан, слать `Authorization: Bearer `. +- `FirstHeadingParser` + `extract_title(html_text, fallback) -> str`. +- `apply_template(template_text, html_text, title) -> str`. +- Хелпер `convert(markdown_text: str, fallback_title: str = "Document") -> str` — объединяет три шага и читает `md/template.html` один раз (кэшировать через `functools.lru_cache`). + +Путь к шаблону: `Path(__file__).resolve().parent.parent / "md" / "template.html"`. + +`md_to_html.py` переписать так, чтобы он импортировал `convert` из `app.converter` — убрать дублирование. CLI-поведение сохранить (входной путь → записать рядом `.html`). + +### 2. `app/api.py` — FastAPI + +Endpoints: + +- `POST /convert` + - Тело: `{"markdown": "", "title": ""}` — Pydantic-модель с `field_validator` на `markdown`, проверяющим `len(value.encode("utf-8")) <= MAX_MARKDOWN_BYTES` (именно **байты UTF-8**, не `constr(max_length=...)` — тот считает символы). При превышении — `raise HTTPException(status_code=413, detail=...)`, чтобы обойти дефолтный `422` FastAPI-валидатора. + - **Приоритет title (зафиксировано явно):** + 1. Первый `` из отрендеренного HTML. + 2. Если heading отсутствует — переданный `title` из запроса. + 3. Если и его нет — `"Document"`. + - Ответ: `text/html` с полной страницей (`Response(content=..., media_type="text/html; charset=utf-8")`). + - Ошибки: пустой markdown → `400` (через отдельную проверку в роуте, до валидации); превышение размера → `413` (через ручной `HTTPException` в валидаторе, см. выше); `RuntimeError` от GitHub API → `502 Bad Gateway` с текстом исключения. + - Дополнительно: `exception_handler(RequestValidationError)` перехватывает Pydantic-422 и возвращает структурированный `400` — чтобы публичный API не отдавал разные коды на разные виды плохого ввода. +- `GET /health` → `{"status": "ok"}` для проверки. +- `GET /ready` → проверяет, что шаблон загружен и при желании пингует `https://api.github.com` (опционально). + +**Лимит размера запроса (два уровня, defence-in-depth):** + +1. **Hard guard — ASGI middleware до парсинга тела.** Читает `Content-Length` и, если превышает `MAX_REQUEST_BYTES = 1_200_000` (немного больше лимита на поле, чтобы учитывать JSON-обёртку), возвращает `413` без чтения тела. Если `Content-Length` отсутствует (chunked) — аккуратно считать байты из `receive()` и обрывать при превышении. Это настоящий request-size guard, не post-parse. +2. **Soft guard — Pydantic `field_validator` на поле `markdown`,** проверяющий `len(value.encode("utf-8")) <= MAX_MARKDOWN_BYTES` (`1_048_576`, 1 МБ) и поднимающий `HTTPException(413)`. Это вторая линия — на случай, если middleware обойдут (прокси, переписанные заголовки). + +GitHub Markdown API сам ограничивает вход ~400 КБ, поэтому 1 МБ на стороне сервиса — безопасный запас с отсечкой абьюза. Значения вынести в env `MAX_MARKDOWN_BYTES` и `MAX_REQUEST_BYTES`. + +CORS открыть для всех origin (`allow_origins=["*"]`, `allow_methods=["POST","GET"]`, `allow_headers=["content-type"]`). + +### 3. `app/streamlit_app.py` — UI + +Минимальный интерфейс: + +1. `st.title("Markdown → HTML")` +2. `st.file_uploader("Загрузите .md файл", type=["md", "markdown"])` +3. Кнопка `st.button("Конвертировать")` — активна только когда файл загружен. +4. После клика: вызвать `convert()` из `app.converter` напрямую (не через HTTP — это тот же Python-импорт, один процесс). +5. Результат (три способа посмотреть, без deprecated API и без iframe-споров): + - **Inline-превью (approximate):** извлечь содержимое `` из результата (простой regex/BeautifulSoup — фрагмент HTML без `/`, без CSS из template) и отрендерить через `st.markdown(body_html, unsafe_allow_html=True)`. Это приблизительный рендер без стилей template — нужен для быстрой проверки разметки. Явно подписать: *«Inline-превью без стилей. Для точного вида — «Открыть превью в новой вкладке» или скачайте файл.»* + - **Превью в новой вкладке:** `st.link_button("Открыть превью", url=f"data:text/html;charset=utf-8;base64,{b64(html_result)}")` — data-URL с полной страницей. Визуально идентично скачанному файлу, без component-API. **Fallback на большие документы:** если `len(html_result.encode()) > 1_500_000`, кнопка не рендерится, вместо неё показать `st.info("Документ слишком большой для превью в браузере. Скачайте файл.")` — браузеры ограничивают длину data-URL (~2 МБ в Chrome), а base64-кодирование раздувает payload в 1.33×. + - **Скачивание:** `st.download_button("Скачать HTML", data=html_result, file_name=f"{stem}.html", mime="text/html")`. + - **Сырой HTML:** `st.expander("Показать исходный HTML")` с `st.code(html_result, language="html")`. + + Обоснование отказа от `st.components.v1.html` / `st.components.v2.*`: project skill `developing-with-streamlit` помечает v1 как deprecated, а v2 — это API для custom components с Python↔JS (`st.components.v2.component()`), не для простого показа HTML. Для нашего случая native-решения (`st.markdown` + `st.link_button` с data-URL) достаточно. +6. Хранить результат в `st.session_state["html_result"]`, чтобы повторный rerun (клик по expander, download) не терял его и не гонял GitHub API заново. + +Обработка ошибок: `try/except RuntimeError` → `st.error(str(e))`. + +### 4. Зависимости и окружение (локальная разработка) + +Локально использовать **`uv`** + виртуальное окружение, не системный `pip`: + +```bash +uv venv .venv # создать venv в .venv/ +source .venv/bin/activate # (или `uv run ` без активации) +uv pip install -r requirements.txt +``` + +Либо эквивалент через `uv pip sync requirements.txt`, либо (если решим сразу в pep-621-формате) — `pyproject.toml` + `uv sync`. Для данного плана достаточно плоского `requirements.txt` ради совместимости с Docker-образом, где `uv` не обязателен. + +`.gitignore`/`.dockerignore` — исключить `.venv/`. + +**`requirements.txt`:** + +``` +streamlit>=1.42 +fastapi>=0.115 +uvicorn[standard]>=0.32 +pydantic>=2.9 +``` + +Streamlit зафиксирован `>=1.42` (актуальная стабильная ветка на момент планирования). Никаких HTTP-клиентов: `render_markdown` использует стандартную `urllib`. Для извлечения `` в inline-превью достаточно stdlib (`html.parser`) — ту же `HTMLParser`-базу, что уже применяется в `FirstHeadingParser`. Отдельная зависимость на BeautifulSoup не нужна. + +**В Docker-образе** `uv` не используется — остаёмся на `pip install --no-cache-dir -r requirements.txt`, чтобы не тащить лишний бинарь в runtime-образ. `uv` — только для локального dev-цикла. + +### 5. `Dockerfile` + +- Базовый образ: `python:3.12-slim`. +- Установить `tini` через apt (`apt-get update && apt-get install -y --no-install-recommends tini && rm -rf /var/lib/apt/lists/*`). +- `WORKDIR /app`, скопировать `requirements.txt`, `pip install --no-cache-dir -r requirements.txt`. +- Скопировать остальной код. +- `EXPOSE 8000 8501`. +- `ENTRYPOINT ["/usr/bin/tini", "--"]`, `CMD ["python", "start.py"]`. +- `HEALTHCHECK` — см. секцию 6b. + +### 6. Запуск двух процессов — `start.py` (супервизор) + +В одном контейнере два процесса — это риск (см. F-01 из ревью: упавший uvicorn, неубитые zombies, игнор сигналов PID 1). Вместо хрупкого shell-скрипта использовать маленький Python-супервизор, который: + +- стартует `uvicorn` и `streamlit` через `subprocess.Popen`; +- пробрасывает `SIGTERM`/`SIGINT` обоим дочерним через `signal.signal`; +- ждёт через `os.wait()` — **если любой из детей падает, супервизор убивает второго и выходит с его exit code** (контейнер корректно умирает и Docker перезапускает его по restart policy, а не живёт-зомби на одном сервисе); +- после `SIGTERM` делает graceful shutdown с таймаутом (например 10 сек), затем `SIGKILL`. + +Скрипт ~40 строк, без дополнительных зависимостей. Альтернатива `tini` как init (`ENTRYPOINT ["/usr/bin/tini", "--"]`) — для reaping, но решение о fail-fast всё равно за супервизором. + +Ориентир (псевдокод, финализировать при реализации): + +```python +# start.py +import os, signal, subprocess, sys +procs = [ + subprocess.Popen(["uvicorn", "app.api:app", "--host", "0.0.0.0", "--port", "8000"]), + subprocess.Popen(["streamlit", "run", "app/streamlit_app.py", + "--server.port", "8501", "--server.address", "0.0.0.0", + "--server.headless", "true", + "--browser.gatherUsageStats", "false"]), +] +def shutdown(signum, _frame): + for p in procs: p.terminate() +signal.signal(signal.SIGTERM, shutdown) +signal.signal(signal.SIGINT, shutdown) +pid, status = os.wait() +for p in procs: + if p.pid != pid: p.terminate() +sys.exit(os.waitstatus_to_exitcode(status)) +``` + +`Dockerfile` использует `CMD ["python", "start.py"]` плюс `ENTRYPOINT ["tini", "--"]` (ставится через `apt-get install -y --no-install-recommends tini`) для надёжного reaping. + +### 6b. Healthcheck + +`HEALTHCHECK` в Dockerfile проверяет **оба** сервиса: + +```dockerfile +HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ + CMD python -c "import urllib.request as u; \ + u.urlopen('http://127.0.0.1:8000/health', timeout=3); \ + u.urlopen('http://127.0.0.1:8501/_stcore/health', timeout=3)" || exit 1 +``` + +Если упадёт только Streamlit — healthcheck покраснеет, контейнер перезапустится (в паре с restart policy). + +### 7. `.dockerignore` + +Исключить `.git`, `.DS_Store`, `.claude/`, `.agents/`, `.review-sandboxes/`, `md/*.html` (сгенерированное), `__pycache__/`, `*.pyc`, `venv/`, `.venv/`. + +### 8. `README.md` + +Короткий блок: как собрать образ (`docker build -t md-to-html .`), как запустить (`docker run -p 8000:8000 -p 8501:8501 -e GITHUB_TOKEN=... md-to-html`), примеры `curl` для API. + +## Критические файлы + +- **Создать:** `app/converter.py`, `app/api.py`, `app/streamlit_app.py`, `app/__init__.py`, `requirements.txt`, `Dockerfile`, `start.py`, `.dockerignore`, `README.md`. +- **Изменить:** `md_to_html.py` (переписать на использование `app.converter.convert`). +- **Без изменений:** `md/template.html`, `md/01-01-pretask.md`. + +## Проверка (verification) + +1. **Локально без Docker (через uv + venv):** + - `uv venv .venv && source .venv/bin/activate && uv pip install -r requirements.txt`. + - `uvicorn app.api:app --reload` → `curl -X POST http://localhost:8000/convert -H 'Content-Type: application/json' -d '{"markdown":"# Hello"}'` — должна вернуться полная HTML-страница с `Hello`. + - `streamlit run app/streamlit_app.py` → загрузить `md/01-01-pretask.md`, нажать кнопку, убедиться что превью отрисовывается и скачивание работает. + - `GET /health` → `{"status":"ok"}`. + - CLI не сломался: `python md_to_html.py md/01-01-pretask.md` создаёт рядом `.html`, идентичный прежнему. + +2. **Error paths API:** + - `curl -X POST .../convert -d '{"markdown":""}'` → `400`. + - `curl` с телом >1 МБ (поле `markdown` превышает `MAX_MARKDOWN_BYTES`) → `413` (validator). + - `curl --data-binary @big.json` >1.2 МБ общего размера → `413` (middleware). + - `curl -H "Transfer-Encoding: chunked" --data-binary @big.json` без `Content-Length` → `413` (middleware-ветка подсчёта байт из `receive()`). + - Невалидный JSON (отсутствует поле `markdown`) → `400` (через `RequestValidationError` handler). + - Имитация недоступности GitHub (временно подменить `API_URL` или поднять firewall rule) → `502`, контейнер не падает. + +3. **Preview fallback в Streamlit:** загрузить синтетический markdown, дающий HTML >1.5 МБ — убедиться, что `link_button` скрывается и появляется `st.info` про скачивание; `download_button` при этом работает. + +4. **В Docker:** + - `docker build -t md-to-html .` — собирается без ошибок. + - `docker run --rm -p 8000:8000 -p 8501:8501 md-to-html` — оба порта отвечают. + - Открыть `http://localhost:8501`, прогнать сценарий из пункта 1. + - `curl` на `http://localhost:8000/convert` работает снаружи контейнера. + +5. **Supervision (F-01):** + - Внутри работающего контейнера убить uvicorn (`docker exec ... pkill -f uvicorn`) → контейнер **должен завершиться** (не остаться с одним Streamlit). `docker ps` покажет рестарт. + - `docker stop ` — оба процесса уходят в ≤10 сек, exit code корректный. + - `docker inspect ... | grep Health` после 30 сек — `healthy`; после kill любого сервиса — `unhealthy`. + +6. **С токеном:** `docker run -e GITHUB_TOKEN=ghp_... ...` — запросы проходят, в логах нет 403/429 при нагрузке. diff --git a/archive/md/01-03-answers.md b/archive/md/01-03-answers.md new file mode 100644 index 0000000..3826924 --- /dev/null +++ b/archive/md/01-03-answers.md @@ -0,0 +1,65 @@ +1. Функциональный паритет с GitHub API. Сейчас HTML от GitHub даёт: таблицы, task-list, strikethrough, autolinks, footnotes, подсветку кода, emoji + :name:, и главное — обёртки
с якорями (на них завязан CSS в template.html). Что нужно сохранить 1-в-1? + - a) Полный GFM (goldmark поддерживает через extension.GFM) — да/нет? + - b) Подсветку кода chroma встроить в
? Или оставить «просто теги» без классов?                                                               
+  - c) Emoji-shortcodes (yuin/goldmark-emoji)?                                              
+  - d) Обёртки heading’ов с якорями (делается через abhinav/goldmark-anchor или кастомный renderer). Если убрать — придётся править CSS в шаблоне.         
+  - e) Frontmatter (---) — парсить/игнорировать/использовать для title?                                                                                    
+                                                                                                                                                           
+  2. Архитектура Go-приложения. Предлагаю один бинарник с подкомандами:                                                                                    
+  - serve — единый HTTP-сервер: / и /convert (форма на templUI, HTMX-превью), /api/convert, /preview/{id}, /health, /version, /ready, /download/{id}.      
+  - cli  — режим CLI (заменяет md_to_html.py).                                                                                                    
+                                                                                                                                                           
+     Подходит, или надо разделить два бинарника (api и ui)?                                                                                                
+                                                                                                                                                           
+  3. templUI-стек. templUI = templ + Tailwind + Alpine.js + HTMX. Подтвердите:                                                                             
+  - a) Tailwind CSS сборку (tailwindcss CLI) встраиваем в Docker/Makefile?                                                                                 
+  - b) HTMX для live-превью (без полной перезагрузки) — желательно?                                                                                        
+  - c) Тёмная тема / языковой переключатель — нужны или оставляем русский-only как сейчас?                              
+                                                                                                                                                           
+  4. Структура репозитория. Ваш вариант — archive/ для Python, корень для Go. Подтвердите:                                                                 
+  /archive/        # текущий Python-проект целиком                                                                                                         
+  /cmd/md-to-html/ # main.go                                                                                                                               
+  /internal/converter/   # goldmark-рендер                                                                                                                 
+  /internal/server/      # HTTP handlers                                                                                                                   
+  /internal/ui/          # .templ файлы (templUI components)                                                                                               
+  /web/static/           # Tailwind output, favicon                                                                                                        
+  /template/             # самодостаточный HTML-шаблон для итога                                                                                           
+  go.mod / Makefile / Dockerfile                                                                                                                           
+     Go module path — github.com/fserg/md-to-html?                                                                                                         
+                                                                                                                                                           
+  5. Шаблон итогового HTML. template.html сейчас рассчитан на GitHub-разметку (классы .markdown-heading, .heading-element, .anchor). Варианты:             
+  - a) Сохранить визуал идентично → реализовать goldmark-renderer, генерирующий ту же разметку.                                                            
+  - b) Упростить шаблон под «чистый» goldmark-вывод (меньше кода, чуть другой вид h2/h3).                                                                  
+                                                                                                                                                           
+     Какой?                                                                                                                                                
+                                                                                                                        
+  6. Версионирование и релизы. Сейчас v0.1.2, GitHub Actions собирает Docker в GHCR. После переписывания:                                                  
+  - a) Бампнуть до v0.2.0 (или v1.0.0)?                                                                                 
+  - b) CI: заменить на Go-сборку (тесты + cross-compile linux/amd64,arm64, darwin/arm64) + Docker multi-stage?                                             
+  - c) Публиковать бинарники в GitHub Releases?                                                                         
+                                                                                                                                                           
+  7. Нефункциональное.                                                                                                                                     
+  - Лимиты MAX_MARKDOWN_BYTES, MAX_REQUEST_BYTES — переносим (envs)?                                                                                       
+  - Go 1.23+?                                                                                                                                              
+  - Тесты: golden-файлы (MD→HTML diff против эталона) + smoke-тесты HTTP?                                               
+  - air / templ generate --watch для dev-режима? 
+
+# Ответы
+1. Полный GFM и хотелось бы подсветку кода. Шаблон можно править как угодно под новый рендер. Якоря в заголовках хотелось бы сохранить, так как они полезны для навигации по документу.
+
+2. Один бинарник, включая cli режим.
+
+3. templUI можно встраивать в бинарник? Лайв-превью было бы круто, но не критично. Тёмная тема и языковой переключатель не нужны, так как целевая аудитория русскоязычная.
+
+4. archive/ для Python, корень для Go
+
+5. Шаблон поменяй под новый проект
+
+6. После перехода на Go предлагаю бампнуть до v0.2.0
+
+7. Про лимиты не знаю, на твое усмотрение и Го - на твой выбор. Тесты с golden-файлами звучат отлично, а для dev-режима air / templ generate --watch будет удобно.
+
+===
+
+Сохрани подробный план как md/02-01-plan.md
diff --git a/archive/md/example.html b/archive/md/example.html
new file mode 100644
index 0000000..4b5d522
--- /dev/null
+++ b/archive/md/example.html
@@ -0,0 +1,210 @@
+
+
+
+
+    
+    
+    Проект 1С УНФ 3.0
+    
+
+
+
+    
+ +
+

Основная кодовая база

+
+
    +
  • Исходный код основной конфигурации 1С Управление нашей фирмой (УТ) 3.0.12.146: + 1c-src/Configuration +
  • +
+
+

Расширения конфигурации

+
+
    +
  • расширение конфигурации АПРО_Доработки 1c-src/ExtensionsXML/АПРО_Доработки с доработками + функционала по рабочему месту кассиров (РМК) и части документов
  • +
+
+

Окружение разработки

+
+
    +
  • Разработка ведется в операционной системе Ubuntu 24.04 с использованием платформы 1С:Предприятие + 8.3.27.1688 и + конфигуратора 1С:Предприятие.
  • +
  • В системе доступен Python 3.12.
  • +
  • Агентские возможности нужно запускать учитывая особенности консоли на Bash.
  • +
+
+

MCP-серверы и когда их вызывать

+
+
+

1с-metadata (MCP)

+
+

Назначение: быстрый поиск описаний объектов конфигурации (структуры метаданных). + Жёсткий порядок работы: +

+
    +
  1. + search_metadata(query[, object_type]) → топ-K совпадений (как минимум: id, + name, иногда type, score). +
  2. +
  3. Выбираешь релевантный результат и вызываешь metadata_details_by_id(id) → подробности по + объекту. +
  4. +
+

Использовать, когда:

+
    +
  • нужно понять, существуют ли документ/справочник/регистр и как они называются;
  • +
  • требуется структура объекта, реквизиты, измерения, ресурсы, табличные части и т.п.;
  • +
  • нужно уточнить корректные имена метаданных перед написанием запроса/кода.
  • +
+
+ + + \ No newline at end of file diff --git a/md_to_html.py b/archive/md_to_html.py similarity index 100% rename from md_to_html.py rename to archive/md_to_html.py diff --git a/requirements.txt b/archive/requirements.txt similarity index 100% rename from requirements.txt rename to archive/requirements.txt diff --git a/screen.png b/archive/screen.png similarity index 100% rename from screen.png rename to archive/screen.png diff --git a/start.py b/archive/start.py similarity index 100% rename from start.py rename to archive/start.py diff --git a/template.html b/archive/template.html similarity index 100% rename from template.html rename to archive/template.html