diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index b00e6fe3..3c5f04d3 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.26 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.27 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -2034,6 +2034,30 @@ function Emit-Title {
}
}
+function Map-TitleLoc {
+ param([string]$v)
+ switch ("$v".ToLower()) {
+ "none" { "None" }
+ "left" { "Left" }
+ "right" { "Right" }
+ "top" { "Top" }
+ "bottom" { "Bottom" }
+ "auto" { "Auto" }
+ default { "$v" }
+ }
+}
+
+# TitleLocation у check/radio: нет ключа → умный дефолт (Right/None), эмитится;
+# "" → подавить (= дефолт платформы, она его сама не пишет); значение → эмитить (маппинг регистра).
+function Emit-TitleLocation {
+ param($el, [string]$indent, [string]$smartDefault)
+ if ($null -ne $el.PSObject.Properties['titleLocation']) {
+ if ($el.titleLocation) { X "$indent$(Map-TitleLoc "$($el.titleLocation)")" }
+ } elseif ($smartDefault) {
+ X "$indent$smartDefault"
+ }
+}
+
function Emit-Group {
param($el, [string]$name, [int]$id, [string]$indent)
@@ -2196,8 +2220,7 @@ function Emit-Check {
Emit-Title -el $el -name $name -indent $inner -auto:(-not $el.path)
Emit-CommonFlags -el $el -indent $inner
- $tl = if ($el.titleLocation) { "$($el.titleLocation)" } else { "Right" }
- X "$inner$tl"
+ Emit-TitleLocation -el $el -indent $inner -smartDefault "Right"
Emit-Layout -el $el -indent $inner
@@ -2337,18 +2360,7 @@ function Emit-Radio {
Emit-Title -el $el -name $name -indent $inner -auto:(-not $el.path)
Emit-CommonFlags -el $el -indent $inner
- # TitleLocation default is None for radio (matches typical configurator behavior)
- $tl = if ($el.titleLocation) {
- switch ("$($el.titleLocation)") {
- "none" { "None" }
- "left" { "Left" }
- "right" { "Right" }
- "top" { "Top" }
- "bottom" { "Bottom" }
- default { "$($el.titleLocation)" }
- }
- } else { "None" }
- X "$inner$tl"
+ Emit-TitleLocation -el $el -indent $inner -smartDefault "None"
# RadioButtonType: Auto | RadioButtons | Tumbler. Accept synonyms.
$rbtRaw = if ($el.radioButtonType) { "$($el.radioButtonType)".Trim() } else { "Auto" }
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index c69233be..64d9df88 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.26 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.27 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -1620,6 +1620,23 @@ def emit_title(lines, el, name, indent, auto=False):
emit_mltext(lines, indent, 'Title', title_from_name(name))
+_TITLE_LOC_MAP = {'none': 'None', 'left': 'Left', 'right': 'Right', 'top': 'Top', 'bottom': 'Bottom', 'auto': 'Auto'}
+
+
+def map_title_loc(v):
+ return _TITLE_LOC_MAP.get(str(v).lower(), str(v))
+
+
+def emit_title_location(lines, el, indent, smart_default):
+ # Нет ключа → умный дефолт (Right/None), эмитится. "" → подавить (дефолт платформы).
+ # Значение → эмитить с маппингом регистра.
+ if 'titleLocation' in el:
+ if el.get('titleLocation'):
+ lines.append(f"{indent}{map_title_loc(el['titleLocation'])}")
+ elif smart_default:
+ lines.append(f"{indent}{smart_default}")
+
+
# --- Type emitter ---
V8_TYPES = {
@@ -2007,8 +2024,7 @@ def emit_check(lines, el, name, eid, indent):
emit_title(lines, el, name, inner, auto=not el.get('path'))
emit_common_flags(lines, el, inner)
- tl = el.get('titleLocation') or 'Right'
- lines.append(f'{inner}{tl}')
+ emit_title_location(lines, el, inner, 'Right')
emit_layout(lines, el, inner)
@@ -2031,13 +2047,7 @@ def emit_radio_button_field(lines, el, name, eid, indent):
emit_title(lines, el, name, inner, auto=not el.get('path'))
emit_common_flags(lines, el, inner)
- tl_raw = el.get('titleLocation')
- if tl_raw:
- loc_map = {'none': 'None', 'left': 'Left', 'right': 'Right', 'top': 'Top', 'bottom': 'Bottom'}
- tl = loc_map.get(str(tl_raw), str(tl_raw))
- else:
- tl = 'None'
- lines.append(f'{inner}{tl}')
+ emit_title_location(lines, el, inner, 'None')
rbt = normalize_radio_button_type(el.get('radioButtonType'))
lines.append(f'{inner}{rbt}')
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index dbd36e77..32690714 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.5 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.6 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -203,6 +203,15 @@ function Add-Layout {
$ha = Get-Child $node 'HorizontalAlign'; if ($ha) { $obj['horizontalAlign'] = $ha }
}
+# TitleLocation у check/radio (зеркало Emit-TitleLocation):
+# тега нет → "" (дефолт платформы); значение = умный дефолт → опускаем; иначе пишем.
+function Add-TitleLocation {
+ param($obj, $node, [string]$smartDefault)
+ $tl = Get-Child $node 'TitleLocation'
+ if ($null -eq $tl) { $obj['titleLocation'] = '' }
+ elseif ($tl -ne $smartDefault) { $obj['titleLocation'] = $tl.ToLower() }
+}
+
# Суффиксы авто-имён обработчиков (инверсия компилятора)
$HANDLER_SUFFIX = @{
'OnChange'='ПриИзменении'; 'StartChoice'='НачалоВыбора'; 'ChoiceProcessing'='ОбработкаВыбора';
@@ -375,12 +384,13 @@ function Decompile-Element {
$obj[$key] = $name
$dp = Get-Child $node 'DataPath'; if ($dp) { $obj['path'] = $dp }
Add-CommonProps $obj $node $name
- $tl = Get-Child $node 'TitleLocation'; if ($tl) { $obj['titleLocation'] = $tl.ToLower() }
+ Add-TitleLocation $obj $node 'Right'
}
'RadioButtonField' {
$obj[$key] = $name
$dp = Get-Child $node 'DataPath'; if ($dp) { $obj['path'] = $dp }
Add-CommonProps $obj $node $name
+ Add-TitleLocation $obj $node 'None'
$rbt = Get-Child $node 'RadioButtonType'; if ($rbt) { $obj['radioButtonType'] = $rbt }
$cc = Get-Child $node 'ColumnsCount'; if ($cc) { $obj['columnsCount'] = [int]$cc }
$cl = $node.SelectSingleNode("lf:ChoiceList", $ns)
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index 36d7a06b..d8153b7c 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -216,7 +216,7 @@
| Свойство | Тип | Описание |
|----------|-----|----------|
| `path` | string | DataPath |
-| `titleLocation` | string | Расположение заголовка |
+| `titleLocation` | string | Расположение заголовка. **Нет ключа** → умный дефолт `Right` (флажки почти всегда справа). **`""`** → дефолт платформы (`Left`, тег не пишется). Значение (`none`/`left`/`top`/…) → как указано |
#### radio — RadioButtonField
@@ -237,7 +237,7 @@
| `path` | string | DataPath |
| `radioButtonType` | string | `Auto` (по умолчанию), `RadioButtons`, `Tumbler` |
| `columnsCount` | int | Число колонок раскладки |
-| `titleLocation` | string | Расположение заголовка (компилятор подставляет `None`, если не задан) |
+| `titleLocation` | string | Расположение заголовка. **Нет ключа** → умный дефолт `None`. **`""`** → дефолт платформы (тег не пишется). Значение → как указано |
| `choiceList` | array | Варианты выбора: массив `{ value, presentation }` |
`choiceList[*]`:
diff --git a/tests/skills/cases/form-compile/input-fields.json b/tests/skills/cases/form-compile/input-fields.json
index 27c45e99..a29c9cc3 100644
--- a/tests/skills/cases/form-compile/input-fields.json
+++ b/tests/skills/cases/form-compile/input-fields.json
@@ -21,7 +21,9 @@
{ "input": "ПолеПароля", "path": "ПолеПароля", "passwordMode": true, "title": "Пароль" },
{ "input": "ПолеСКнопками", "path": "ПолеСКнопками", "choiceButton": true, "clearButton": true, "title": "Выбор" },
{ "input": "ПолеПодсказка", "path": "ПолеПодсказка", "inputHint": "Введите значение...", "title": "Подсказка" },
- { "check": "Флаг", "path": "Флаг", "title": "Включено" }
+ { "check": "Флаг", "path": "Флаг", "title": "Включено" },
+ { "check": "ФлагПлатформенный", "path": "ФлагПлатформенный", "title": "Слева", "titleLocation": "" },
+ { "check": "ФлагЯвный", "path": "ФлагЯвный", "title": "Сверху", "titleLocation": "top" }
],
"attributes": [
{ "name": "Объект", "type": "DataProcessorObject.ПоляВвода", "main": true },
@@ -30,7 +32,9 @@
{ "name": "ПолеПароля", "type": "string(50)" },
{ "name": "ПолеСКнопками", "type": "string" },
{ "name": "ПолеПодсказка", "type": "string" },
- { "name": "Флаг", "type": "boolean" }
+ { "name": "Флаг", "type": "boolean" },
+ { "name": "ФлагПлатформенный", "type": "boolean" },
+ { "name": "ФлагЯвный", "type": "boolean" }
]
}
}
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 d07eaea2..0a81de55 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
@@ -87,15 +87,38 @@
+
+ ФлагПлатформенный
+
+
+ ru
+ Слева
+
+
+
+
+
+
+ ФлагЯвный
+
+
+ ru
+ Сверху
+
+
+ Top
+
+
+
-
+
cfg:DataProcessorObject.ПоляВвода
true
-
+
ru
@@ -110,7 +133,7 @@
-
+
ru
@@ -125,7 +148,7 @@
-
+
ru
@@ -140,7 +163,7 @@
-
+
ru
@@ -155,7 +178,7 @@
-
+
ru
@@ -170,7 +193,7 @@
-
+
ru
@@ -181,5 +204,27 @@
xs:boolean
+
+
+
+ ru
+ Флаг платформенный
+
+
+
+ xs:boolean
+
+
+
+
+
+ ru
+ Флаг явный
+
+
+
+ xs:boolean
+
+