diff --git a/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md b/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md new file mode 100644 index 0000000..a4addc6 --- /dev/null +++ b/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md @@ -0,0 +1,95 @@ +# Дизайн: основная надпись чертежа (drawing_fill_title_block) через API7 + +**Дата:** 2026-05-27 +**Статус:** спайк зелёный (round-trip графы подтверждён); спроектировано; на ревью (Codex) + +## Цель + +Веха 2D-ЧЕРТЁЖ, инкремент 2 (оформление). После стандартных видов (`drawing_create_standard_views`) +— заполнить **основную надпись** (штамп, title block) чертежа: обозначение, наименование, материал. +Чертёж с видами + заполненным штампом = презентабельный конструкторский документ. Размеры на видах +— отдельный инкремент 3 (нужен спайк по размерным интерфейсам). + +## Спайк: механизм подтверждён вживую (НЕ разучивать) + +Спайк (`DrawingStampSpikeTests`, зелёный) подтвердил путь API7 — round-trip графы: + +``` +IKompasDocument2D doc2d = (IKompasDocument2D)app.ActiveDocument; // активный чертёж +ILayoutSheet sheet = (ILayoutSheet)doc2d.LayoutSheets.ItemByNumber[1]; // лист 1 (1-based) +IStamp stamp = (IStamp)sheet.Stamp; +stamp.Text[columnId].Str = text; // Text[Int32 Id] — индексированное свойство; Id = номер графы +stamp.Update(); // фиксирует +string readBack = stamp.Text[columnId].Str; // чтение для проверки +``` + +**Проверено:** запись в графу 2 (обозначение) «ДЕТАЛЬ.001» → чтение возвращает «ДЕТАЛЬ.001». +`IText.Str` — read/write; `IStamp.Text[Id]` — индексированное свойство (рефлексия: `get_Text(Int32 Id)`). +`Update()` фиксирует; перестроение документа не нужно. + +**Номера граф ГОСТ (форма 1), `ksStampEnum`:** `ksStPartNumber=1` (наименование изделия), +`ksStDescription=2` (обозначение документа), `ksStMaterial=3` (материал), `ksStScale=6` (масштаб), +`ksStAuthor=110` (разработал) и т.д. + +## Архитектура + +Метод в существующем `DrawingService`. Свой enum `StampField` (паттерн `BasePlane`/`MateType`) +маппится на номера граф — COM-числа не текут в Host/тесты. + +- `src/Kompas.Mcp.Core/Drawings/StampField.cs` — enum `StampField` + static `StampFields` + (`ColumnId(field)` → номер графы). +- `DrawingService.FillTitleBlockAsync(designation?, name?, material?, ct)` — реализация. +- Инструмент `drawing_fill_title_block` в `DrawingTools`. + +## MCP-инструмент + +| Инструмент | Параметры | Поведение | +|---|---|---| +| `drawing_fill_title_block` | `designation?`, `name?`, `material?` (все опц.) | Заполнить графы основной надписи активного чертежа: обозначение (графа 2), наименование (1), материал (3). Передаются только нужные поля (null/пусто — пропускается). Возвращает число заполненных граф. | + +Контракт: активный документ — чертёж; хотя бы одно поле задано (иначе ошибка). Набор полей — +3 ключевые графы ГОСТ (расширяемо). За один вызов заполняются все переданные (меньше round-trip'ов +для LLM-агента, чем у «одна графа за вызов»). + +## Валидация (чистые static, unit-тест) + +- `StampFields.ColumnId(StampField)` — `Designation→2`, `Name→1`, `Material→3` (unit-тест маппинга). +- В сервисе: собрать непустые (после `Trim`) поля; если ни одного — `ArgumentException` + «укажите хотя бы одно поле». (Чистый помощник `StampFields.Collect(...)` → список (columnId,text) + — unit-тестируемо без COM.) + +## Проверка активного документа + +`RequireActiveDrawing()` → `IKompasDocument2D` (тип `ksDocumentDrawing`, иначе раздельная ошибка) — +выделить из существующего `RequireActiveDrawingViews` общий шаг (тот станет надстройкой: вернуть +`.ViewsAndLayersManager.Views`). + +## Реализация + +`FillTitleBlockCore`: собрать поля (`StampFields.Collect`) → если пусто, ошибка → `RequireActiveDrawing` +→ `sheet = LayoutSheets.ItemByNumber[1]` → `stamp = (IStamp)sheet.Stamp` → для каждого поля +`stamp.Text[columnId].Str = text` → `stamp.Update()` (результат проверить) → вернуть число граф. +Транзитные RCW не освобождаем точечно (долг v2-2, как везде). + +## Тестирование + +### Unit (`StampFieldsTests`) +- `StampFields.ColumnId` — каждое поле → ожидаемый номер графы (2/1/3). +- `StampFields.Collect` — пропускает null/пусто/пробелы; при всех пустых даёт пустой список; + собирает заданные с обрезкой пробелов. + +### Integration (`DrawingStampTests`) +Паттерн: создать чертёж → `FillTitleBlockAsync(...)` → перечитать графы напрямую (helper в сервисе +`ReadStampCellAsync(columnId)` для теста ИЛИ проверка через возврат) → `finally CloseAsync` (база +`IntegrationTestBase`). +1. **Заполнение и чтение**: `designation="ДЕТАЛЬ.001", material="Сталь 45"` → 2 графы; чтение графы + 2 == «ДЕТАЛЬ.001», графы 3 == «Сталь 45». +2. **Пустой ввод**: все поля null → `ArgumentException` (ни одной графы не тронуто). +3. **Не-чертёж**: активный документ — деталь → ошибка. + +(Для чтения в тесте — добавить `DrawingService.ReadStampCellAsync(columnId)` — он же подтверждает +round-trip; спайк-файл удаляется.) + +## Дальнейшее (вне спека) +- Прочие графы (масштаб, разработал, даты, литера), размеры/обозначения на видах (инкремент 3), + рамка/формат листа.