From 6377cdfba51a4953805e01a149daa676624f94df Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Fri, 12 Jun 2026 11:09:15 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-decompile,form-compile):=20Table=20he?= =?UTF-8?q?aderHeight/footerHeight/currentRowUse=20+=20=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC.=20conversationsRepresentation=20(pass-through)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Раундтрип терял 4 свойства (категория Table-скаляры + форм-уровень): - Table / (высота шапки/подвала в строках; ~35/~6 форм) - Table (использование текущей строки; ≠ одноимённое свойство команды, у которой свой путь захвата/эмиссии). Значения: DontUse/Use/SelectionPresentation/ SelectionPresentationAndChoice/Choice - форм-уровень (Auto/Show/DontShow; редкое) Все три Table-свойства были явно отложены в Emit-Table (комментарий о «строгом Table-XSD»). Корпусные данные показывают, что 1С эмитит те же теги в РАЗНЫХ позициях у разных форм → загрузчик толерантен к порядку детей Table (как и существующий компилятор с ранним DataPath). Размещены pass-through в Emit-Table (height-теги рядом с UseAlternationRowColor, CurrentRowUse у блока дин-списка); форм-уровень — generic Emit-Properties (авто-PascalCase). decompile (ps1): захват headerHeight/footerHeight/currentRowUse на Table; ConversationsRepresentation в KNOWN_FORM_PROPS. compile (ps1+py): эмиссия в emit_table. Верификация: таргет-раундтрип 4 форм → match (TOTAL diff lines 0); регресс form-compile 43/43 (ps1+py); 1С-cert кейса table (форма с тремя тегами грузится в платформу). spec обновлён. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/form-compile/scripts/form-compile.ps1 | 11 ++++++++--- .claude/skills/form-compile/scripts/form-compile.py | 10 +++++++++- .../skills/form-decompile/scripts/form-decompile.ps1 | 9 +++++++-- docs/form-dsl-spec.md | 4 ++++ .../DataProcessors/Таблица/Forms/Форма/Ext/Form.xml | 3 +++ tests/skills/cases/form-compile/table.json | 2 +- 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 66231d88..b1eb6b6c 100644 --- a/.claude/skills/form-compile/scripts/form-compile.ps1 +++ b/.claude/skills/form-compile/scripts/form-compile.ps1 @@ -1,4 +1,4 @@ -# form-compile v1.125 — Compile 1C managed form from JSON or object metadata +# form-compile v1.126 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -3026,8 +3026,8 @@ $script:genericScalars = @( @{ Tag='Shortcut'; Key='shortcut'; Kind='value' } # Батч простых скаляров (input/radio/group/picDecoration/button): режим выбора незаполненного, # равная ширина колонок, выравнивание детей, масштаб/зум картинки, форма/положение картинки кнопки. - # (Table HeaderHeight/FooterHeight/CurrentRowUse — НЕ здесь: строгий Table-XSD требует точной - # позиции, generic-позиция ломает загрузку; отдельная задача в Emit-Table.) + # (Table HeaderHeight/FooterHeight/CurrentRowUse — НЕ здесь, а в Emit-Table: pass-through, + # 1С толерантна к порядку детей Table — в корпусе те же теги встречаются в разных позициях.) @{ Tag='IncompleteChoiceMode'; Key='incompleteChoiceMode'; Kind='value' } @{ Tag='EqualColumnsWidth'; Key='equalColumnsWidth'; Kind='bool' } @{ Tag='ChildrenAlign'; Key='childrenAlign'; Kind='value' } @@ -4305,6 +4305,9 @@ function Emit-Table { if ($el.multipleChoice -eq $true) { X "$innertrue" } if ($el.searchOnInput) { X "$inner$($el.searchOnInput)" } if ($null -ne $el.markIncomplete) { X "$inner$(if ($el.markIncomplete){'true'}else{'false'})" } + # Высота шапки/подвала в строках (pass-through; 1С толерантна к порядку детей Table) + if ($null -ne $el.headerHeight) { X "$inner$($el.headerHeight)" } + if ($null -ne $el.footerHeight) { X "$inner$($el.footerHeight)" } if ($el.useAlternationRowColor -eq $true) { X "$innertrue" } if ($el.selectionMode) { X "$inner$($el.selectionMode)" } if ($el.rowSelectionMode) { X "$inner$($el.rowSelectionMode)" } @@ -4315,6 +4318,8 @@ function Emit-Table { if ($el.rowPictureDataPath) { X "$inner$($el.rowPictureDataPath)" } # RowsPicture — та же конвенция, что ValuesPicture (дефолт LoadTransparent=false; abs/TransparentPixel) Emit-PictureRef -val $el.rowsPicture -picTag 'RowsPicture' -indent $inner + # Использование текущей строки таблицы (pass-through; в корпусе соседствует с блоком дин-списка) + if ($el.currentRowUse) { X "$inner$($el.currentRowUse)" } # Блок свойств дин-список-таблицы (помечена эвристикой 11b.4) if ($el.PSObject.Properties["_dynList"] -and $el._dynList) { Emit-DynListTableBlock -el $el -indent $inner } if ($el.viewStatusLocation) { X "$inner$($el.viewStatusLocation)" } diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 84322f25..a2ea811b 100644 --- a/.claude/skills/form-compile/scripts/form-compile.py +++ b/.claude/skills/form-compile/scripts/form-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-compile v1.125 — Compile 1C managed form from JSON or object metadata +# form-compile v1.126 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -4029,6 +4029,11 @@ def emit_table(lines, el, name, eid, indent): lines.append(f'{inner}{el["searchOnInput"]}') if el.get('markIncomplete') is not None: lines.append(f'{inner}{"true" if el["markIncomplete"] else "false"}') + # Высота шапки/подвала в строках (pass-through; 1С толерантна к порядку детей Table) + if el.get('headerHeight') is not None: + lines.append(f'{inner}{el["headerHeight"]}') + if el.get('footerHeight') is not None: + lines.append(f'{inner}{el["footerHeight"]}') if el.get('useAlternationRowColor') is True: lines.append(f'{inner}true') if el.get('selectionMode'): @@ -4047,6 +4052,9 @@ def emit_table(lines, el, name, eid, indent): lines.append(f'{inner}{el["rowPictureDataPath"]}') # RowsPicture — та же конвенция, что ValuesPicture (дефолт LoadTransparent=false; abs/TransparentPixel) emit_picture_ref(lines, el.get('rowsPicture'), 'RowsPicture', inner) + # Использование текущей строки таблицы (pass-through; в корпусе соседствует с блоком дин-списка) + if el.get('currentRowUse'): + lines.append(f'{inner}{el["currentRowUse"]}') # Блок свойств дин-список-таблицы (помечена эвристикой) if el.get('_dynList'): emit_dynlist_table_block(lines, el, inner) diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index aa7cbe3f..ba073368 100644 --- a/.claude/skills/form-decompile/scripts/form-decompile.ps1 +++ b/.claude/skills/form-decompile/scripts/form-decompile.ps1 @@ -1,4 +1,4 @@ -# form-decompile v0.99 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v1.00 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -1761,6 +1761,11 @@ function Decompile-Element { if ((Get-Child $node 'Footer') -eq 'true') { $obj['footer'] = $true } # Высота в строках — отдельный ключ heightInTableRows (≠ height = , его ловит Add-Layout) $htr = Get-Child $node 'HeightInTableRows'; if ($htr) { $obj['heightInTableRows'] = [int]$htr } + # Высота шапки/подвала таблицы в строках (pass-through; компилятор эмитит в Emit-Table) + $hh = Get-Child $node 'HeaderHeight'; if ($null -ne $hh) { $obj['headerHeight'] = [int]$hh } + $fh = Get-Child $node 'FooterHeight'; if ($null -ne $fh) { $obj['footerHeight'] = [int]$fh } + # Использование текущей строки (Table-уровень; ≠ command-level CurrentRowUse) — pass-through + $cru = Get-Child $node 'CurrentRowUse'; if ($cru) { $obj['currentRowUse'] = $cru } # CommandBarLocation: для дин-список-таблицы компилятор авто-инжектит "None" → инвертируем # (нет тега → суппресс-маркер ""; "None" → опускаем = авто-дефолт; иначе → захват). $cbl = Get-Child $node 'CommandBarLocation' @@ -2187,7 +2192,7 @@ $titleNode = $root.SelectSingleNode("lf:Title", $ns) if ($titleNode) { $t = Get-LangText $titleNode; if ($null -ne $t) { $dsl['title'] = $t } } # properties (прямые скаляры под
, PascalCase → camelCase) -$KNOWN_FORM_PROPS = @('AutoTitle','ReportResult','DetailsData','ReportFormType','AutoShowState','ReportResultViewMode','ViewModeApplicationOnSetReportResult','WindowOpeningMode','CommandBarLocation','SaveDataInSettings','AutoSaveDataInSettings','AutoTime','UsePostingMode','RepostOnWrite','AutoURL','AutoFillCheck','Customizable','EnterKeyBehavior','VerticalScroll','Width','Height','Group','UseForFoldersAndItems','SaveWindowSettings','ScalingMode','VerticalSpacing','VariantAppearance','ShowCloseButton','HorizontalAlign','ChildrenAlign','ShowTitle') +$KNOWN_FORM_PROPS = @('AutoTitle','ReportResult','DetailsData','ReportFormType','AutoShowState','ReportResultViewMode','ViewModeApplicationOnSetReportResult','WindowOpeningMode','CommandBarLocation','SaveDataInSettings','AutoSaveDataInSettings','AutoTime','UsePostingMode','RepostOnWrite','AutoURL','AutoFillCheck','Customizable','EnterKeyBehavior','VerticalScroll','Width','Height','Group','UseForFoldersAndItems','SaveWindowSettings','ScalingMode','VerticalSpacing','VariantAppearance','ShowCloseButton','HorizontalAlign','ChildrenAlign','ShowTitle','ConversationsRepresentation') $props = [ordered]@{} foreach ($pn in $KNOWN_FORM_PROPS) { $v = Get-Child $root $pn diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 9dfa8c9f..4a6f83bc 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -78,6 +78,7 @@ | `horizontalAlign` | `` | `Left`, `Center`, `Right` — горизонтальное выравнивание формы | | `childrenAlign` | `` | Выравнивание элементов/заголовков (`ItemsLeftTitlesLeft`, `ItemsRightTitlesLeft`, `None`, …) | | `showTitle` | `` | `true` / `false` — показывать заголовок формы | +| `conversationsRepresentation` | `` | `Auto`, `Show`, `DontShow` — отображение панели обсуждений; pass-through (редкое) | Нераспознанные ключи преобразуются с автоматическим PascalCase (первая буква в верхний регистр). @@ -560,6 +561,9 @@ companion-панели с собственным контентом. Оба не | `heightInTableRows` | int | Высота в строках (``) — отдельное свойство от `height`; таблица может нести оба | | `header` | bool | Показывать шапку | | `footer` | bool | Показывать подвал | +| `headerHeight` | int | Высота шапки в строках (``); pass-through (редкое, ~35 форм в корпусе) | +| `footerHeight` | int | Высота подвала в строках (``); pass-through (редкое, ~6 форм) | +| `currentRowUse` | string | Использование текущей строки таблицы (``): `DontUse`, `Use`, `SelectionPresentation`, `SelectionPresentationAndChoice`, `Choice`; pass-through (≠ одноимённое свойство команды) | | `commandBarLocation` | string | `None`, `Top`, `Bottom`, `Auto` | | `searchStringLocation` | string | `None`, `Top`, `Bottom`, `CommandBar`, `Auto` | | `viewStatusLocation` | string | `None`, `Top`, `Bottom`, `Auto` | diff --git a/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml index f4aebfd8..81ac249b 100644 --- a/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml +++ b/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml @@ -18,6 +18,9 @@ true Use true + 2 + 1 + SelectionPresentationAndChoice None None diff --git a/tests/skills/cases/form-compile/table.json b/tests/skills/cases/form-compile/table.json index ba3d6939..663c5f63 100644 --- a/tests/skills/cases/form-compile/table.json +++ b/tests/skills/cases/form-compile/table.json @@ -16,7 +16,7 @@ "input": { "title": "Просмотр данных", "elements": [ - { "table": "Данные", "path": "Данные", "changeRowSet": true, "titleLocation": "top", "height": 80, "heightInTableRows": 5, "autofill": true, "multipleChoice": true, "searchOnInput": "Use", "markIncomplete": true, "settingsNamedItemDetailedRepresentation": false, "heightControlVariant": "UseHeightInTableRows", "maxRowsCount": 5, "autoMaxRowsCount": false, + { "table": "Данные", "path": "Данные", "changeRowSet": true, "titleLocation": "top", "height": 80, "heightInTableRows": 5, "headerHeight": 2, "footerHeight": 1, "currentRowUse": "SelectionPresentationAndChoice", "autofill": true, "multipleChoice": true, "searchOnInput": "Use", "markIncomplete": true, "settingsNamedItemDetailedRepresentation": false, "heightControlVariant": "UseHeightInTableRows", "maxRowsCount": 5, "autoMaxRowsCount": false, "viewStatusLocation": "None", "searchControlLocation": "None", "excludedCommands": ["Add", "Delete", "MoveUp", "MoveDown"], "commandBar": { "autofill": false, "children": [