diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 2f6141ff..c13096c2 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.100 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.101 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -1890,11 +1890,11 @@ function Emit-AppearanceValue {
}
function Emit-ConditionalAppearance {
- param($items, [string]$indent, $blockViewMode = $null, $blockUserSettingID = $null)
+ param($items, [string]$indent, $blockViewMode = $null, $blockUserSettingID = $null, [string]$wrapTag = 'dcsset:conditionalAppearance')
$hasItems = $items -and $items.Count -gt 0
$hasBlockMeta = ($null -ne $blockViewMode) -or ($null -ne $blockUserSettingID)
if (-not $hasItems -and -not $hasBlockMeta) { return }
- X "$indent"
+ X "$indent<$wrapTag>"
foreach ($ca in $items) {
X "$indent`t"
if ($ca.use -eq $false) { X "$indent`t`tfalse" }
@@ -1915,7 +1915,12 @@ function Emit-ConditionalAppearance {
X "$indent`t`t"
}
if ($ca.presentation) {
- if ($ca.presentation -is [hashtable] -or $ca.presentation -is [System.Collections.IDictionary] -or $ca.presentation -is [PSCustomObject]) { Emit-MLText -tag "dcsset:presentation" -text $ca.presentation -indent "$indent`t`t" }
+ if ($ca.presentation -is [hashtable] -or $ca.presentation -is [System.Collections.IDictionary] -or $ca.presentation -is [PSCustomObject]) {
+ # Мультиязык → LocalStringType (платформа объявляет тип у локализованного presentation)
+ X "$indent`t`t"
+ Emit-MLItems -val $ca.presentation -indent "$indent`t`t`t"
+ X "$indent`t`t"
+ }
else { X "$indent`t`t$(Esc-Xml "$($ca.presentation)")" }
}
if ($ca.viewMode) { X "$indent`t`t$(Esc-Xml "$($ca.viewMode)")" }
@@ -1942,7 +1947,7 @@ function Emit-ConditionalAppearance {
$uid = if ("$blockUserSettingID" -eq 'auto') { New-Guid-String } else { "$blockUserSettingID" }
X "$indent`t$(Esc-Xml $uid)"
}
- X "$indent"
+ X "$indent$wrapTag>"
}
# --- 5. Type emitter ---
@@ -4674,10 +4679,18 @@ function Emit-DLParameters {
}
function Emit-Attributes {
- param($attrs, [string]$indent)
+ param($attrs, [string]$indent, $conditionalAppearance = $null)
+ $hasCA = $conditionalAppearance -and @($conditionalAppearance).Count -gt 0
# Платформа ВСЕГДА эмитит (100% корпуса; 162 формы — пустой ).
- if (-not $attrs -or $attrs.Count -eq 0) { X "$indent"; return }
+ if ((-not $attrs -or $attrs.Count -eq 0) -and -not $hasCA) { X "$indent"; return }
+ if (-not $attrs -or $attrs.Count -eq 0) {
+ # Нет реквизитов, но есть условное оформление (последний child )
+ X "$indent"
+ Emit-ConditionalAppearance -items $conditionalAppearance -indent "$indent`t" -wrapTag 'ConditionalAppearance'
+ X "$indent"
+ return
+ }
X "$indent"
$seenAttrs = @{}
@@ -4890,6 +4903,8 @@ function Emit-Attributes {
X "$indent`t"
}
+ # Условное оформление формы — последний child (та же DCS-грамматика, что settings CA)
+ Emit-ConditionalAppearance -items $conditionalAppearance -indent "$indent`t" -wrapTag 'ConditionalAppearance'
X "$indent"
}
@@ -5465,7 +5480,7 @@ if ($def.elements -and $def.elements.Count -gt 0) {
}
# 12g. Attributes
-Emit-Attributes -attrs $def.attributes -indent "`t"
+Emit-Attributes -attrs $def.attributes -indent "`t" -conditionalAppearance $def.conditionalAppearance
# 12h. Parameters
Emit-Parameters -params $def.parameters -indent "`t"
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index f9ba8b66..1297b97b 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.100 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.101 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -1640,12 +1640,12 @@ def emit_appearance_value(lines, key, val, indent):
lines.append(f'{indent}')
-def emit_conditional_appearance(lines, items, indent, block_view_mode=None, block_user_setting_id=None):
+def emit_conditional_appearance(lines, items, indent, block_view_mode=None, block_user_setting_id=None, wrap_tag='dcsset:conditionalAppearance'):
has_items = bool(items) and len(items) > 0
has_block_meta = (block_view_mode is not None) or (block_user_setting_id is not None)
if not has_items and not has_block_meta:
return
- lines.append(f'{indent}')
+ lines.append(f'{indent}<{wrap_tag}>')
for ca in (items or []):
lines.append(f'{indent}\t')
if ca.get('use') is False:
@@ -1670,7 +1670,10 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc
lines.append(f'{indent}\t\t')
if ca.get('presentation'):
if isinstance(ca['presentation'], dict):
- emit_mltext(lines, f'{indent}\t\t', 'dcsset:presentation', ca['presentation'])
+ # Мультиязык → LocalStringType (платформа объявляет тип у локализованного presentation)
+ lines.append(f'{indent}\t\t')
+ emit_ml_items(lines, f'{indent}\t\t\t', ca['presentation'])
+ lines.append(f'{indent}\t\t')
else:
lines.append(f'{indent}\t\t{esc_xml(str(ca["presentation"]))}')
if ca.get('viewMode'):
@@ -1695,7 +1698,7 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc
if block_user_setting_id is not None:
uid = new_uuid() if str(block_user_setting_id) == 'auto' else str(block_user_setting_id)
lines.append(f'{indent}\t{esc_xml(uid)}')
- lines.append(f'{indent}')
+ lines.append(f'{indent}{wrap_tag}>')
def write_utf8_bom(path, content):
@@ -4388,11 +4391,18 @@ def emit_dl_parameters(lines, params, indent):
emit_dl_parameter(lines, p, parsed, indent)
-def emit_attributes(lines, attrs, indent):
+def emit_attributes(lines, attrs, indent, conditional_appearance=None):
+ has_ca = bool(conditional_appearance) and len(conditional_appearance) > 0
# Платформа ВСЕГДА эмитит (100% корпуса; 162 формы — пустой ).
- if not attrs or len(attrs) == 0:
+ if (not attrs or len(attrs) == 0) and not has_ca:
lines.append(f'{indent}')
return
+ if not attrs or len(attrs) == 0:
+ # Нет реквизитов, но есть условное оформление (последний child )
+ lines.append(f'{indent}')
+ emit_conditional_appearance(lines, conditional_appearance, f'{indent}\t', wrap_tag='ConditionalAppearance')
+ lines.append(f'{indent}')
+ return
lines.append(f'{indent}')
seen_attrs = set()
@@ -4595,6 +4605,8 @@ def emit_attributes(lines, attrs, indent):
lines.append(f'{inner}')
lines.append(f'{indent}\t')
+ # Условное оформление формы — последний child (та же DCS-грамматика, что settings CA)
+ emit_conditional_appearance(lines, conditional_appearance, f'{indent}\t', wrap_tag='ConditionalAppearance')
lines.append(f'{indent}')
@@ -5297,7 +5309,7 @@ def main():
lines.append('\t')
# Attributes
- emit_attributes(lines, defn.get('attributes'), '\t')
+ emit_attributes(lines, defn.get('attributes'), '\t', conditional_appearance=defn.get('conditionalAppearance'))
# Parameters
emit_parameters(lines, defn.get('parameters'), '\t')
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index 7bc88f74..87ededa7 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.76 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.77 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -145,7 +145,8 @@ function Fail-Ring3 {
[Console]::Error.WriteLine("Для точечной работы с этой формой используй /form-edit.")
exit 3
}
-foreach ($el in $xmlDoc.SelectNodes("//*[local-name()='ConditionalAppearance']")) { Fail-Ring3 -kind "ConditionalAppearance" -loc "form/ConditionalAppearance" }
+# ConditionalAppearance со scope (привязка к области) пока не воспроизводим — fail-ring3 только в этом случае.
+foreach ($el in $xmlDoc.SelectNodes("//*[local-name()='ConditionalAppearance']/*[local-name()='item']/*[local-name()='scope'][node()]")) { Fail-Ring3 -kind "ConditionalAppearance со scope" -loc "form/ConditionalAppearance/item/scope" }
# --- 1c. Compact JSON serializer (созвучно skd-decompile: 2-проб. indent, inline в пределах lineLimit) ---
function Convert-StringToJsonLiteral {
@@ -2099,6 +2100,16 @@ if ($attrsNode) {
if ($attrs.Count -gt 0) { $dsl['attributes'] = @($attrs) }
}
+# conditionalAppearance формы ( — последний child ;
+# та же DCS-грамматика, что settings.conditionalAppearance → переиспользуем Build-ConditionalAppearance)
+if ($attrsNode) {
+ $caNode = $attrsNode.SelectSingleNode("lf:ConditionalAppearance", $ns)
+ if ($caNode) {
+ $ca = Build-ConditionalAppearance -caNode $caNode -loc "form/conditionalAppearance"
+ if (@($ca).Count -gt 0) { $dsl['conditionalAppearance'] = @($ca) }
+ }
+}
+
# parameters
$parsNode = $root.SelectSingleNode("lf:Parameters", $ns)
if ($parsNode) {
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index 020c568f..88e81316 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -985,6 +985,31 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
---
+## 7c. conditionalAppearance — условное оформление формы
+
+Форменный ключ `conditionalAppearance` (XML `` — последний child ``).
+**Грамматика идентична `settings.conditionalAppearance` дин-списка** (DCS — см. §«order/filter/conditionalAppearance»):
+массив объектов `{ selection?, filter?, appearance?, presentation?, viewMode?, userSettingID?, use? }`.
+
+```json
+"conditionalAppearance": [
+ { "selection": ["ОбычноеПоле"], "filter": ["ЧисловоеПоле > 100"],
+ "appearance": { "ЦветФона": "style:FormBackColor" },
+ "presentation": { "ru": "Подсветка", "en": "Highlight" } }
+]
+```
+
+- `selection` — массив имён форматируемых полей (``).
+- `filter` — условие (filter-shorthand, как в СКД).
+- `appearance` — словарь «параметр-DCS: значение» (рус. verbatim: `ЦветТекста`/`ЦветФона`/`Шрифт`/…). Цвет → `v8ui:Color`.
+- `presentation` — мультиязык → `xsi:type="v8:LocalStringType"`.
+
+Декомпилятор/компилятор переиспользуют `Build-ConditionalAppearance`/`Emit-ConditionalAppearance` настроек списка
+(отличие — тег-обёртка `ConditionalAppearance` без `dcsset:` и без блок-мета). `scope` (привязка к области) в формах
+не встречается; форма со `scope` → fail-ring3.
+
+---
+
## 8. Система типов (shorthand)
### Примитивные типы
diff --git a/tests/skills/cases/form-compile/input-fields.json b/tests/skills/cases/form-compile/input-fields.json
index 9c574529..ef84b43b 100644
--- a/tests/skills/cases/form-compile/input-fields.json
+++ b/tests/skills/cases/form-compile/input-fields.json
@@ -67,6 +67,10 @@
{ "name": "ФлагЯвный", "type": "boolean" },
{ "name": "ФлагТумблер", "type": "boolean" },
{ "name": "ЧисловоеПоле", "type": "number(10,2)" }
+ ],
+ "conditionalAppearance": [
+ { "selection": ["ОбычноеПоле"], "filter": ["ЧисловоеПоле > 100"], "appearance": { "ЦветФона": "style:FormBackColor" },
+ "presentation": { "ru": "Подсветка", "en": "Highlight" } }
]
}
}
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 2daf4900..3092635c 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
@@ -592,5 +592,37 @@
+
+
+
+
+ ОбычноеПоле
+
+
+
+
+ ЧисловоеПоле
+ Greater
+ 100
+
+
+
+
+ ЦветФона
+ style:FormBackColor
+
+
+
+
+ ru
+ Подсветка
+
+
+ en
+ Highlight
+
+
+
+