phase0: archive Python implementation under archive/
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,41 @@
|
||||
Описание Github API конвертера markdown в HTML: https://docs.github.com/en/rest/markdown/markdown?apiVersion=2022-11-28
|
||||
|
||||
Пример вызова API:
|
||||
```bash
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: text/html" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/markdown \
|
||||
-d '{"text":"## Title 2\nHello **world**"}'
|
||||
```
|
||||
Ответ:
|
||||
```html
|
||||
<div class="markdown-heading"><h2 class="heading-element">Title 2</h2><a id="user-content-title-2" class="anchor" aria-label="Permalink: Title 2" href="#title-2"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Hello <strong>world</strong></p>
|
||||
```
|
||||
Нужен простой 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 для автоматической сборки и публикации докер образа при каждом релизе.
|
||||
|
||||
|
||||
@@ -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 <token>`.
|
||||
- `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": "<text>", "title": "<optional>"}` — 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. Первый `<h1..h6>` из отрендеренного 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):** извлечь содержимое `<body>` из результата (простой regex/BeautifulSoup — фрагмент HTML без `<html>/<head>`, без 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 <cmd>` без активации)
|
||||
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`. Для извлечения `<body>` в 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-страница с `<title>Hello</title>`.
|
||||
- `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 <container>` — оба процесса уходят в ≤10 сек, exit code корректный.
|
||||
- `docker inspect ... | grep Health` после 30 сек — `healthy`; после kill любого сервиса — `unhealthy`.
|
||||
|
||||
6. **С токеном:** `docker run -e GITHUB_TOKEN=ghp_... ...` — запросы проходят, в логах нет 403/429 при нагрузке.
|
||||
@@ -0,0 +1,65 @@
|
||||
1. Функциональный паритет с GitHub API. Сейчас HTML от GitHub даёт: таблицы, task-list, strikethrough, autolinks, footnotes, подсветку кода, emoji
|
||||
:name:, и главное — обёртки <div class="markdown-heading"> с якорями <a class="anchor"> (на них завязан CSS в template.html). Что нужно сохранить 1-в-1?
|
||||
- a) Полный GFM (goldmark поддерживает через extension.GFM) — да/нет?
|
||||
- b) Подсветку кода chroma встроить в <pre><code>? Или оставить «просто теги» без классов?
|
||||
- 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 <file.md> — режим 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
|
||||
@@ -0,0 +1,210 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Проект 1С УНФ 3.0</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
||||
line-height: 1.45;
|
||||
color: #1a1a1a;
|
||||
background: #fafafa;
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background: #ffffff;
|
||||
padding: 50px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.markdown-heading {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.markdown-heading:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.heading-element {
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
color: #0a0a0a;
|
||||
}
|
||||
|
||||
h2.heading-element {
|
||||
font-size: 28px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
h3.heading-element {
|
||||
font-size: 20px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.anchor {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown-heading:hover .anchor {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.anchor:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 8px;
|
||||
color: #2a2a2a;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin-bottom: 8px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 2px;
|
||||
color: #2a2a2a;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f5f5f5;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
color: #525252;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
color: #0a0a0a;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2563eb;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 12px 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 28px 20px;
|
||||
}
|
||||
|
||||
h2.heading-element {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3.heading-element {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="markdown-heading">
|
||||
<h2 class="heading-element">Проект 1С Управление нашей фирмой (УНФ) 3.0 с доработками в расширениях
|
||||
конфигурации
|
||||
</h2><a id="user-content-проект-1с-управление-нашей-фирмой-унф-30-с-доработками-в-расширениях-конфигурации"
|
||||
class="anchor"
|
||||
aria-label="Permalink: Проект 1С Управление нашей фирмой (УНФ) 3.0 с доработками в расширениях конфигурации"
|
||||
href="#проект-1с-управление-нашей-фирмой-унф-30-с-доработками-в-расширениях-конфигурации"><span
|
||||
aria-hidden="true" class="octicon octicon-link"></span></a>
|
||||
</div>
|
||||
<div class="markdown-heading">
|
||||
<h3 class="heading-element">Основная кодовая база</h3><a id="user-content-основная-кодовая-база"
|
||||
class="anchor" aria-label="Permalink: Основная кодовая база" href="#основная-кодовая-база"><span
|
||||
aria-hidden="true" class="octicon octicon-link"></span></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Исходный код основной конфигурации 1С Управление нашей фирмой (УТ) 3.0.12.146:
|
||||
<code>1c-src/Configuration</code>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="markdown-heading">
|
||||
<h3 class="heading-element">Расширения конфигурации</h3><a id="user-content-расширения-конфигурации"
|
||||
class="anchor" aria-label="Permalink: Расширения конфигурации" href="#расширения-конфигурации"><span
|
||||
aria-hidden="true" class="octicon octicon-link"></span></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>расширение конфигурации АПРО_Доработки <code>1c-src/ExtensionsXML/АПРО_Доработки</code> с доработками
|
||||
функционала по рабочему месту кассиров (РМК) и части документов</li>
|
||||
</ul>
|
||||
<div class="markdown-heading">
|
||||
<h2 class="heading-element">Окружение разработки</h2><a id="user-content-окружение-разработки"
|
||||
class="anchor" aria-label="Permalink: Окружение разработки" href="#окружение-разработки"><span
|
||||
aria-hidden="true" class="octicon octicon-link"></span></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Разработка ведется в операционной системе Ubuntu 24.04 с использованием платформы 1С:Предприятие
|
||||
8.3.27.1688 и
|
||||
конфигуратора 1С:Предприятие.</li>
|
||||
<li>В системе доступен Python 3.12.</li>
|
||||
<li>Агентские возможности нужно запускать учитывая особенности консоли на Bash.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading">
|
||||
<h2 class="heading-element">MCP-серверы и когда их вызывать</h2><a
|
||||
id="user-content-mcp-серверы-и-когда-их-вызывать" class="anchor"
|
||||
aria-label="Permalink: MCP-серверы и когда их вызывать" href="#mcp-серверы-и-когда-их-вызывать"><span
|
||||
aria-hidden="true" class="octicon octicon-link"></span></a>
|
||||
</div>
|
||||
<div class="markdown-heading">
|
||||
<h3 class="heading-element">1с-metadata (MCP)</h3><a id="user-content-1с-metadata-mcp" class="anchor"
|
||||
aria-label="Permalink: 1с-metadata (MCP)" href="#1с-metadata-mcp"><span aria-hidden="true"
|
||||
class="octicon octicon-link"></span></a>
|
||||
</div>
|
||||
<p><strong>Назначение:</strong> быстрый поиск описаний объектов конфигурации (структуры метаданных).
|
||||
<strong>Жёсткий порядок работы:</strong>
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
<code>search_metadata(query[, object_type])</code> → топ-K совпадений (как минимум: <code>id</code>,
|
||||
<code>name</code>, иногда <code>type</code>, <code>score</code>).
|
||||
</li>
|
||||
<li>Выбираешь релевантный результат и вызываешь <code>metadata_details_by_id(id)</code> → подробности по
|
||||
объекту.
|
||||
</li>
|
||||
</ol>
|
||||
<p><strong>Использовать, когда:</strong></p>
|
||||
<ul>
|
||||
<li>нужно понять, существуют ли документ/справочник/регистр и как они называются;</li>
|
||||
<li>требуется структура объекта, реквизиты, измерения, ресурсы, табличные части и т.п.;</li>
|
||||
<li>нужно уточнить корректные имена метаданных перед написанием запроса/кода.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user