diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index d62215ef..e4148cd1 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.80 — Compile 1C managed form from JSON or object metadata +# form-compile v1.81 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -2287,6 +2287,34 @@ function Emit-Events { X "$indent" } +# ExtendedTooltip — это LabelDecoration: может нести own-content (layout/оформление/флаги/hyperlink) +# вместо/вместе с текстом. Признак структурированной формы: объект с любым НЕ-текстовым ключом +# ({text,formatted} и языковые ключи {ru,en} → обычная текст-форма). +$script:companionStructKeys = @( + 'width','autoMaxWidth','maxWidth','height','autoMaxHeight','maxHeight','verticalAlign','titleHeight', + 'horizontalStretch','verticalStretch','horizontalAlign','groupHorizontalAlign','groupVerticalAlign', + 'visible','hidden','enabled','disabled','hyperlink', + 'textColor','backColor','borderColor','font','border','цветтекста','цветфона','цветрамки','шрифт','рамка' +) +function Test-CompanionStructured { + param($content) + if (-not (($content -is [System.Collections.IDictionary]) -or ($content -is [System.Management.Automation.PSCustomObject]))) { return $false } + foreach ($k in $script:companionStructKeys) { + $present = if ($content -is [System.Collections.IDictionary]) { $content.Contains($k) } else { [bool]$content.PSObject.Properties[$k] } + if ($present) { return $true } + } + return $false +} + +function Emit-CompanionTitle { + param($content, [string]$indent) + $r = Resolve-MLFormatted $content + $fmt = if ($r.formatted) { 'true' } else { 'false' } + X "$indent" + Emit-MLItems -val $r.text -indent "$indent`t" + X "$indent" +} + function Emit-Companion { param([string]$tag, [string]$name, [string]$indent, $content = $null) $id = New-Id @@ -2295,13 +2323,20 @@ function Emit-Companion { X "$indent<$tag name=`"$name`" id=`"$id`"/>" return } - # Companion с контентом: (расширенная подсказка) + $inner = "$indent`t" X "$indent<$tag name=`"$name`" id=`"$id`">" - $r = Resolve-MLFormatted $content - $fmt = if ($r.formatted) { 'true' } else { 'false' } - X "$indent`t<Title formatted=`"$fmt`">" - Emit-MLItems -val $r.text -indent "$indent`t`t" - X "$indent`t" + if (Test-CompanionStructured $content) { + # структурированная форма (own-content). Порядок как у платформы: own-content (флаги/hyperlink/ + # layout/оформление) ПЕРЕД Title (в корпусе layout-first 582 vs 10). + $txtPresent = if ($content -is [System.Collections.IDictionary]) { $content.Contains('text') } else { [bool]$content.PSObject.Properties['text'] } + Emit-CommonFlags -el $content -indent $inner + if ($content.hyperlink -eq $true) { X "$innertrue" } + Emit-Layout -el $content -indent $inner + Emit-Appearance -el $content -indent $inner -profile 'decoration' + if ($txtPresent) { Emit-CompanionTitle -content $content -indent $inner } + } else { + Emit-CompanionTitle -content $content -indent $inner + } X "$indent" } diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index a20bf72d..e3d44ccc 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.80 — Compile 1C managed form from JSON or object metadata +# form-compile v1.81 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -2296,17 +2296,42 @@ def resolve_ml_formatted(val): return val, _has_real_markup(val) +# ExtendedTooltip — это LabelDecoration: own-content (layout/оформление/флаги/hyperlink) ±текст. +# Признак структурированной формы: объект с любым НЕ-текстовым ключом ({text,formatted}/{ru,en} → текст). +COMPANION_STRUCT_KEYS = { + 'width', 'autoMaxWidth', 'maxWidth', 'height', 'autoMaxHeight', 'maxHeight', 'verticalAlign', 'titleHeight', + 'horizontalStretch', 'verticalStretch', 'horizontalAlign', 'groupHorizontalAlign', 'groupVerticalAlign', + 'visible', 'hidden', 'enabled', 'disabled', 'hyperlink', + 'textColor', 'backColor', 'borderColor', 'font', 'border', 'цветтекста', 'цветфона', 'цветрамки', 'шрифт', 'рамка', +} + + +def emit_companion_title(lines, content, indent): + text, fmt = resolve_ml_formatted(content) + lines.append(f'{indent}') + emit_ml_items(lines, f'{indent}\t', text) + lines.append(f'{indent}') + + def emit_companion(lines, tag, name, indent, content=None): cid = new_id() has_content = content is not None and not (isinstance(content, str) and content == '') if not has_content: lines.append(f'{indent}<{tag} name="{name}" id="{cid}"/>') return + inner = f'{indent}\t' lines.append(f'{indent}<{tag} name="{name}" id="{cid}">') - text, fmt = resolve_ml_formatted(content) - lines.append(f'{indent}\t') - emit_ml_items(lines, f'{indent}\t\t', text) - lines.append(f'{indent}\t') + if isinstance(content, dict) and any(k in content for k in COMPANION_STRUCT_KEYS): + # own-content ПЕРЕД Title (в корпусе layout-first 582 vs 10). + emit_common_flags(lines, content, inner) + if content.get('hyperlink') is True: + lines.append(f'{inner}true') + emit_layout(lines, content, inner) + emit_appearance(lines, content, inner, 'decoration') + if 'text' in content: + emit_companion_title(lines, content, inner) + else: + emit_companion_title(lines, content, inner) lines.append(f'{indent}') diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index 0a1ec0e2..5ac7ce4a 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.56 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v0.57 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -1599,9 +1599,30 @@ function Decompile-Element { } 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 } } + # extendedTooltip: companion <ExtendedTooltip> (это LabelDecoration). Текст-форма (только <Title>) → + # строка/{text,formatted}. Own-content (layout/оформление/флаги/hyperlink) → объект { text?, … }. + # Events компаньона пока НЕ захватываем (хвост — требует имя-обработчик). + $etNode = $node.SelectSingleNode("lf:ExtendedTooltip", $ns) + if ($etNode) { + $etTitle = $etNode.SelectSingleNode("lf:Title", $ns) + $textVal = if ($etTitle) { Get-MLFormattedValue $etTitle } else { $null } + $etObj = [ordered]@{} + Add-Layout $etObj $etNode + Add-GenericScalars $etObj $etNode + Add-Appearance $etObj $etNode + if ((Get-Child $etNode 'Visible') -eq 'false') { $etObj['hidden'] = $true } + if ((Get-Child $etNode 'Enabled') -eq 'false') { $etObj['disabled'] = $true } + if ((Get-Child $etNode 'Hyperlink') -eq 'true') { $etObj['hyperlink'] = $true } + if ($etObj.Count -gt 0) { + if ($null -ne $textVal) { + if ($textVal -is [System.Collections.IDictionary]) { $etObj['text'] = $textVal['text']; if ($textVal['formatted']) { $etObj['formatted'] = $true } } + else { $etObj['text'] = $textVal } + } + $obj['extendedTooltip'] = $etObj + } elseif ($null -ne $textVal) { + $obj['extendedTooltip'] = $textVal + } + } # companion-панели с контентом: AutoCommandBar → commandBar, ContextMenu → contextMenu (любой элемент) $isDynListTable = ($tag -eq 'Table') -and (Has-Child $node 'UpdateOnDataChange') $cb = Decompile-CompanionPanel $node 'AutoCommandBar' $isDynListTable diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 34425e99..7defcfa0 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -121,7 +121,7 @@ | `titleLocation` | string | Расположение заголовка: `none`/`left`/`right`/`top`/`bottom`/`auto`. Эмитится при наличии (input, labelField, picField, table, calendar). У `check`/`radio` — особая семантика с умным дефолтом (см. их разделы) | | `tooltip` | string/object | Всплывающая подсказка элемента (`<ToolTip>`). Строка → ru, объект `{ "ru": …, "en": … }` → мультиязычный (как `title`). Эмитится сразу после `title` | | `tooltipRepresentation` | string | Режим показа подсказки (`<ToolTipRepresentation>`): `None`, `Button`, `ShowBottom`, `ShowTop`, `ShowLeft`, `ShowRight`, `ShowAuto`, `Balloon`. Эмитится при наличии | -| `extendedTooltip` | string/object | Расширенная подсказка (контент companion `<ExtendedTooltip>`). См. форму ML-текста ниже. Синоним: `extTooltip` | +| `extendedTooltip` | string/object | Расширенная подсказка (companion `<ExtendedTooltip>`, по сути LabelDecoration). **Текст-форма**: строка / ML / `{text, formatted}`. **Own-content форма** (объект с layout/оформлением/флагами): `{ text?, formatted?, width?, autoMaxWidth?, maxWidth?, height?, horizontalStretch?, verticalAlign?, titleHeight?, hyperlink?, visible?, enabled?, textColor?, font?, … }` — own-content эмитится перед `Title`. (События компаньона пока не поддержаны.) Синоним: `extTooltip` | #### Форма ML-текста и `formatted` diff --git a/tests/skills/cases/form-compile/input-fields.json b/tests/skills/cases/form-compile/input-fields.json index 08bd8735..bfe26a96 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; ЧН=" }, + { "input": "ОбычноеПоле", "path": "ОбычноеПоле", "title": "Обычное поле", "tooltip": "Введите значение поля", "tooltipRepresentation": "ShowBottom", "editMode": "EnterOnInput", "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" } }, { "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": "Пароль" }, @@ -45,7 +45,7 @@ "choiceParameterLinks": [ "Отбор.Организация=ОбычноеПоле", "Отбор.Тип=ПолеСписокВыбора:DontChange" ], "typeLink": "ПолеПодсказка" }, - { "check": "Флаг", "path": "Флаг", "title": "Включено" }, + { "check": "Флаг", "path": "Флаг", "title": "Включено", "extendedTooltip": { "width": 45 } }, { "check": "ФлагПлатформенный", "path": "ФлагПлатформенный", "title": "Слева", "titleLocation": "" }, { "check": "ФлагЯвный", "path": "ФлагЯвный", "title": "Сверху", "titleLocation": "top" }, { "check": "ФлагТумблер", "path": "ФлагТумблер", "title": "Тумблер", "checkBoxType": "switcher", "format": "БЛ=Нет; БИ=Да" } 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 3b8dbff8..54bac057 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 @@ -40,7 +40,16 @@ </v8:item> </EditFormat> <ContextMenu name="ОбычноеПолеКонтекстноеМеню" id="2"/> - <ExtendedTooltip name="ОбычноеПолеРасширеннаяПодсказка" id="3"/> + <ExtendedTooltip name="ОбычноеПолеРасширеннаяПодсказка" id="3"> + <AutoMaxWidth>false</AutoMaxWidth> + <Width>30</Width> + <Title formatted="false"> + <v8:item> + <v8:lang>ru</v8:lang> + <v8:content>Расширенная подсказка поля</v8:content> + </v8:item> + + ОбычноеПоле @@ -308,7 +317,9 @@ Auto Right - + + 45 + ФлагПлатформенный