diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 07e6c700..d62215ef 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.79 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.80 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -2576,6 +2576,10 @@ function Emit-Element {
"autoCmdBar"=1
# дополнения командной панели таблицы (тип-ключи + свойства)
"searchString"=1;"viewStatus"=1;"searchControl"=1;"source"=1;"horizontalLocation"=1;"additions"=1
+ # generic-скаляры (pass-through) + точечные
+ "verticalAlign"=1;"throughAlign"=1;"enableContentChange"=1;"pictureSize"=1;"titleHeight"=1
+ "childItemsWidth"=1;"showLeftMargin"=1;"cellHyperlink"=1;"viewMode"=1;"verticalScrollBar"=1
+ "rowInputMode"=1;"mask"=1;"createButton"=1
}
# Оформление (цвета/шрифты/граница) — авто-регистрация из самих структур, чтобы allowlist
# не дрейфовал при добавлении новых ключей/синонимов. Канонические + forgiving-синонимы.
@@ -2748,6 +2752,39 @@ $script:appOrderField = @('titleTextColor','titleBackColor','titleFont','fo
$script:appOrderDecoration = @('textColor','font','backColor','borderColor','border')
$script:appOrderButton = @('textColor','backColor','borderColor','font')
+# Простые скаляры элемента (pass-through: captured/emitted «как есть»). Только НЕ-перекрывающиеся
+# теги (не обрабатываемые специфично где-то ещё). kind: bool → true/false; value → строка verbatim.
+$script:genericScalars = @(
+ @{ Tag='VerticalAlign'; Key='verticalAlign'; Kind='value' }
+ @{ Tag='ThroughAlign'; Key='throughAlign'; Kind='value' }
+ @{ Tag='EnableContentChange'; Key='enableContentChange'; Kind='bool' }
+ @{ Tag='PictureSize'; Key='pictureSize'; Kind='value' }
+ @{ Tag='TitleHeight'; Key='titleHeight'; Kind='value' }
+ @{ Tag='ChildItemsWidth'; Key='childItemsWidth'; Kind='value' }
+ @{ Tag='ShowLeftMargin'; Key='showLeftMargin'; Kind='bool' }
+ @{ Tag='CellHyperlink'; Key='cellHyperlink'; Kind='bool' }
+ @{ Tag='ViewMode'; Key='viewMode'; Kind='value' }
+ @{ Tag='VerticalScrollBar'; Key='verticalScrollBar'; Kind='value' }
+ @{ Tag='RowInputMode'; Key='rowInputMode'; Kind='value' }
+ @{ Tag='Mask'; Key='mask'; Kind='value' }
+ @{ Tag='CreateButton'; Key='createButton'; Kind='bool' }
+)
+
+function Emit-GenericScalars {
+ param($el, [string]$indent)
+ if ($null -eq $el) { return }
+ foreach ($s in $script:genericScalars) {
+ $p = $el.PSObject.Properties[$s.Key]
+ if (-not $p -or $null -eq $p.Value) { continue }
+ if ($s.Kind -eq 'bool') {
+ X "$indent<$($s.Tag)>$(if ($p.Value){'true'}else{'false'})$($s.Tag)>"
+ } else {
+ $v = "$($p.Value)"; if ($v -eq '') { continue }
+ X "$indent<$($s.Tag)>$(Esc-Xml $v)$($s.Tag)>"
+ }
+ }
+}
+
function Get-AppearanceValue {
param($el, [string]$canonical)
if ($null -eq $el) { return $null }
@@ -2833,6 +2870,7 @@ function Emit-Layout {
if ($el.groupHorizontalAlign) { X "$indent$($el.groupHorizontalAlign)" }
if ($el.groupVerticalAlign) { X "$indent$($el.groupVerticalAlign)" }
if ($el.horizontalAlign) { X "$indent$($el.horizontalAlign)" }
+ Emit-GenericScalars -el $el -indent $indent
}
function Title-FromName {
@@ -3772,6 +3810,7 @@ function Emit-Page {
}
if ($orientation) { X "$inner$orientation" }
}
+ if ($el.showTitle -eq $false) { X "$innerfalse" }
Emit-Layout -el $el -indent $inner
# Companion
@@ -4441,8 +4480,12 @@ function Emit-Attributes {
X "$inner"
}
}
- if ($attr.fillChecking) {
- X "$inner$($attr.fillChecking)"
+ # Проверка заполнения реквизита → (реальный тег; в схеме нет).
+ # bool true → ShowError (единственное значение в корпусе); строка → verbatim. Синоним fillChecking.
+ $fcRaw = if ($null -ne $attr.PSObject.Properties['fillCheck']) { $attr.fillCheck } elseif ($null -ne $attr.PSObject.Properties['fillChecking']) { $attr.fillChecking } else { $null }
+ if ($fcRaw) {
+ $fcv = if ($fcRaw -is [bool]) { 'ShowError' } else { "$fcRaw" }
+ X "$inner$fcv"
}
# UseAlways: поля, всегда читаемые (дин-список/таблица). Две формы DSL сливаются:
@@ -4607,6 +4650,8 @@ function Emit-Commands {
X "$inner$($cmd.action)"
}
+ if ($cmd.modifiesSavedData -eq $true) { X "$innertrue" }
+
Emit-FunctionalOptions -fo $cmd.functionalOptions -indent $inner
if ($cmd.currentRowUse) {
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index 907f2445..a20bf72d 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.79 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.80 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -1813,6 +1813,10 @@ KNOWN_KEYS = {
"autoCmdBar",
# дополнения командной панели таблицы (тип-ключи + свойства)
"searchString", "viewStatus", "searchControl", "source", "horizontalLocation", "additions",
+ # generic-скаляры (pass-through)
+ "verticalAlign", "throughAlign", "enableContentChange", "pictureSize", "titleHeight",
+ "childItemsWidth", "showLeftMargin", "cellHyperlink", "viewMode", "verticalScrollBar",
+ "rowInputMode", "mask", "createButton",
}
# picture/picField — НИЗКИЙ приоритет: 'picture' это и тип (PictureDecoration), и свойство-иконка
@@ -2624,6 +2628,37 @@ def emit_appearance(lines, el, indent, profile='field'):
emit_border_tag(lines, val, indent)
+# Простые скаляры элемента (pass-through, зеркало $script:genericScalars). kind bool/value.
+GENERIC_SCALARS = [
+ ('VerticalAlign', 'verticalAlign', 'value'),
+ ('ThroughAlign', 'throughAlign', 'value'),
+ ('EnableContentChange', 'enableContentChange', 'bool'),
+ ('PictureSize', 'pictureSize', 'value'),
+ ('TitleHeight', 'titleHeight', 'value'),
+ ('ChildItemsWidth', 'childItemsWidth', 'value'),
+ ('ShowLeftMargin', 'showLeftMargin', 'bool'),
+ ('CellHyperlink', 'cellHyperlink', 'bool'),
+ ('ViewMode', 'viewMode', 'value'),
+ ('VerticalScrollBar', 'verticalScrollBar', 'value'),
+ ('RowInputMode', 'rowInputMode', 'value'),
+ ('Mask', 'mask', 'value'),
+ ('CreateButton', 'createButton', 'bool'),
+]
+
+
+def emit_generic_scalars(lines, el, indent):
+ for tag, key, kind in GENERIC_SCALARS:
+ if key not in el or el[key] is None:
+ continue
+ if kind == 'bool':
+ lines.append(f'{indent}<{tag}>{"true" if el[key] else "false"}{tag}>')
+ else:
+ v = str(el[key])
+ if v == '':
+ continue
+ lines.append(f'{indent}<{tag}>{esc_xml(v)}{tag}>')
+
+
def emit_layout(lines, el, indent, skip_height=False, multi_line_default=False):
# Общие layout-свойства — применимы ко всем элементам. Порядок согласован
# с историческим выводом input/label, чтобы не сдвигать существующие снапшоты.
@@ -2655,6 +2690,7 @@ def emit_layout(lines, el, indent, skip_height=False, multi_line_default=False):
lines.append(f"{indent}{el['groupVerticalAlign']}")
if el.get('horizontalAlign'):
lines.append(f"{indent}{el['horizontalAlign']}")
+ emit_generic_scalars(lines, el, indent)
def title_from_name(name):
@@ -3495,6 +3531,8 @@ def emit_page(lines, el, name, eid, indent):
orientation = orientation_map.get(str(el['group']))
if orientation:
lines.append(f'{inner}{orientation}')
+ if el.get('showTitle') is False:
+ lines.append(f'{inner}false')
emit_layout(lines, el, inner)
# Companion
@@ -4169,8 +4207,12 @@ def emit_attributes(lines, attrs, indent):
for f in save_fields:
lines.append(f'{inner}\t{esc_xml(f)}')
lines.append(f'{inner}')
- if attr.get('fillChecking'):
- lines.append(f'{inner}{attr["fillChecking"]}')
+ # Проверка заполнения → (реальный тег; в схеме нет).
+ # bool true → ShowError; строка → verbatim. Синоним fillChecking.
+ fc_raw = attr['fillCheck'] if 'fillCheck' in attr else attr.get('fillChecking')
+ if fc_raw:
+ fcv = 'ShowError' if isinstance(fc_raw, bool) else str(fc_raw)
+ lines.append(f'{inner}{fcv}')
# UseAlways: поля, всегда читаемые. Две формы DSL сливаются:
# attr.useAlways[] (короткие имена) + columns с useAlways:true → ИмяРеквизита.Поле.
@@ -4312,6 +4354,9 @@ def emit_commands(lines, cmds, indent):
if cmd.get('action'):
lines.append(f'{inner}{cmd["action"]}')
+ if cmd.get('modifiesSavedData') is True:
+ lines.append(f'{inner}true')
+
emit_functional_options(lines, cmd.get('functionalOptions'), inner)
if cmd.get('currentRowUse'):
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index b8aa34b5..0a1ec0e2 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.55 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.56 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1120,6 +1120,34 @@ $ELEMENT_KEY = @{
'SearchStringAddition'='searchString'; 'ViewStatusAddition'='viewStatus'; 'SearchControlAddition'='searchControl'
}
+# Простые скаляры элемента (pass-through, зеркало $script:genericScalars компилятора). kind bool/value.
+$GENERIC_SCALARS = @(
+ @{ Tag='VerticalAlign'; Key='verticalAlign'; Kind='value' }
+ @{ Tag='ThroughAlign'; Key='throughAlign'; Kind='value' }
+ @{ Tag='EnableContentChange'; Key='enableContentChange'; Kind='bool' }
+ @{ Tag='PictureSize'; Key='pictureSize'; Kind='value' }
+ @{ Tag='TitleHeight'; Key='titleHeight'; Kind='value' }
+ @{ Tag='ChildItemsWidth'; Key='childItemsWidth'; Kind='value' }
+ @{ Tag='ShowLeftMargin'; Key='showLeftMargin'; Kind='bool' }
+ @{ Tag='CellHyperlink'; Key='cellHyperlink'; Kind='bool' }
+ @{ Tag='ViewMode'; Key='viewMode'; Kind='value' }
+ @{ Tag='VerticalScrollBar'; Key='verticalScrollBar'; Kind='value' }
+ @{ Tag='RowInputMode'; Key='rowInputMode'; Kind='value' }
+ @{ Tag='Mask'; Key='mask'; Kind='value' }
+ @{ Tag='CreateButton'; Key='createButton'; Kind='bool' }
+)
+
+# Захват generic-скаляров. Специфичная обработка (если ключ уже задан) — побеждает.
+function Add-GenericScalars {
+ param($obj, $node)
+ foreach ($s in $GENERIC_SCALARS) {
+ if ($obj.Contains($s.Key)) { continue }
+ $v = Get-Child $node $s.Tag
+ if ($null -eq $v) { continue }
+ if ($s.Kind -eq 'bool') { $obj[$s.Key] = ($v -eq 'true') } else { $obj[$s.Key] = $v }
+ }
+}
+
function Decompile-Children {
param($parentNode, [string]$childContainer = 'ChildItems')
$container = $parentNode.SelectSingleNode("lf:$childContainer", $ns)
@@ -1355,6 +1383,7 @@ function Decompile-Element {
$v = Get-Child $node $p; if ($null -ne $v) { $obj[($p.Substring(0,1).ToLower()+$p.Substring(1))] = (To-Bool $v) }
}
$cbr = Get-Child $node 'ChoiceButtonRepresentation'; if ($cbr) { $obj['choiceButtonRepresentation'] = $cbr }
+ if ((Get-Child $node 'TextEdit') -eq 'false') { $obj['textEdit'] = $false }
$cl = Decompile-ChoiceList $node; if ($cl) { $obj['choiceList'] = $cl }
Add-FormatProps $obj $node
# Параметры выбора / Связи параметров выбора / Связь по типу
@@ -1504,6 +1533,7 @@ function Decompile-Element {
$g = Get-Child $node 'Group'
$gmap = @{ 'Horizontal'='horizontal'; 'Vertical'='vertical'; 'AlwaysHorizontal'='alwaysHorizontal'; 'AlwaysVertical'='alwaysVertical' }
if ($g -and $gmap.ContainsKey($g)) { $obj['group'] = $gmap[$g] }
+ if ((Get-Child $node 'ShowTitle') -eq 'false') { $obj['showTitle'] = $false }
$kids = Decompile-Children $node
if ($kids) { $obj['children'] = $kids }
}
@@ -1568,6 +1598,7 @@ function Decompile-Element {
if ($autoTitle) { $obj['title'] = '' }
}
Add-Layout $obj $node
+ Add-GenericScalars $obj $node
# extendedTooltip: контент companion (любой элемент)
$etTitle = $node.SelectSingleNode("lf:ExtendedTooltip/lf:Title", $ns)
if ($etTitle) { $et = Get-MLFormattedValue $etTitle; if ($null -ne $et) { $obj['extendedTooltip'] = $et } }
@@ -1711,7 +1742,7 @@ if ($attrsNode) {
if ($stripped.Count -eq 1) { $ao['save'] = $stripped[0] } else { $ao['save'] = $stripped }
}
}
- $fc = Get-Child $a 'FillChecking'; if ($fc) { $ao['fillChecking'] = $fc }
+ $fc = Get-Child $a 'FillCheck'; if ($fc) { $ao['fillCheck'] = $fc }
$afo = Decompile-FunctionalOptions $a; if ($afo) { $ao['functionalOptions'] = $afo }
$colsNode = $a.SelectSingleNode("lf:Columns", $ns)
if ($colsNode) {
@@ -1839,6 +1870,7 @@ if ($cmdsNode) {
foreach ($c in @($cmdsNode.SelectNodes("lf:Command", $ns))) {
$co = [ordered]@{}; $co['name'] = $c.GetAttribute("name")
$act = Get-Child $c 'Action'; if ($act) { $co['action'] = $act }
+ if ((Get-Child $c 'ModifiesSavedData') -eq 'true') { $co['modifiesSavedData'] = $true }
$tNode = $c.SelectSingleNode("lf:Title", $ns); if ($tNode) { $t = Get-LangText $tNode; if ($null -ne $t) { $co['title'] = $t } }
$ttNode = $c.SelectSingleNode("lf:ToolTip", $ns); if ($ttNode) { $t = Get-LangText $ttNode; if ($null -ne $t) { $co['tooltip'] = $t } }
$us = Decompile-XrFlag $c 'Use'; if ($null -ne $us) { $co['use'] = $us }
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index 48351db8..34425e99 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -253,7 +253,19 @@ companion-панели с собственным контентом. Оба не
| `headerHorizontalAlign` | `` | `Left`/`Right`/`Center`/`Auto` |
| `headerPicture` | `` | Картинка в шапке колонки. Формат — см. «Картинка-ссылка» ниже |
| `footerPicture` | `` | Картинка в подвале колонки. Формат — см. «Картинка-ссылка» ниже |
+| `verticalAlign` | `` | `Top`/`Center`/`Bottom` |
+| `throughAlign` | `` | `Use`/`DontUse` (сквозное выравнивание группы) |
+| `enableContentChange` | `` | bool (группа/страницы) |
+| `pictureSize` | `` | `AutoSize`/`Proportionally`/`ByFontSize`/… (декорация-картинка) |
+| `titleHeight` | `` | число |
+| `childItemsWidth` | `` | `Equal`/`LeftWide`/… (ширины дочерних в группе) |
+| `showLeftMargin` | `` | bool (группа) |
+| `cellHyperlink` | `` | bool |
+| `mask` | `` | строка маски ввода (input) |
+| `createButton` | `` | bool (input) |
+| `viewMode` / `verticalScrollBar` / `rowInputMode` | ``/… | свойства таблицы (pass-through) |
+> Эти простые скаляры — pass-through (captured/emitted «как есть»), применимы там, где платформа их пишет.
> `defaultItem`/`enableStartDrag`/`fileDragMode`/`skipOnInput` + cell-свойства (`showInHeader`/`showInFooter`/`autoCellHeight`/`footerHorizontalAlign`/`headerHorizontalAlign`/`headerPicture`/`footerPicture`) — общие для любого поля-колонки (input, label, picField, check).
#### Картинка-ссылка (`headerPicture`/`footerPicture`/`valuesPicture`)
@@ -743,7 +755,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs
| `valueType` | string | Тип значений у реквизита типа `ValueList` (``). Грамматика — как у `type`, включая составной `A \| B`. **Три состояния**: нет ключа → нет ``; `""` → пустой `` (список без ограничения типа); тип → с типом. Forgiving-синонимы: `typeDescription` (≈1С «ОписаниеТипов» / XML), `описаниеТипов`, `типЗначений`. Пример: `"valueType": "CatalogRef.Контрагенты"` |
| `savedData` | bool | Сохраняемые данные (``) |
| `save` | bool/string/array | Сохранение значения в пользовательских настройках (`…`). `true` → `имя`; строка/массив строк → под-поля с авто-префиксом `имя.` (путь с точкой / UUID `1/0:…` / совпадающее с именем — берётся как есть). Нет ключа или `false` → не эмитится. Пример периода: `["Период","EndDate","StartDate","Variant"]` |
-| `fillChecking` | string | `Show`, `DontShow` |
+| `fillCheck` | bool/string | Проверка заполнения реквизита (``). `true` → `ShowError` (единственное значение в схеме); строка → verbatim. Синоним `fillChecking`. (`` в схеме нет — был багом) |
| `columns` | array | Колонки для ValueTable/ValueTree (`{ name, type, title?, functionalOptions?, useAlways? }`) |
| `additionalColumns` | array | Доп. колонки табличных частей объекта: `[{ table: "Объект.ТабЧасть", columns: [] }]`. У главного реквизита-объекта; `` — та же грамматика, что у `columns`. Эмитятся в `` после прямых колонок |
| `settings` | object | Настройки динамического списка (только `type: "DynamicList"`) |
@@ -861,6 +873,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs
| `use` | bool/object | Доступность команды по ролям (`
ВыполнитьОбработка
+ true
Ctrl+Enter
diff --git a/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml
index 837f212b..106ac5f3 100644
--- a/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml
+++ b/tests/skills/cases/form-compile/snapshots/groups/DataProcessors/СГруппами/Forms/Форма/Ext/Form.xml
@@ -26,6 +26,9 @@
Usual
true
Right
+ Top
+ Use
+ Equal
@@ -38,6 +41,7 @@
true
20
+ 999-999
@@ -74,6 +78,8 @@
Vertical
Collapsible
true
+ false
+ false
diff --git a/tests/skills/cases/form-compile/snapshots/pages/DataProcessors/Мастер/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/pages/DataProcessors/Мастер/Forms/Форма/Ext/Form.xml
index 6328fbc7..fb05c5e0 100644
--- a/tests/skills/cases/form-compile/snapshots/pages/DataProcessors/Мастер/Forms/Форма/Ext/Form.xml
+++ b/tests/skills/cases/form-compile/snapshots/pages/DataProcessors/Мастер/Forms/Форма/Ext/Form.xml
@@ -20,6 +20,7 @@
+ false