diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 1a8d3cf7..f24ec997 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.115 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.116 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -1570,8 +1570,9 @@ function Emit-MLItems {
}
function Emit-MLText {
- param([string]$tag, $text, [string]$indent)
- X "$indent<$tag>"
+ param([string]$tag, $text, [string]$indent, [string]$xsiType)
+ $attr = if ($xsiType) { " xsi:type=`"$xsiType`"" } else { "" }
+ X "$indent<$tag$attr>"
Emit-MLItems -val $text -indent "$indent`t"
X "$indent$tag>"
}
@@ -1860,8 +1861,12 @@ function Emit-AppearanceValue {
if ($null -ne $av) { $attrParts += "$attrName=`"$(Esc-Xml "$av")`"" }
}
X "$indent`t"
+ } elseif ($isDict -and (_HasKey $innerVal 'field')) {
+ # Ссылка на поле (dcscor:Field) — значение параметра оформления = поле компоновки
+ X "$indent`t$(Esc-Xml "$(_Get $innerVal 'field')")"
} elseif ($isDict) {
- Emit-MLText -tag "dcscor:value" -text $innerVal -indent "$indent`t"
+ # Локализуемый текст параметра оформления: платформа объявляет xsi:type на dcscor:value
+ Emit-MLText -tag "dcscor:value" -text $innerVal -indent "$indent`t" -xsiType "v8:LocalStringType"
} else {
$actualVal = "$innerVal"
$keyTypeMap = @{
@@ -1876,7 +1881,13 @@ function Emit-AppearanceValue {
if ($keyType) { X "$indent`t$(Esc-Xml $actualVal)" }
elseif ($actualVal -match '^(style|web|win):') { X "$indent`t$(Esc-Xml $actualVal)" }
elseif ($actualVal -eq "true" -or $actualVal -eq "false") { X "$indent`t$actualVal" }
- elseif ($key -eq "Текст" -or $key -eq "Заголовок" -or $key -eq "Формат") { Emit-MLText -tag "dcscor:value" -text $actualVal -indent "$indent`t" }
+ elseif ($key -eq "Текст" -or $key -eq "Заголовок" -or $key -eq "Формат") {
+ # Текст/Заголовок/Формат: голая строка = плоский xs:string (так платформа хранит
+ # нелокализованный литерал). Локализуемый текст → объект {ru,en} (ветка isDict выше).
+ # Пустая строка → самозакрывающийся тег (как у платформы).
+ if ($actualVal -eq '') { X "$indent`t" }
+ else { X "$indent`t$(Esc-Xml $actualVal)" }
+ }
elseif ($actualVal -match '^-?\d+(\.\d+)?$') { X "$indent`t$actualVal" }
elseif ($key -eq 'ЦветТекста' -or $key -eq 'ЦветФона' -or $key -eq 'ЦветГраницы') { X "$indent`t$(Esc-Xml $actualVal)" }
else { X "$indent`t$(Esc-Xml $actualVal)" }
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index 429881a8..73abe5dd 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.115 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.116 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -1300,11 +1300,12 @@ def emit_ml_items(lines, indent, val):
lines.append(f"{indent}")
-def emit_mltext(lines, indent, tag, text):
+def emit_mltext(lines, indent, tag, text, xsi_type=None):
+ attr = f' xsi:type="{xsi_type}"' if xsi_type else ''
if not text:
- lines.append(f"{indent}<{tag}/>")
+ lines.append(f"{indent}<{tag}{attr}/>")
return
- lines.append(f"{indent}<{tag}>")
+ lines.append(f"{indent}<{tag}{attr}>")
emit_ml_items(lines, f"{indent}\t", text)
lines.append(f"{indent}{tag}>")
@@ -1606,8 +1607,12 @@ def emit_appearance_value(lines, key, val, indent):
if av is not None:
attr_parts.append(f'{attr_name}="{esc_xml(str(av))}"')
lines.append(f'{indent}\t')
+ elif is_dict and _has_key(inner_val, 'field'):
+ # Ссылка на поле (dcscor:Field) — значение параметра оформления = поле компоновки
+ lines.append(f'{indent}\t{esc_xml(str(_get(inner_val, "field")))}')
elif is_dict:
- emit_mltext(lines, f'{indent}\t', 'dcscor:value', inner_val)
+ # Локализуемый текст параметра оформления: платформа объявляет xsi:type на dcscor:value
+ emit_mltext(lines, f'{indent}\t', 'dcscor:value', inner_val, xsi_type='v8:LocalStringType')
else:
actual_val = str(inner_val)
key_type_map = {
@@ -1626,7 +1631,12 @@ def emit_appearance_value(lines, key, val, indent):
elif actual_val == 'true' or actual_val == 'false':
lines.append(f'{indent}\t{actual_val}')
elif key == 'Текст' or key == 'Заголовок' or key == 'Формат':
- emit_mltext(lines, f'{indent}\t', 'dcscor:value', actual_val)
+ # Голая строка = плоский xs:string (нелокализованный литерал). Локализуемый → объект {ru,en}.
+ # Пустая строка → самозакрывающийся тег (как у платформы).
+ if actual_val == '':
+ lines.append(f'{indent}\t')
+ else:
+ lines.append(f'{indent}\t{esc_xml(actual_val)}')
elif re.match(r'^-?\d+(\.\d+)?$', actual_val):
lines.append(f'{indent}\t{actual_val}')
elif key == 'ЦветТекста' or key == 'ЦветФона' or key == 'ЦветГраницы':
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index 86be4675..3bd38d04 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.91 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.92 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -407,14 +407,26 @@ function Get-LineValue {
return $obj
}
-# Прочитать в JSON-значение: Font/Line/multilang/raw text.
+# Прочитать в JSON-значение: Font/Line/Field/multilang/raw text.
function Read-AppearanceValueNode {
param($valNode)
if (-not $valNode) { return $null }
$vt = Get-LocalXsiType $valNode
- if ($vt -eq 'LocalStringType') { return (Get-MLText $valNode) }
+ if ($vt -eq 'LocalStringType') {
+ # НЕ схлопываем одноязычный в строку: значение параметра оформления различает
+ # xs:string (плоская строка) и LocalStringType (локализуемый текст) — обе формы
+ # одноязычно дают одну строку. Всегда объект-карта языков → компилятор эмитит LocalStringType.
+ $map = [ordered]@{}
+ foreach ($it in @($valNode.SelectNodes("v8:item", $ns))) {
+ $lang = $it.SelectSingleNode("v8:lang", $ns); $content = $it.SelectSingleNode("v8:content", $ns)
+ if ($lang) { $map[$lang.InnerText] = if ($content) { $content.InnerText } else { "" } }
+ }
+ return $map
+ }
if ($vt -eq 'Font') { return (Get-FontValue $valNode) }
if ($vt -eq 'Line') { return (Get-LineValue $valNode) }
+ # dcscor:Field — значение = ссылка на поле компоновки → объект {field:путь}
+ if ($vt -eq 'Field') { return [ordered]@{ field = $valNode.InnerText } }
return $valNode.InnerText
}
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index 2bd6f8c2..ced1e68b 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -976,6 +976,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
- **order** — строка `"Поле"` (asc) / `"Поле desc"` (синонимы `убыв`/`desc`, `возр`/`asc`) / `"Auto"`, либо объект `{ field, direction?, use?, viewMode? }`.
- **filter** — shorthand `"Поле оператор значение @флаги"` (`@off`, `@user`, `@quickAccess`, `@normal`, `@inaccessible`; `_` = пусто) или объект `{ field, op, value?, use?, userSettingID? }` или группа `{ group: "And"|"Or"|"Not", items: [...] }`.
- **conditionalAppearance** — объект `{ selection?, filter?, appearance?, presentation?, viewMode?, userSettingID?, use? }`. `appearance` — словарь «параметр: значение» платформы (`ЦветТекста`, `ЦветФона`, `Шрифт` и т.п.).
+ - Значение текстовых параметров (`Текст`/`Заголовок`/`Формат`) ведётся **по форме значения**: голая строка → плоский `xs:string` (нелокализованный литерал; `""` → самозакрывающийся тег); объект `{ru,en}` → локализуемый `LocalStringType`; объект `{field:"путь"}` → ссылка на поле компоновки (`dcscor:Field`). (В отличие от `title`/`tooltip`, где голая строка = `LocalStringType` — здесь это намеренное scoped-различие: платформа хранит обе формы, и их надо различать.)
`userSettingID: "auto"` → платформа сгенерирует идентификатор пользовательской настройки. Пустые контейнеры (без правил) эмитируются автоматически.
diff --git a/tests/skills/cases/form-compile/input-fields.json b/tests/skills/cases/form-compile/input-fields.json
index 4e62789a..85f4a877 100644
--- a/tests/skills/cases/form-compile/input-fields.json
+++ b/tests/skills/cases/form-compile/input-fields.json
@@ -69,8 +69,11 @@
{ "name": "ЧисловоеПоле", "type": "number(10,2)" }
],
"conditionalAppearance": [
- { "selection": ["ОбычноеПоле"], "filter": ["ЧисловоеПоле > 100"], "appearance": { "ЦветФона": "style:FormBackColor" },
- "presentation": { "ru": "Подсветка", "en": "Highlight" } }
+ { "selection": ["ОбычноеПоле"], "filter": ["ЧисловоеПоле > 100"], "appearance": { "ЦветФона": "style:FormBackColor", "Текст": "Плоский текст" },
+ "presentation": { "ru": "Подсветка", "en": "Highlight" } },
+ { "filter": ["ЧисловоеПоле = 0"], "appearance": { "Текст": { "ru": "Локализованный", "en": "Localized" } } },
+ { "filter": ["ЧисловоеПоле < 0"], "appearance": { "Текст": "" } },
+ { "filter": ["ЧисловоеПоле = 1"], "appearance": { "Текст": { "field": "ОбычноеПоле" } } }
]
}
}
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 ad0535c2..6b9f9437 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
@@ -629,6 +629,10 @@
ЦветФона
style:FormBackColor
+
+ Текст
+ Плоский текст
+
@@ -641,6 +645,63 @@
+
+
+
+
+ ЧисловоеПоле
+ Equal
+ 0
+
+
+
+
+ Текст
+
+
+ ru
+ Локализованный
+
+
+ en
+ Localized
+
+
+
+
+
+
+
+
+
+ ЧисловоеПоле
+ Less
+ 0
+
+
+
+
+ Текст
+
+
+
+
+
+
+
+
+ ЧисловоеПоле
+ Equal
+ 1
+
+
+
+
+ Текст
+ ОбычноеПоле
+
+
+