feat(form-compile): авто-вид кнопки по контексту АКП

Кнопки внутри cmdBar/autoCmdBar/popup автоматически получают
CommandBarButton (или CommandBarHyperlink при type="hyperlink") —
указывать вид вручную не нужно. Резолвер прощающий: принимает и
короткие DSL-формы, и XML-имена в любом контексте.

Пример «Диалог загрузки файла» в SKILL.md и тест-кейс file-dialog
переведены на нативный паттерн с autoCmdBar вместо отдельной
горизонтальной группы кнопок внизу формы.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-05-03 19:41:43 +03:00
parent 60de083a05
commit 6c60398406
8 changed files with 125 additions and 59 deletions
+5 -3
View File
@@ -228,7 +228,7 @@ powershell.exe -NoProfile -File .claude/skills/form-compile/scripts/form-compile
| `command` | Имя команды формы → `Form.Command.Имя` |
| `stdCommand` | Стандартная команда: `"Close"``Form.StandardCommand.Close`; с точкой: `"Товары.Add"``Form.Item.Товары.StandardCommand.Add` |
| `defaultButton: true` | Кнопка по умолчанию |
| `type` | `"usual"`, `"hyperlink"`, `"commandBar"` |
| `type` | `"usual"`, `"hyperlink"`. По умолчанию `"usual"`. Конкретный XML-вид (UsualButton/Hyperlink/CommandBarButton/CommandBarHyperlink) подставляется автоматически по контексту |
| `picture` | Картинка кнопки |
| `representation` | `"Auto"`, `"Text"`, `"Picture"`, `"PictureAndText"` |
| `locationInCommandBar` | `"Auto"`, `"InCommandBar"`, `"InAdditionalSubmenu"` |
@@ -259,6 +259,8 @@ powershell.exe -NoProfile -File .claude/skills/form-compile/scripts/form-compile
]}
```
Кнопки основных действий формы и подменю размещают здесь, а не в отдельной группе на форме. Отдельной кнопкой в layout — только если она логически привязана к конкретному полю или группе.
### Выпадающее меню (popup)
| Ключ | Описание |
@@ -401,9 +403,9 @@ powershell.exe -NoProfile -File .claude/skills/form-compile/scripts/form-compile
{ "check": "ПерваяСтрокаЗаголовок", "path": "ПерваяСтрокаЗаголовок" }
]},
{ "input": "Результат", "path": "Результат", "multiLine": true, "height": 8, "readOnly": true, "title": "Лог" },
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
{ "autoCmdBar": "ФормаКоманднаяПанель", "children": [
{ "button": "Загрузить", "command": "Загрузить", "defaultButton": true },
{ "button": "Закрыть", "stdCommand": "Close" }
{ "button": "Закрыть", "stdCommand": "Close" }
]}
],
"attributes": [
@@ -1,4 +1,4 @@
# form-compile v1.16 — Compile 1C managed form from JSON or object metadata
# form-compile v1.17 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -1825,7 +1825,7 @@ function Emit-Companion {
}
function Emit-Element {
param($el, [string]$indent)
param($el, [string]$indent, [bool]$inCmdBar = $false)
# Silent synonyms: model often writes XML name or Russian (ПолеПереключателя/RadioButtonField → radio).
# Maps any synonym to canonical short DSL key.
@@ -1946,7 +1946,7 @@ function Emit-Element {
"table" { Emit-Table -el $el -name $name -id $id -indent $indent }
"pages" { Emit-Pages -el $el -name $name -id $id -indent $indent }
"page" { Emit-Page -el $el -name $name -id $id -indent $indent }
"button" { Emit-Button -el $el -name $name -id $id -indent $indent }
"button" { Emit-Button -el $el -name $name -id $id -indent $indent -inCmdBar $inCmdBar }
"picture" { Emit-PictureDecoration -el $el -name $name -id $id -indent $indent }
"picField" { Emit-PictureField -el $el -name $name -id $id -indent $indent }
"calendar" { Emit-Calendar -el $el -name $name -id $id -indent $indent }
@@ -2538,19 +2538,48 @@ function Emit-Page {
}
function Emit-Button {
param($el, [string]$name, [int]$id, [string]$indent)
param($el, [string]$name, [int]$id, [string]$indent, [bool]$inCmdBar = $false)
X "$indent<Button name=`"$name`" id=`"$id`">"
$inner = "$indent`t"
# Type
# Type — context-aware:
# Inside command bar (cmdBar/autoCmdBar/popup) only CommandBarButton/CommandBarHyperlink are valid.
# UsualButton/Hyperlink would be silently ignored by 1C.
$btnType = $null
if ($el.type) {
$btnType = switch ("$($el.type)") {
"usual" { "UsualButton" }
"hyperlink" { "Hyperlink" }
"commandBar" { "CommandBarButton" }
default { "$($el.type)" }
$rawType = "$($el.type)"
if ($inCmdBar) {
# Be forgiving: any "ordinary button" hint resolves to CommandBarButton,
# any "hyperlink" hint resolves to CommandBarHyperlink. The model can pass
# either DSL ("usual"/"hyperlink") or XML names — all map to the right kind.
switch ($rawType) {
"usual" { $btnType = "CommandBarButton" }
"UsualButton" { $btnType = "CommandBarButton" }
"commandBar" { $btnType = "CommandBarButton" }
"CommandBarButton" { $btnType = "CommandBarButton" }
"hyperlink" { $btnType = "CommandBarHyperlink" }
"Hyperlink" { $btnType = "CommandBarHyperlink" }
"CommandBarHyperlink" { $btnType = "CommandBarHyperlink" }
default { $btnType = $rawType }
}
} else {
# Symmetric: any "ordinary button" hint → UsualButton, any "hyperlink" → Hyperlink.
switch ($rawType) {
"usual" { $btnType = "UsualButton" }
"UsualButton" { $btnType = "UsualButton" }
"commandBar" { $btnType = "UsualButton" }
"CommandBarButton" { $btnType = "UsualButton" }
"hyperlink" { $btnType = "Hyperlink" }
"Hyperlink" { $btnType = "Hyperlink" }
"CommandBarHyperlink" { $btnType = "Hyperlink" }
default { $btnType = $rawType }
}
}
} elseif ($inCmdBar) {
$btnType = "CommandBarButton"
}
if ($btnType) {
X "$inner<Type>$btnType</Type>"
}
@@ -2684,7 +2713,7 @@ function Emit-CommandBar {
if ($el.children -and $el.children.Count -gt 0) {
X "$inner<ChildItems>"
foreach ($child in $el.children) {
Emit-Element -el $child -indent "$inner`t"
Emit-Element -el $child -indent "$inner`t" -inCmdBar $true
}
X "$inner</ChildItems>"
}
@@ -2716,7 +2745,7 @@ function Emit-Popup {
if ($el.children -and $el.children.Count -gt 0) {
X "$inner<ChildItems>"
foreach ($child in $el.children) {
Emit-Element -el $child -indent "$inner`t"
Emit-Element -el $child -indent "$inner`t" -inCmdBar $true
}
X "$inner</ChildItems>"
}
@@ -3164,7 +3193,7 @@ if ($acbHasInner) {
if ($hasAcbChildren) {
X "`t`t<ChildItems>"
foreach ($child in $script:mainAcbDef.children) {
Emit-Element -el $child -indent "`t`t`t"
Emit-Element -el $child -indent "`t`t`t" -inCmdBar $true
}
X "`t`t</ChildItems>"
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.16 — Compile 1C managed form from JSON or object metadata
# form-compile v1.17 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -1760,7 +1760,7 @@ def emit_type(lines, type_str, indent):
# --- Element emitters ---
def emit_element(lines, el, indent):
def emit_element(lines, el, indent, in_cmd_bar=False):
# Silent synonyms: model often writes XML name or Russian (ПолеПереключателя/RadioButtonField → radio)
for src, dst in ELEMENT_TYPE_SYNONYMS.items():
if src in el and dst not in el:
@@ -1804,7 +1804,10 @@ def emit_element(lines, el, indent):
emitter = emitters.get(type_key)
if emitter:
emitter(lines, el, name, eid, indent)
if type_key == 'button':
emitter(lines, el, name, eid, indent, in_cmd_bar=in_cmd_bar)
else:
emitter(lines, el, name, eid, indent)
def emit_group(lines, el, name, eid, indent):
@@ -2188,14 +2191,42 @@ def emit_page(lines, el, name, eid, indent):
lines.append(f'{indent}</Page>')
def emit_button(lines, el, name, eid, indent):
def emit_button(lines, el, name, eid, indent, in_cmd_bar=False):
lines.append(f'{indent}<Button name="{name}" id="{eid}">')
inner = f'{indent}\t'
# Type
# Type — context-aware. Inside command bars (cmdBar/autoCmdBar/popup) only
# CommandBarButton/CommandBarHyperlink are valid; UsualButton/Hyperlink would be ignored.
# Forgiving resolver: any "ordinary button" hint resolves to UsualButton/CommandBarButton,
# any "hyperlink" hint resolves to Hyperlink/CommandBarHyperlink — depending on context.
btn_type = None
if el.get('type'):
btn_type_map = {'usual': 'UsualButton', 'hyperlink': 'Hyperlink', 'commandBar': 'CommandBarButton'}
btn_type = btn_type_map.get(str(el['type']), str(el['type']))
raw = str(el['type'])
if in_cmd_bar:
cmd_bar_map = {
'usual': 'CommandBarButton',
'UsualButton': 'CommandBarButton',
'commandBar': 'CommandBarButton',
'CommandBarButton': 'CommandBarButton',
'hyperlink': 'CommandBarHyperlink',
'Hyperlink': 'CommandBarHyperlink',
'CommandBarHyperlink': 'CommandBarHyperlink',
}
btn_type = cmd_bar_map.get(raw, raw)
else:
normal_map = {
'usual': 'UsualButton',
'UsualButton': 'UsualButton',
'commandBar': 'UsualButton',
'CommandBarButton': 'UsualButton',
'hyperlink': 'Hyperlink',
'Hyperlink': 'Hyperlink',
'CommandBarHyperlink': 'Hyperlink',
}
btn_type = normal_map.get(raw, raw)
elif in_cmd_bar:
btn_type = 'CommandBarButton'
if btn_type:
lines.append(f'{inner}<Type>{btn_type}</Type>')
# CommandName
@@ -2322,7 +2353,7 @@ def emit_command_bar(lines, el, name, eid, indent):
if el.get('children') and len(el['children']) > 0:
lines.append(f'{inner}<ChildItems>')
for child in el['children']:
emit_element(lines, child, f'{inner}\t')
emit_element(lines, child, f'{inner}\t', in_cmd_bar=True)
lines.append(f'{inner}</ChildItems>')
lines.append(f'{indent}</CommandBar>')
@@ -2348,7 +2379,7 @@ def emit_popup(lines, el, name, eid, indent):
if el.get('children') and len(el['children']) > 0:
lines.append(f'{inner}<ChildItems>')
for child in el['children']:
emit_element(lines, child, f'{inner}\t')
emit_element(lines, child, f'{inner}\t', in_cmd_bar=True)
lines.append(f'{inner}</ChildItems>')
lines.append(f'{indent}</Popup>')
@@ -2957,7 +2988,7 @@ def main():
if has_acb_children:
lines.append('\t\t<ChildItems>')
for child in main_acb_def['children']:
emit_element(lines, child, '\t\t\t')
emit_element(lines, child, '\t\t\t', in_cmd_bar=True)
lines.append('\t\t</ChildItems>')
lines.append('\t</AutoCommandBar>')
else:
@@ -23,7 +23,7 @@
{ "check": "ПерваяСтрокаЗаголовок", "path": "ПерваяСтрокаЗаголовок" }
]},
{ "input": "Результат", "path": "Результат", "multiLine": true, "height": 8, "readOnly": true, "title": "Лог" },
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
{ "autoCmdBar": "ФормаКоманднаяПанель", "children": [
{ "button": "Загрузить", "command": "Загрузить", "defaultButton": true },
{ "button": "Закрыть", "stdCommand": "Close" }
]}
@@ -10,6 +10,7 @@
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
<ChildItems>
<Button name="ИзменитьВыделенные" id="1">
<Type>CommandBarButton</Type>
<CommandName>Form.Command.ИзменитьВыделенные</CommandName>
<LocationInCommandBar>InAdditionalSubmenu</LocationInCommandBar>
<ExtendedTooltip name="ИзменитьВыделенныеРасширеннаяПодсказка" id="2"/>
@@ -14,11 +14,13 @@
<CommandBar name="Панель" id="1">
<ChildItems>
<Button name="Выполнить" id="2">
<Type>CommandBarButton</Type>
<CommandName>Form.Command.Выполнить</CommandName>
<DefaultButton>true</DefaultButton>
<ExtendedTooltip name="ВыполнитьРасширеннаяПодсказка" id="3"/>
</Button>
<Button name="Закрыть" id="4">
<Type>CommandBarButton</Type>
<CommandName>Form.StandardCommand.Close</CommandName>
<ExtendedTooltip name="ЗакрытьРасширеннаяПодсказка" id="5"/>
</Button>
@@ -7,16 +7,30 @@
</v8:item>
</Title>
<AutoTitle>false</AutoTitle>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
<ChildItems>
<Button name="Загрузить" id="1">
<Type>CommandBarButton</Type>
<CommandName>Form.Command.Загрузить</CommandName>
<DefaultButton>true</DefaultButton>
<ExtendedTooltip name="ЗагрузитьРасширеннаяПодсказка" id="2"/>
</Button>
<Button name="Закрыть" id="3">
<Type>CommandBarButton</Type>
<CommandName>Form.StandardCommand.Close</CommandName>
<ExtendedTooltip name="ЗакрытьРасширеннаяПодсказка" id="4"/>
</Button>
</ChildItems>
</AutoCommandBar>
<Events>
<Event name="OnCreateAtServer">ПриСозданииНаСервере</Event>
</Events>
<ChildItems>
<UsualGroup name="ГруппаФайл" id="1">
<UsualGroup name="ГруппаФайл" id="5">
<Group>Horizontal</Group>
<ExtendedTooltip name="ГруппаФайлРасширеннаяПодсказка" id="2"/>
<ExtendedTooltip name="ГруппаФайлРасширеннаяПодсказка" id="6"/>
<ChildItems>
<InputField name="ИмяФайла" id="3">
<InputField name="ИмяФайла" id="7">
<DataPath>ИмяФайла</DataPath>
<Title>
<v8:item>
@@ -30,21 +44,21 @@
<v8:content>Выберите файл...</v8:content>
</v8:item>
</InputHint>
<ContextMenu name="ИмяФайлаКонтекстноеМеню" id="4"/>
<ExtendedTooltip name="ИмяФайлаРасширеннаяПодсказка" id="5"/>
<ContextMenu name="ИмяФайлаКонтекстноеМеню" id="8"/>
<ExtendedTooltip name="ИмяФайлаРасширеннаяПодсказка" id="9"/>
<Events>
<Event name="StartChoice">ИмяФайлаНачалоВыбора</Event>
</Events>
</InputField>
<CheckBoxField name="ПерваяСтрокаЗаголовок" id="6">
<CheckBoxField name="ПерваяСтрокаЗаголовок" id="10">
<DataPath>ПерваяСтрокаЗаголовок</DataPath>
<TitleLocation>Right</TitleLocation>
<ContextMenu name="ПерваяСтрокаЗаголовокКонтекстноеМеню" id="7"/>
<ExtendedTooltip name="ПерваяСтрокаЗаголовокРасширеннаяПодсказка" id="8"/>
<ContextMenu name="ПерваяСтрокаЗаголовокКонтекстноеМеню" id="11"/>
<ExtendedTooltip name="ПерваяСтрокаЗаголовокРасширеннаяПодсказка" id="12"/>
</CheckBoxField>
</ChildItems>
</UsualGroup>
<InputField name="Результат" id="9">
<InputField name="Результат" id="13">
<DataPath>Результат</DataPath>
<Title>
<v8:item>
@@ -56,33 +70,18 @@
<MultiLine>true</MultiLine>
<AutoMaxWidth>false</AutoMaxWidth>
<Height>8</Height>
<ContextMenu name="РезультатКонтекстноеМеню" id="10"/>
<ExtendedTooltip name="РезультатРасширеннаяПодсказка" id="11"/>
<ContextMenu name="РезультатКонтекстноеМеню" id="14"/>
<ExtendedTooltip name="РезультатРасширеннаяПодсказка" id="15"/>
</InputField>
<UsualGroup name="ГруппаКнопок" id="12">
<Group>Horizontal</Group>
<ExtendedTooltip name="ГруппаКнопокРасширеннаяПодсказка" id="13"/>
<ChildItems>
<Button name="Загрузить" id="14">
<CommandName>Form.Command.Загрузить</CommandName>
<DefaultButton>true</DefaultButton>
<ExtendedTooltip name="ЗагрузитьРасширеннаяПодсказка" id="15"/>
</Button>
<Button name="Закрыть" id="16">
<CommandName>Form.StandardCommand.Close</CommandName>
<ExtendedTooltip name="ЗакрытьРасширеннаяПодсказка" id="17"/>
</Button>
</ChildItems>
</UsualGroup>
</ChildItems>
<Attributes>
<Attribute name="Объект" id="18">
<Attribute name="Объект" id="16">
<Type>
<v8:Type>cfg:DataProcessorObject.ЗагрузкаИзФайла</v8:Type>
</Type>
<MainAttribute>true</MainAttribute>
</Attribute>
<Attribute name="ИмяФайла" id="19">
<Attribute name="ИмяФайла" id="17">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
@@ -97,7 +96,7 @@
</v8:StringQualifiers>
</Type>
</Attribute>
<Attribute name="ПерваяСтрокаЗаголовок" id="20">
<Attribute name="ПерваяСтрокаЗаголовок" id="18">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
@@ -108,7 +107,7 @@
<v8:Type>xs:boolean</v8:Type>
</Type>
</Attribute>
<Attribute name="Результат" id="21">
<Attribute name="Результат" id="19">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
@@ -125,7 +124,7 @@
</Attribute>
</Attributes>
<Commands>
<Command name="Загрузить" id="22">
<Command name="Загрузить" id="20">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
@@ -10,6 +10,7 @@
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
<ChildItems>
<Button name="Кн1" id="1">
<Type>CommandBarButton</Type>
<CommandName>Form.Command.Кн1</CommandName>
<ExtendedTooltip name="Кн1РасширеннаяПодсказка" id="2"/>
</Button>
@@ -24,6 +25,7 @@
<CommandBar name="ДопПанель" id="6">
<ChildItems>
<Button name="Кн2" id="7">
<Type>CommandBarButton</Type>
<CommandName>Form.Command.Кн2</CommandName>
<ExtendedTooltip name="Кн2РасширеннаяПодсказка" id="8"/>
</Button>