diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 20157c16..298b7fb7 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.113 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.114 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -2978,6 +2978,8 @@ $script:genericScalars = @(
@{ Tag='ChoiceListHeight'; Key='choiceListHeight'; Kind='value' }
@{ Tag='ThreeState'; Key='threeState'; Kind='bool' }
@{ Tag='ScrollOnCompress'; Key='scrollOnCompress'; Kind='bool' }
+ # Сочетание клавиш — общее свойство (input/group/radio/page/picField/label/table/check; команда — отд. путь, §7)
+ @{ Tag='Shortcut'; Key='shortcut'; Kind='value' }
)
function Emit-GenericScalars {
@@ -3558,7 +3560,7 @@ function Emit-Input {
X "$inner$loc"
}
- if ($el.multiLine -eq $true) { X "$innertrue" }
+ if ($null -ne $el.multiLine) { X "$inner$(if ($el.multiLine){'true'}else{'false'})" }
if ($null -ne $el.passwordMode) { X "$inner$(if ($el.passwordMode){'true'}else{'false'})" }
# ChoiceButton — захват «как есть» (платформа эмитит явное значение; ref-поля выводят сама,
# декомпилятор фиксирует факт. значение). Нет ключа → не эмитим (не додумываем по событию).
@@ -4514,7 +4516,6 @@ function Emit-PictureField {
Emit-ColumnPics -el $el -indent $inner
if ($el.titleLocation) { X "$inner$(Map-TitleLoc "$($el.titleLocation)")" }
if ($el.hyperlink -eq $true) { X "$innertrue" }
- if ($el.shortcut) { X "$inner$(Esc-Xml "$($el.shortcut)")" }
Emit-Layout -el $el -indent $inner
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index 0497a18d..97880515 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.113 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.114 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -3096,6 +3096,8 @@ GENERIC_SCALARS = [
('ChoiceListHeight', 'choiceListHeight', 'value'),
('ThreeState', 'threeState', 'bool'),
('ScrollOnCompress', 'scrollOnCompress', 'bool'),
+ # Сочетание клавиш — общее свойство (команда — отдельный путь)
+ ('Shortcut', 'shortcut', 'value'),
]
@@ -3640,8 +3642,8 @@ def emit_input(lines, el, name, eid, indent):
loc = loc_map.get(str(el['titleLocation']), str(el['titleLocation']))
lines.append(f'{inner}{loc}')
- if el.get('multiLine') is True:
- lines.append(f'{inner}true')
+ if el.get('multiLine') is not None:
+ lines.append(f'{inner}{"true" if el["multiLine"] else "false"}')
if el.get('passwordMode') is not None:
lines.append(f'{inner}{"true" if el["passwordMode"] else "false"}')
# ChoiceButton — захват «как есть» (платформа эмитит явное значение; ref-поля выводят сама,
@@ -4237,8 +4239,6 @@ def emit_picture_field(lines, el, name, eid, indent):
lines.append(f'{inner}{map_title_loc(el["titleLocation"])}')
if el.get('hyperlink') is True:
lines.append(f'{inner}true')
- if el.get('shortcut'):
- lines.append(f'{inner}{esc_xml(str(el["shortcut"]))}')
emit_layout(lines, el, inner)
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index 2b518262..132bfc5b 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.89 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.90 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1304,6 +1304,8 @@ $GENERIC_SCALARS = @(
@{ Tag='ChoiceListHeight'; Key='choiceListHeight'; Kind='value' }
@{ Tag='ThreeState'; Key='threeState'; Kind='bool' }
@{ Tag='ScrollOnCompress'; Key='scrollOnCompress'; Kind='bool' }
+ # Сочетание клавиш — общее свойство элемента (команда — отдельный путь)
+ @{ Tag='Shortcut'; Key='shortcut'; Kind='value' }
)
# Захват generic-скаляров. Специфичная обработка (если ключ уже задан) — побеждает.
@@ -1561,7 +1563,8 @@ function Decompile-Element {
$obj[$key] = $name
$dp = Get-Child $node 'DataPath'; if ($dp) { $obj['path'] = $dp }
Add-CommonProps $obj $node $name
- if ((Get-Child $node 'MultiLine') -eq 'true') { $obj['multiLine'] = $true }
+ # MultiLine: факт. значение (платформа эмитит и явный false — 425 в корпусе; ≠ «if true»)
+ $mlIn = Get-Child $node 'MultiLine'; if ($null -ne $mlIn) { $obj['multiLine'] = ($mlIn -eq 'true') }
# PasswordMode: факт. значение (платформа эмитит и false — 349/504 в корпусе; ≠ «if true»)
$pmIn = Get-Child $node 'PasswordMode'; if ($null -ne $pmIn) { $obj['passwordMode'] = ($pmIn -eq 'true') }
$mi = Get-Child $node 'AutoMarkIncomplete'; if ($null -ne $mi) { $obj['markIncomplete'] = ($mi -eq 'true') }
@@ -1669,7 +1672,6 @@ function Decompile-Element {
$em = Get-Child $node 'EditMode'; if ($em) { $obj['editMode'] = $em }
$tl = Get-Child $node 'TitleLocation'; if ($tl) { $obj['titleLocation'] = $tl.ToLower() }
if ((Get-Child $node 'Hyperlink') -eq 'true') { $obj['hyperlink'] = $true }
- $sct = Get-Child $node 'Shortcut'; if ($sct) { $obj['shortcut'] = $sct }
$vp = Get-PictureRef $node 'ValuesPicture'; if ($null -ne $vp) { $obj['valuesPicture'] = $vp }
$npt = $node.SelectSingleNode("lf:NonselectedPictureText", $ns); if ($npt) { $t = Get-LangText $npt; if ($null -ne $t) { $obj['nonselectedPictureText'] = $t } }
}
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index d7d1d843..6506a999 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -371,7 +371,7 @@ companion-панели с собственным контентом. Оба не
| Свойство | Тип | Описание |
|----------|-----|----------|
| `path` | string | DataPath |
-| `multiLine` | bool | Многострочный режим |
+| `multiLine` | bool | Многострочный режим (``). Факт. значение: платформа эмитит и явный `false` (425 в корпусе) — захватываем true/false, отсутствие = дефолт (однострочный) |
| `passwordMode` | bool | Режим пароля |
| `titleLocation` | string | `none`, `left`, `right`, `top`, `bottom` |
| `choiceButton` | bool | Показывать кнопку выбора |
@@ -698,7 +698,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs
| `valuesPicture` | string \| object | Картинка значения. Формат картинки-ссылки — см. §4.1 «Картинка-ссылка» |
| `editMode` | string | Режим редактирования колонки (`EnterOnInput` и т.п.) |
| `hyperlink` | bool | Картинка-гиперссылка (`true`) — кликабельная картинка |
-| `shortcut` | string | Сочетание клавиш (``, напр. `Ctrl+S`) |
+| `shortcut` | string | Сочетание клавиш (``, напр. `Ctrl+S`). Общий generic-скаляр любого элемента (input/group/radio/page/picField/label/table/check), не только колонки-картинки |
#### calendar — CalendarField
diff --git a/tests/skills/cases/form-compile/groups.json b/tests/skills/cases/form-compile/groups.json
index f9ef6346..bc17985a 100644
--- a/tests/skills/cases/form-compile/groups.json
+++ b/tests/skills/cases/form-compile/groups.json
@@ -17,7 +17,7 @@
"title": "Группы",
"elements": [
{ "cmdBar": "КоманднаяПанель", "autofill": true },
- { "group": "horizontal", "name": "ГруппаШапка", "behavior": "usual", "showTitle": true, "title": "Шапка", "horizontalStretch": true, "groupHorizontalAlign": "Right", "throughAlign": "Use", "verticalAlign": "Top", "childItemsWidth": "Equal", "horizontalSpacing": "None", "children": [
+ { "group": "horizontal", "name": "ГруппаШапка", "behavior": "usual", "showTitle": true, "title": "Шапка", "shortcut": "Ctrl+G", "horizontalStretch": true, "groupHorizontalAlign": "Right", "throughAlign": "Use", "verticalAlign": "Top", "childItemsWidth": "Equal", "horizontalSpacing": "None", "children": [
{ "input": "Поле1", "path": "Поле1", "title": "Поле 1", "width": 20, "skipOnInput": true, "mask": "999-999" },
{ "input": "Поле2", "path": "Поле2", "title": "Поле 2" },
{ "labelField": "Метка", "path": "Поле1", "groupVerticalAlign": "Center" }
diff --git a/tests/skills/cases/form-compile/input-fields.json b/tests/skills/cases/form-compile/input-fields.json
index a225912b..4e62789a 100644
--- a/tests/skills/cases/form-compile/input-fields.json
+++ b/tests/skills/cases/form-compile/input-fields.json
@@ -16,7 +16,7 @@
"input": {
"title": "Поля ввода",
"elements": [
- { "input": "ОбычноеПоле", "path": "ОбычноеПоле", "title": "Обычное поле", "tooltip": "Введите значение поля", "tooltipRepresentation": "ShowBottom", "editMode": "EnterOnInput", "horizontalStretch": false, "verticalStretch": false, "format": "ЧДЦ=2", "editFormat": "ЧДЦ=2; ЧН=", "extendedTooltip": { "width": 30, "autoMaxWidth": false, "text": "Расширенная подсказка поля" } },
+ { "input": "ОбычноеПоле", "path": "ОбычноеПоле", "title": "Обычное поле", "tooltip": "Введите значение поля", "tooltipRepresentation": "ShowBottom", "editMode": "EnterOnInput", "multiLine": false, "shortcut": "Ctrl+Shift+O", "horizontalStretch": false, "verticalStretch": false, "format": "ЧДЦ=2", "editFormat": "ЧДЦ=2; ЧН=", "extendedTooltip": { "width": 30, "autoMaxWidth": false, "text": "Расширенная подсказка поля" } },
{ "labelField": "Ссылка", "path": "ОбычноеПоле", "titleLocation": "left", "hyperlink": true, "format": { "ru": "ДЛФ=D", "en": "DLF=D" }, "warningOnEdit": "Поле только для чтения" },
{ "input": "МногострочноеПоле", "path": "МногострочноеПоле", "multiLine": true, "height": 5, "title": "Комментарий", "wrap": false, "showInHeader": false, "showInFooter": false, "autoCellHeight": true, "footerHorizontalAlign": "Right", "openButton": false, "chooseType": false },
{ "input": "ПолеПароля", "path": "ПолеПароля", "passwordMode": true, "title": "Пароль" },
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 2c1dea03..cffa7b27 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
@@ -30,6 +30,7 @@
Use
Equal
None
+ Ctrl+G
diff --git a/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml
index fd815919..ad0535c2 100644
--- a/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml
+++ b/tests/skills/cases/form-compile/snapshots/input-fields/DataProcessors/ПоляВвода/Forms/Форма/Ext/Form.xml
@@ -24,9 +24,11 @@
ShowBottom
+ false
EnterOnInput
false
false
+ Ctrl+Shift+O
ru