feat(form-decompile,form-compile): контент расширенной подсказки extendedTooltip + единая ML-text форма с formatted (кластер companion-content tier 1)

Companion <ExtendedTooltip> несёт <Title> (текст расширенной подсказки) — декомпилятор
пропускал companion целиком, текст терялся. Теперь companion = свойство родителя:
ключ extendedTooltip на элементе (синоним extTooltip).

Единая форма ML-текста (для title/tooltip/extendedTooltip):
- строка → ru; {ru,en} → многоязычно; {text, formatted: true} → форматированный.
- formatted: текст несётся RAW (1С inline-разметка <b>/<color>/<link>/</> — часть строки,
  round-trip через XML-экранирование, спаны не моделируем).
- Гибрид: флаг formatted авто-детектится по известной разметке/</> (детектор идентичен в
  ps1+py+декомпиляторе); явный {text,formatted} — только когда авто-детект неверен (~2%
  корпуса: formatted без разметки / литеральные <…>-плейсхолдеры). Авто-детект подтверждён
  данными (98% верно, мисматч 1003 из 53612).

Декомпилятор (v0.26): извлекает Title из companion <ExtendedTooltip> на родителя (гибрид).
Компилятор (ps1+py v1.44): Emit-Companion с опциональным контентом; 14 call-site'ов
ExtendedTooltip передают el.extendedTooltip; синоним extTooltip→extendedTooltip; whitelist.

Валидация: content-bearing round-trip CLEAN (Банки/ФормаЭлемента byte-identical, formatted=true
round-trip); регресс 33/33 ps+py; py==ps1 идентичны; harness 8368→8202.

Хвосты (в BACKLOG): пустое присутствие companion (Table/CommandBar/Popup не генерят
ExtendedTooltip — ~437, вскрыто фиксом метрики) и ExtendedTooltip с own-layout (<Width>).
Spec: extendedTooltip + раздел ML-text/formatted/markup-словарь.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-06 18:16:32 +03:00
co-authored by Claude Opus 4.8
parent e905d5f576
commit 684cd17d5f
4 changed files with 143 additions and 39 deletions
@@ -1,4 +1,4 @@
# form-decompile v0.25 — Decompile 1C managed Form.xml to JSON DSL (draft)
# form-decompile v0.26 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -230,6 +230,27 @@ function Get-LangText {
return $map
}
# Детектор «настоящей» inline-разметки форматированного текста (идентичен form-compile!).
$script:fmtMarkupRe = '</>|<\s*(?:link|b|i|u|s|color|colorStyle|bgColor|bgColorStyle|font|fontSize|fontStyle|img)(?:\s|>)'
function Test-HasRealMarkup {
param($text)
if ($null -eq $text) { return $false }
$vals = if ($text -is [System.Collections.IDictionary]) { @($text.Values) } else { @("$text") }
foreach ($v in $vals) { if ("$v" -match $script:fmtMarkupRe) { return $true } }
return $false
}
# Title-узел → DSL-значение ML-поля (гибрид): строка/мапа когда авто-детект formatted
# совпал с атрибутом; иначе явный {text, formatted}.
function Get-MLFormattedValue {
param($titleNode)
if (-not $titleNode) { return $null }
$text = Get-LangText $titleNode
if ($null -eq $text) { return $null }
$fmtAttr = ($titleNode.GetAttribute('formatted') -eq 'true')
if ($fmtAttr -eq (Test-HasRealMarkup $text)) { return $text }
$o = [ordered]@{}; $o['text'] = $text; $o['formatted'] = $fmtAttr; return $o
}
# Прочитать дочерний скаляр (по local-name, без namespace)
function Get-Child {
param($node, [string]$name)
@@ -1086,6 +1107,9 @@ function Decompile-Element {
if ($autoTitle) { $obj['title'] = '' }
}
Add-Layout $obj $node
# extendedTooltip: контент companion <ExtendedTooltip><Title> (любой элемент)
$etTitle = $node.SelectSingleNode("lf:ExtendedTooltip/lf:Title", $ns)
if ($etTitle) { $et = Get-MLFormattedValue $etTitle; if ($null -ne $et) { $obj['extendedTooltip'] = $et } }
return $obj
}