diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 0c43f1cd..8df7df3b 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.157 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.158 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -4139,7 +4139,8 @@ function Emit-ChoiceList {
if ($item.Contains("valueType")) { $vtRaw = "$($item["valueType"])" }
} elseif ($item.PSObject.Properties["valueType"]) { $vtRaw = "$($item.valueType)" }
- if ($vtRaw) { $norm = @{ XsiType = $vtRaw; Text = "$valRaw" } }
+ if ($vtRaw -eq 'nil') { $norm = @{ XsiType = $null; Text = $null; Nil = $true } }
+ elseif ($vtRaw) { $norm = @{ XsiType = $vtRaw; Text = "$valRaw" } }
else { $norm = Normalize-ChoiceValue -value $valRaw }
# авто-вывод presentation, если не задан
@@ -4158,7 +4159,7 @@ function Emit-ChoiceList {
X "$valIndent0"
X "$valIndent"
Emit-ChoicePresentation -pres $presRaw -indent "$valIndent`t"
- X "$valIndent`t$(Get-ChoiceValueTag $norm)"
+ X "$valIndent`t$(if ($norm.Nil) { '' } else { Get-ChoiceValueTag $norm })"
X "$valIndent"
X "$itemIndent"
}
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index 5cd24f84..6a5033d2 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.157 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.158 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -2327,17 +2327,19 @@ def emit_choice_list(lines, el, indent):
# valueType: явный xsi:type значения (системное перечисление ent:*, иной не-примитив) —
# переопределяет авто-детект (normalize_choice_value вывела бы xs:string).
vt_raw = item.get('valueType')
- if vt_raw:
+ if vt_raw == 'nil':
+ norm = {'xsi_type': None, 'text': None, 'nil': True}
+ elif vt_raw:
norm = {'xsi_type': str(vt_raw), 'text': '' if val_raw is None else str(val_raw)}
else:
norm = normalize_choice_value(val_raw)
if not has_pres:
- if norm['xsi_type'] == 'xr:DesignTimeRef':
+ if norm.get('xsi_type') == 'xr:DesignTimeRef':
tail = norm['text'].split('.')[-1]
pres_raw = title_from_name(tail)
else:
- pres_raw = norm['text']
+ pres_raw = norm.get('text')
lines.append(f'{item_indent}')
val_indent = f'{item_indent}\t'
@@ -2345,7 +2347,8 @@ def emit_choice_list(lines, el, indent):
lines.append(f'{val_indent}0')
lines.append(f'{val_indent}')
emit_choice_presentation(lines, pres_raw, f'{val_indent}\t')
- lines.append(f'{val_indent}\t{choice_value_tag(norm)}')
+ val_tag = '' if norm.get('nil') else choice_value_tag(norm)
+ lines.append(f'{val_indent}\t{val_tag}')
lines.append(f'{val_indent}')
lines.append(f'{item_indent}')
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 96f59f90..b3df467c 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.132 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.133 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1677,12 +1677,20 @@ function Decompile-ChoiceList {
$presNode = $it.SelectSingleNode("xr:Value/lf:Presentation", $ns)
$ci = [ordered]@{}
if ($valNode) {
- $xsiType = $valNode.GetAttribute("type", $NS_XSI)
- $ci['value'] = Convert-TypedValue $valNode.InnerText $xsiType
- # Системное перечисление (ent:*) / иной не-примитивный, не-DesignTimeRef тип → сохраняем
- # valueType (Normalize-ChoiceValue в компиляторе вывела бы xs:string и потеряла тип).
- if ($xsiType -and $xsiType -notmatch '^xs:(string|decimal|boolean|dateTime)$' -and $xsiType -ne 'xr:DesignTimeRef') {
- $ci['valueType'] = $xsiType
+ if ($valNode.GetAttribute("nil", $NS_XSI) -eq 'true') {
+ # nil-значение элемента choiceList — компилятор эмитит
+ # (иначе Convert-TypedValue вернул бы "" → typed-empty xs:string).
+ $ci['valueType'] = 'nil'
+ } else {
+ $xsiType = $valNode.GetAttribute("type", $NS_XSI)
+ $ci['value'] = Convert-TypedValue $valNode.InnerText $xsiType
+ # Системное перечисление (ent:*) / иной не-примитивный тип → сохраняем valueType
+ # (Normalize-ChoiceValue вывела бы xs:string). DesignTimeRef обычно авто-детектится
+ # компилятором по named-ref (Enum.X.Y), НО raw-ссылка по GUID (GUID.GUID) — нет → сохраняем.
+ if ($xsiType -and $xsiType -notmatch '^xs:(string|decimal|boolean|dateTime)$' -and `
+ ($xsiType -ne 'xr:DesignTimeRef' -or "$($valNode.InnerText)" -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-')) {
+ $ci['valueType'] = $xsiType
+ }
}
}
# Presentation: непустой → текст/мультиязык; пустой → "" — суппресс-маркер,
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index fe0f63e2..3f05232d 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -505,7 +505,7 @@ companion-панели с собственным контентом. Оба не
| Свойство | Тип | Описание |
|----------|-----|----------|
| `value` | string/number/bool | Значение варианта. Для перечисления — `"Enum.ИмяТипа.EnumValue.ИмяЗначения"` (xsi:type автоматически: `xr:DesignTimeRef` / `xs:string` / `xs:decimal` / `xs:boolean`) |
-| `valueType` | string | Явный xsi:type значения, переопределяет авто-детект. Нужен для **системных перечислений** (`ent:` namespace: `ent:AccountType`=ВидСчёта, `ent:AccumulationRecordType`, `ent:HorizontalAlignment`, … — см. «Системные перечисления» в палитре типов) и иных не-примитивных типов. Напр. `{ "value": "Active", "valueType": "ent:AccountType", "presentation": "Активный" }` |
+| `valueType` | string | Явный xsi:type значения, переопределяет авто-детект. Нужен для **системных перечислений** (`ent:` namespace: `ent:AccountType`=ВидСчёта, `ent:AccumulationRecordType`, `ent:HorizontalAlignment`, … — см. «Системные перечисления» в палитре типов) и иных не-примитивных типов. Напр. `{ "value": "Active", "valueType": "ent:AccountType", "presentation": "Активный" }`. Спец-маркеры (раундтрип): **`"nil"`** → `` (пустое значение варианта без типа); **`"xr:DesignTimeRef"`** при значении-GUID (`GUID.GUID` — ссылка по метаданным-GUID, не по имени; named-ссылки `Enum.X.Y` авто-детектятся без ключа) |
| `presentation` | string или object | Текст рядом с переключателем. Строка → ru; объект `{ru, en, ...}` → мультиязык. Если не задано — выводится из имени значения |
#### label — LabelDecoration