From da1d8c9297640763618090336206c70778df3ceb Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Fri, 12 Jun 2026 11:22:52 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-decompile,form-compile):=20=D1=81?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=D1=80=D0=B5=D1=81=D1=81-=D0=BC=D0=B0=D1=80?= =?UTF-8?q?=D0=BA=D0=B5=D1=80=20title:""=20=D0=B4=D0=BB=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=20(=D0=BF=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B0=D0=B2=D1=82=D0=BE?= =?UTF-8?q?-=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D0=BE=D0=BB=D0=BE=D0=B2=D0=BA=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Раундтрип додумывал команды из имени, когда в оригинале заголовка нет (напр. ФормаРабочегоМеста_320х320: 126 строк ADDED — «Задания выбрать», «Сканирование далее» и т.п. на ~30 командах-кнопках с одной картинкой). Корпус (acc+erp 8.3.24): из 92040 команд форм 99.87% имеют <Title> (захват штатный), лишь 121 (0.13%) — без заголовка. Значит авто-вывод как дефолт верен (помощь модели), а для редкого хвоста нужен суппресс-маркер — ровно как уже сделано для элементов (Emit-Title) и формы (autoTitle). compile (ps1+py): Emit-Commands зеркалит Emit-Title — ключ title есть+непустой → эмитим; есть+"" → суппресс (не эмитим, не додумываем); отсутствует → авто-вывод из имени. decompile: нет <Title> у команды → title:"" (иначе компилятор додумает). Верификация: таргет-раундтрип формы → match (было 126 → 0); регресс form-compile 43/43 (ps1+py); 1С-cert кейса commands (команда с title:"" грузится без <Title>). spec обновлён. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --- .../skills/form-compile/scripts/form-compile.ps1 | 12 ++++++++---- .../skills/form-compile/scripts/form-compile.py | 14 ++++++++++---- .../form-decompile/scripts/form-decompile.ps1 | 8 ++++++-- docs/form-dsl-spec.md | 2 +- tests/skills/cases/form-compile/commands.json | 3 ++- .../Команды/Forms/Форма/Ext/Form.xml | 8 ++++++++ 6 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index b1eb6b6c..c6a145ec 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.126 — Compile 1C managed form from JSON or object metadata +# form-compile v1.127 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -5390,9 +5390,13 @@ function Emit-Commands { X "$indent`t<Command name=`"$($cmd.name)`" id=`"$cmdId`">" $inner = "$indent`t`t" - $cmdTitle = if ($cmd.title) { $cmd.title } else { Title-FromName -name "$($cmd.name)" } - if ($cmdTitle) { - Emit-MLText -tag "Title" -text $cmdTitle -indent $inner + # Заголовок команды (зеркало Emit-Title): ключ есть+непустой → эмитим; ключ есть+"" → суппресс + # (в оригинале <Title> нет — не додумывать); ключ отсутствует → авто-вывод из имени (помощь модели). + if ($null -ne $cmd.PSObject.Properties['title']) { + if ($cmd.title) { Emit-MLText -tag "Title" -text $cmd.title -indent $inner } + } else { + $cmdTitle = Title-FromName -name "$($cmd.name)" + if ($cmdTitle) { Emit-MLText -tag "Title" -text $cmdTitle -indent $inner } } if ($cmd.tooltip) { diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index a2ea811b..b940c055 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.126 — Compile 1C managed form from JSON or object metadata +# form-compile v1.127 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -5111,9 +5111,15 @@ def emit_commands(lines, cmds, indent): lines.append(f'{indent}\t<Command name="{cmd["name"]}" id="{cmd_id}">') inner = f'{indent}\t\t' - cmd_title = cmd.get('title') or title_from_name(str(cmd['name'])) - if cmd_title: - emit_mltext(lines, inner, 'Title', cmd_title) + # Заголовок команды (зеркало emit_title): ключ есть+непустой → эмитим; ключ есть+"" → суппресс + # (в оригинале <Title> нет — не додумывать); ключ отсутствует → авто-вывод из имени. + if 'title' in cmd: + if cmd['title']: + emit_mltext(lines, inner, 'Title', cmd['title']) + else: + cmd_title = title_from_name(str(cmd['name'])) + if cmd_title: + emit_mltext(lines, inner, 'Title', cmd_title) if cmd.get('tooltip'): emit_mltext(lines, inner, 'ToolTip', cmd['tooltip']) diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index ba073368..bb4919d3 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 v1.00 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v1.01 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -2517,7 +2517,11 @@ if ($cmdsNode) { $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 } } + # Заголовок команды: есть <Title> → захват; нет → суппресс-маркер "" (иначе компилятор + # додумает из имени — авто-вывод неверен для ~0.13% команд без заголовка в оригинале). + $tNode = $c.SelectSingleNode("lf:Title", $ns) + if ($tNode) { $t = Get-LangText $tNode; if ($null -ne $t) { $co['title'] = $t } } + else { $co['title'] = '' } $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 } $cfo = Decompile-FunctionalOptions $c; if ($cfo) { $co['functionalOptions'] = $cfo } diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 4a6f83bc..d651b4b2 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -1027,7 +1027,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и |----------|-----|----------| | `name` | string | Имя команды (обязательно) | | `action` | string | Имя процедуры-обработчика | -| `title` | string | Заголовок | +| `title` | string/object | Заголовок (`<Title>`, мультиязычный объект `{ru,en}` поддерживается). **Авто-вывод:** ключ отсутствует → компилятор додумывает заголовок из имени (`ЗаданияВыбрать` → «Задания выбрать»), помощь модели. Суппресс-маркер `title: ""` → заголовок не эмитить и не додумывать (для команд без `<Title>` в оригинале — редкие ~0.13%) | | `tooltip` | string/object | Всплывающая подсказка команды (`<ToolTip>`) | | `use` | bool/object | Доступность команды по ролям (`<Use>`). См. §4.1c | | `functionalOptions` | array | Функциональные опции команды (см. §5) | diff --git a/tests/skills/cases/form-compile/commands.json b/tests/skills/cases/form-compile/commands.json index b73a174a..33de8a62 100644 --- a/tests/skills/cases/form-compile/commands.json +++ b/tests/skills/cases/form-compile/commands.json @@ -28,7 +28,8 @@ { "name": "Результат", "type": "string" } ], "commands": [ - { "name": "Выполнить", "action": "ВыполнитьОбработка", "shortcut": "Ctrl+Enter", "use": false, "modifiesSavedData": true, "picture": { "src": "StdPicture.ExecuteTask", "transparentPixel": { "x": 1, "y": 1 } } } + { "name": "Выполнить", "action": "ВыполнитьОбработка", "shortcut": "Ctrl+Enter", "use": false, "modifiesSavedData": true, "picture": { "src": "StdPicture.ExecuteTask", "transparentPixel": { "x": 1, "y": 1 } } }, + { "name": "БезЗаголовка", "action": "БезЗаголовкаОбработка", "title": "", "representation": "Picture", "picture": { "src": "StdPicture.Refresh" } } ], "commandInterface": { "commandBar": { diff --git a/tests/skills/cases/form-compile/snapshots/commands/DataProcessors/Команды/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/commands/DataProcessors/Команды/Forms/Форма/Ext/Form.xml index 1a2d59a1..92985b09 100644 --- a/tests/skills/cases/form-compile/snapshots/commands/DataProcessors/Команды/Forms/Форма/Ext/Form.xml +++ b/tests/skills/cases/form-compile/snapshots/commands/DataProcessors/Команды/Forms/Форма/Ext/Form.xml @@ -94,6 +94,14 @@ <xr:TransparentPixel x="1" y="1"/> </Picture> </Command> + <Command name="БезЗаголовка" id="13"> + <Action>БезЗаголовкаОбработка</Action> + <Picture> + <xr:Ref>StdPicture.Refresh</xr:Ref> + <xr:LoadTransparent>true</xr:LoadTransparent> + </Picture> + <Representation>Picture</Representation> + </Command> </Commands> <CommandInterface> <CommandBar>