From 82e70d2c30d0ff2e60683fcab4a386b8f5cd738b Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Tue, 14 Apr 2026 11:08:22 +0300 Subject: [PATCH] =?UTF-8?q?feat(skd-compile,form-compile):=20Phase=203=20?= =?UTF-8?q?=E2=80=94=20project-level=20presets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - skd-compile v1.12: scan-up from OutputPath for presets/skills/skd/skd-styles.json (PS1+PY) - form-compile presets/README.md: full preset documentation (sections, keys, enums, example) - docs/form-guide.md: --from-object mode + project-level presets sections - skd-compile SKILL.md: updated styles search path description Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/skills/form-compile/presets/README.md | 126 ++++++++++++++++++ .claude/skills/skd-compile/SKILL.md | 2 +- .../skd-compile/scripts/skd-compile.ps1 | 20 ++- .../skills/skd-compile/scripts/skd-compile.py | 23 +++- docs/form-guide.md | 24 ++++ 5 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 .claude/skills/form-compile/presets/README.md diff --git a/.claude/skills/form-compile/presets/README.md b/.claude/skills/form-compile/presets/README.md new file mode 100644 index 00000000..2d4a5931 --- /dev/null +++ b/.claude/skills/form-compile/presets/README.md @@ -0,0 +1,126 @@ +# Form Presets + +Пресеты управляют раскладкой форм, генерируемых в режиме `--from-object`. + +## Как работает + +Цепочка merge (каждый следующий уровень перезаписывает предыдущий через deep merge): + +1. **Hardcoded defaults** -- встроены в скрипт, ориентированы на ERP +2. **Built-in preset** -- файл из этой папки (`erp-standard.json` по умолчанию) +3. **Project-level preset** -- файл `presets/skills/form/.json`, поиск вверх от OutputPath + +Имя пресета задаётся параметром `--preset` (по умолчанию `erp-standard`). + +## Project-level пресет + +Чтобы переопределить стандартный пресет в своём проекте, создайте файл: + +``` +/presets/skills/form/erp-standard.json +``` + +Скрипт ищет этот файл, поднимаясь от OutputPath к корню. Первый найденный файл применяется поверх built-in через deep merge -- не нужно копировать весь пресет, достаточно указать только переопределяемые ключи. + +## Секции + +Ключи верхнего уровня в JSON -- секции вида `{тип}.{назначение}`: + +| Секция | Тип объекта | Назначение формы | +|--------|-------------|------------------| +| `document.item` | Document | Форма документа | +| `document.list` | Document | Форма списка | +| `document.choice` | Document | Форма выбора | +| `catalog.item` | Catalog | Форма элемента | +| `catalog.folder` | Catalog | Форма группы | +| `catalog.list` | Catalog | Форма списка | +| `catalog.choice` | Catalog | Форма выбора | +| `informationRegister.record` | InformationRegister | Форма записи | +| `informationRegister.list` | InformationRegister | Форма списка | +| `accumulationRegister.list` | AccumulationRegister | Форма списка | +| `chartOfCharacteristicTypes.*` | ChartOfCharacteristicTypes | item/folder/list/choice | +| `exchangePlan.*` | ExchangePlan | item/list/choice | +| `chartOfAccounts.*` | ChartOfAccounts | item/folder/list/choice | + +### basedOn + +Секция может наследовать от другой: + +```json +{ + "document.choice": { + "basedOn": "document.list", + "properties": { "windowOpeningMode": "LockOwnerWindow" } + } +} +``` + +## Ключи секций + +### Форма объекта (Item/Record) + +| Ключ | Описание | Допустимые значения | +|------|----------|---------------------| +| `header.position` | Где размещать шапку | `"insidePage"` -- на первой странице, `"abovePages"` -- над страницами | +| `header.layout` | Колонки шапки | `"1col"`, `"2col"` | +| `header.distribute` | Распределение в 2 колонках | `"even"`, `"left"`, `"right"` | +| `header.dateTitle` | Заголовок даты (Document) | строка, напр. `"от"` | +| `footer.fields` | Поля в подвале | массив имён реквизитов, напр. `["Комментарий"]` | +| `footer.position` | Где размещать подвал | `"insidePage"`, `"belowPages"`, `"none"` | +| `tabularSections.container` | Контейнер табчастей | `"pages"` -- на вкладках, `"inline"` -- в корне, `"single-no-pages"` -- одна ТЧ без страниц | +| `tabularSections.exclude` | Исключить табчасти | массив имён, напр. `["ДополнительныеРеквизиты"]` | +| `tabularSections.lineNumber` | Колонка НомерСтроки | `true` / `false` | +| `additional.position` | Блок доп. реквизитов | `"page"` -- отдельная вкладка, `"below"` -- под табчастями, `"none"` -- не создавать | +| `additional.layout` | Колонки доп. блока | `"1col"`, `"2col"` | +| `additional.bspGroup` | Группа ДополнительныеРеквизиты | `true` / `false` | +| `codeDescription.layout` | Код + Наименование | `"horizontal"`, `"vertical"` | +| `codeDescription.order` | Порядок Код/Наименование | `"descriptionFirst"`, `"codeFirst"` | +| `parent.title` | Заголовок поля Родитель | строка, напр. `"Входит в группу"` | +| `parent.position` | Позиция поля Родитель | `"beforeCodeDescription"`, `"afterCodeDescription"`, `"inHeader"` | +| `owner.readOnly` | Владелец только для чтения | `true` / `false` | +| `owner.position` | Позиция поля Владелец | `"first"` | +| `fieldDefaults.ref.choiceButton` | Кнопка выбора для ссылок | `true` / `false` | +| `fieldDefaults.boolean.element` | Элемент для Boolean | `"check"` (флажок) | +| `commandBar` | Командная панель формы | `"auto"`, `"none"` | +| `properties` | Свойства формы | объект: `autoTitle`, `windowOpeningMode` и др. | + +### Форма списка (List/Choice) + +| Ключ | Описание | Допустимые значения | +|------|----------|---------------------| +| `columns` | Какие колонки показывать | `"all"` -- все реквизиты, или массив имён | +| `columnType` | Тип элемента колонки | `"labelField"`, `"input"` | +| `hiddenRef` | Скрытая колонка Ref | `true` / `false` | +| `tableCommandBar` | Командная панель таблицы | `"auto"`, `"none"` | +| `commandBar` | Командная панель формы | `"auto"`, `"none"` | +| `choiceMode` | Режим выбора (ChoiceForm) | `true` / `false` | +| `properties` | Свойства формы | объект: `windowOpeningMode` и др. | + +## Пример project-level пресета + +```json +{ + "name": "my-project", + "description": "Стиль форм нашего проекта", + + "document.item": { + "header": { + "layout": "1col" + }, + "tabularSections": { + "exclude": ["ДополнительныеРеквизиты", "СведенияОСертификатах"] + }, + "additional": { + "position": "none" + } + }, + + "catalog.item": { + "codeDescription": { + "order": "codeFirst" + } + } +} +``` + +Этот файл переопределяет только указанные ключи -- остальное наследуется из built-in пресета. diff --git a/.claude/skills/skd-compile/SKILL.md b/.claude/skills/skd-compile/SKILL.md index e6f281c4..f7321c2d 100644 --- a/.claude/skills/skd-compile/SKILL.md +++ b/.claude/skills/skd-compile/SKILL.md @@ -287,7 +287,7 @@ Folder в selection: `{"folder": "Поступление", "items": ["ПолеА Встроенные стили: `header` (фон, центр, перенос), `data` (фон группы), `subheader` (без фона, центр), `total` (без фона). Все — Arial 10, рамки Solid 1px, цвета через стили платформы. -Пользовательские стили: файл `skd-styles.json` рядом с JSON или в корне проекта. Все допустимые ключи и формат цветов — в `examples/skd-styles.json`. +Пользовательские стили: файл `skd-styles.json` рядом с JSON-определением, в текущей директории, или в `presets/skills/skd/skd-styles.json` (поиск вверх от OutputPath). Первый найденный файл побеждает. Все допустимые ключи и формат цветов — в `examples/skd-styles.json`. Raw XML (`"template": "<...>"`) остаётся как fallback. Детект: если есть `rows` — DSL, иначе — raw. diff --git a/.claude/skills/skd-compile/scripts/skd-compile.ps1 b/.claude/skills/skd-compile/scripts/skd-compile.ps1 index ef671c3c..52909ae8 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.ps1 +++ b/.claude/skills/skd-compile/scripts/skd-compile.ps1 @@ -1,4 +1,4 @@ -# skd-compile v1.11 — Compile 1C DCS from JSON +# skd-compile v1.12 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -1065,10 +1065,22 @@ $script:areaStylePresets = @{ } } -# Load user presets from skd-styles.json (same dir as definition or cwd) +# Load user presets from skd-styles.json +# Search order (first found wins): 1) definition dir, 2) cwd, 3) scan-up from OutputPath for presets/skills/skd/ $script:userStylesLoaded = $false -foreach ($stylesDir in @($script:queryBaseDir, (Get-Location).Path)) { - $stylesFile = Join-Path $stylesDir "skd-styles.json" +$searchPaths = @( + (Join-Path $script:queryBaseDir "skd-styles.json"), + (Join-Path (Get-Location).Path "skd-styles.json") +) +$outResolved = if ([System.IO.Path]::IsPathRooted($OutputPath)) { $OutputPath } else { Join-Path (Get-Location).Path $OutputPath } +$scanDir = [System.IO.Path]::GetDirectoryName($outResolved) +while ($scanDir) { + $searchPaths += Join-Path (Join-Path (Join-Path (Join-Path $scanDir "presets") "skills") "skd") "skd-styles.json" + $parentDir = Split-Path $scanDir -Parent + if ($parentDir -eq $scanDir) { break } + $scanDir = $parentDir +} +foreach ($stylesFile in $searchPaths) { if (Test-Path $stylesFile) { $userStyles = Get-Content -Raw -Encoding UTF8 $stylesFile | ConvertFrom-Json foreach ($prop in $userStyles.PSObject.Properties) { diff --git a/.claude/skills/skd-compile/scripts/skd-compile.py b/.claude/skills/skd-compile/scripts/skd-compile.py index a0fa8d49..6c56512b 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.py +++ b/.claude/skills/skd-compile/scripts/skd-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# skd-compile v1.11 — Compile 1C DCS from JSON +# skd-compile v1.12 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -904,9 +904,21 @@ AREA_STYLE_PRESETS = { } -def load_user_styles(base_dir): - for d in [base_dir, os.getcwd()]: - p = os.path.join(d, 'skd-styles.json') +def load_user_styles(base_dir, output_path=None): + # Search order (first found wins): 1) definition dir, 2) cwd, 3) scan-up from OutputPath for presets/skills/skd/ + search_paths = [ + os.path.join(base_dir, 'skd-styles.json'), + os.path.join(os.getcwd(), 'skd-styles.json'), + ] + if output_path: + scan_dir = os.path.dirname(output_path) + while scan_dir: + search_paths.append(os.path.join(scan_dir, 'presets', 'skills', 'skd', 'skd-styles.json')) + parent_dir = os.path.dirname(scan_dir) + if parent_dir == scan_dir: + break + scan_dir = parent_dir + for p in search_paths: if os.path.isfile(p): with open(p, 'r', encoding='utf-8-sig') as f: user_styles = json.load(f) @@ -1808,7 +1820,8 @@ def main(): query_base_dir = os.path.dirname(def_file) if args.DefinitionFile else os.getcwd() # Load user style presets - load_user_styles(query_base_dir) + out_path_resolved = args.OutputPath if os.path.isabs(args.OutputPath) else os.path.join(os.getcwd(), args.OutputPath) + load_user_styles(query_base_dir, out_path_resolved) # --- 2. Resolve defaults --- diff --git a/docs/form-guide.md b/docs/form-guide.md index bc7374d3..08cb7ee9 100644 --- a/docs/form-guide.md +++ b/docs/form-guide.md @@ -205,6 +205,30 @@ Claude создаст JSON-определение и вызовет `/form-compi `/form-validate` проверит структурную корректность (ID, companions, ссылки), `/form-info` покажет результат в виде компактной сводки. +## Генерация из метаданных объекта + +`/form-compile` умеет автоматически генерировать форму из метаданных объекта (XML-выгрузки). Скрипт читает реквизиты, табличные части, стандартные реквизиты и строит форму по стандарту ERP. + +``` +> Создай форму документа ЗаказКлиенту +``` + +Claude вызовет `/form-compile` с флагом `-FromObject`. Назначение формы (Purpose) автоматически определяется из имени формы (ФормаДокумента -> Item, ФормаСписка -> List, ФормаВыбора -> Choice). + +Поддерживаемые типы: Document, Catalog, InformationRegister, AccumulationRegister, ChartOfCharacteristicTypes, ExchangePlan, ChartOfAccounts. + +Не поддерживается: DataProcessor (реквизиты объекта пустые -- форма строится на реквизитах формы, используйте JSON DSL). + +### Project-level пресеты + +Раскладка формы управляется пресетом (по умолчанию `erp-standard`). Для настройки под проект создайте файл: + +``` +/presets/skills/form/erp-standard.json +``` + +Файл накладывается поверх встроенного пресета через deep merge -- достаточно указать только переопределяемые ключи. Полная документация ключей: `.claude/skills/form-compile/presets/README.md`. + ## Примеры слеш-команд ```