diff --git a/.claude/skills/skd-compile/scripts/skd-compile.ps1 b/.claude/skills/skd-compile/scripts/skd-compile.ps1 index a6dba987..9e7900ac 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.ps1 +++ b/.claude/skills/skd-compile/scripts/skd-compile.ps1 @@ -1,4 +1,4 @@ -# skd-compile v1.94 — Compile 1C DCS from JSON +# skd-compile v1.95 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -2321,12 +2321,34 @@ function Emit-AppearanceValue { X "$indent" - # Распознаём wrapper {use: false, value: ...} (необходимо отличать от multilang dict). + # Helper для проверки property/key на PSCustomObject/IDictionary + function _HasKey { param($o, [string]$k) + if ($o -is [PSCustomObject]) { return [bool]$o.PSObject.Properties[$k] } + if ($o -is [System.Collections.IDictionary]) { return $o.Contains($k) } + return $false + } + function _Get { param($o, [string]$k) + if ($o -is [PSCustomObject]) { return $o.$k } + if ($o -is [System.Collections.IDictionary]) { return $o[$k] } + return $null + } + + # Распознаём wrapper {value:..., use?:false, items?:{}}. + # Top-level Line-value хранится плоско ({@type:Line, width, gap, style, use?, items?}) — + # отличаем от wrapper по наличию @type на самом val. + $isTopLevelLine = (_HasKey $val '@type') -and ("$(_Get $val '@type')" -eq 'Line') $useWrapper = $false $innerVal = $val - if ($val -is [PSCustomObject] -and $val.PSObject.Properties['use'] -and $val.use -eq $false -and $val.PSObject.Properties['value']) { - $useWrapper = $true - $innerVal = $val.value + $nestedItems = $null + if ($isTopLevelLine) { + # items/use лежат рядом с @type + if ((_HasKey $val 'use') -and ((_Get $val 'use') -eq $false)) { $useWrapper = $true } + if (_HasKey $val 'items') { $nestedItems = (_Get $val 'items') } + } elseif ((_HasKey $val 'value') -and (($val -is [PSCustomObject]) -or ($val -is [System.Collections.IDictionary]))) { + # Обычный wrapper {value, use?, items?} + $innerVal = (_Get $val 'value') + if ((_HasKey $val 'use') -and ((_Get $val 'use') -eq $false)) { $useWrapper = $true } + if (_HasKey $val 'items') { $nestedItems = (_Get $val 'items') } } if ($useWrapper) { X "$indent`tfalse" } @@ -2340,8 +2362,18 @@ function Emit-AppearanceValue { } elseif ($innerVal -is [System.Collections.IDictionary]) { if ($innerVal.Contains('@type') -and "$($innerVal['@type'])" -eq 'Font') { $isFontDict = $true } } + # Line dict ({@type: "Line", width, gap, style}) → ... + $isLineDict = $false + if (_HasKey $innerVal '@type') { $isLineDict = ("$(_Get $innerVal '@type')" -eq 'Line') } $isDict = ($innerVal -is [hashtable]) -or ($innerVal -is [System.Collections.IDictionary]) -or ($innerVal -is [PSCustomObject]) - if ($isFontDict) { + if ($isLineDict) { + $lw = if (_HasKey $innerVal 'width') { _Get $innerVal 'width' } else { 0 } + $lg = if (_HasKey $innerVal 'gap') { if ((_Get $innerVal 'gap')) { 'true' } else { 'false' } } else { 'false' } + $ls = if (_HasKey $innerVal 'style') { "$(_Get $innerVal 'style')" } else { 'None' } + X "$indent`t" + X "$indent`t`t$(Esc-Xml $ls)" + X "$indent`t" + } elseif ($isFontDict) { $attrParts = @() foreach ($attrName in @('ref','faceName','height','bold','italic','underline','strikeout','kind','scale')) { $av = $null @@ -2388,6 +2420,20 @@ function Emit-AppearanceValue { X "$indent`t$(Esc-Xml $actualVal)" } } + # Nested SettingsParameterValue items (например СтильГраницы.Сверху/.Снизу/.Слева/.Справа). + # Эмитим как siblings внутри родительского . + if ($nestedItems) { + $niProps = if ($nestedItems -is [PSCustomObject]) { $nestedItems.PSObject.Properties } else { $null } + if ($niProps) { + foreach ($np in $niProps) { + Emit-AppearanceValue -key $np.Name -val $np.Value -indent "$indent`t" + } + } elseif ($nestedItems -is [System.Collections.IDictionary]) { + foreach ($nk in $nestedItems.Keys) { + Emit-AppearanceValue -key $nk -val $nestedItems[$nk] -indent "$indent`t" + } + } + } X "$indent" } diff --git a/.claude/skills/skd-compile/scripts/skd-compile.py b/.claude/skills/skd-compile/scripts/skd-compile.py index 8e28e911..53bec39d 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.py +++ b/.claude/skills/skd-compile/scripts/skd-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# skd-compile v1.94 — Compile 1C DCS from JSON +# skd-compile v1.95 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -1921,19 +1921,36 @@ def emit_order(lines, items, indent, skip_auto=False, block_view_mode=None, bloc def emit_appearance_value(lines, key, val, indent): lines.append(f'{indent}') - # \u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0451\u043c wrapper {use: false, value: ...} \u2014 \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043e\u0431\u0430 \u043a\u043b\u044e\u0447\u0430. + # Top-level Line \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043f\u043b\u043e\u0441\u043a\u043e ({@type: "Line", width, gap, style, use?, items?}). + # \u041e\u0431\u044b\u0447\u043d\u044b\u0439 wrapper: {value, use?, items?}. + is_top_level_line = isinstance(val, dict) and val.get('@type') == 'Line' use_wrapper = False inner_val = val - if isinstance(val, dict) and 'use' in val and val['use'] is False and 'value' in val: - use_wrapper = True + nested_items = None + if is_top_level_line: + if val.get('use') is False: + use_wrapper = True + nested_items = val.get('items') + elif isinstance(val, dict) and 'value' in val: inner_val = val['value'] + if val.get('use') is False: + use_wrapper = True + nested_items = val.get('items') if use_wrapper: lines.append(f'{indent}\tfalse') lines.append(f'{indent}\t{esc_xml(key)}') + # Line dict ({@type: "Line", width, gap, style}) \u2192 + if isinstance(inner_val, dict) and inner_val.get('@type') == 'Line': + lw = inner_val.get('width', 0) + lg = 'true' if inner_val.get('gap') else 'false' + ls = str(inner_val.get('style', 'None')) + lines.append(f'{indent}\t') + lines.append(f'{indent}\t\t{esc_xml(ls)}') + lines.append(f'{indent}\t') # Font dict ({@type: "Font", ref, faceName, height, bold, ...}) \u2192 - if isinstance(inner_val, dict) and inner_val.get('@type') == 'Font': + elif isinstance(inner_val, dict) and inner_val.get('@type') == 'Font': attr_parts = [] for attr_name in ('ref', 'faceName', 'height', 'bold', 'italic', 'underline', 'strikeout', 'kind', 'scale'): if attr_name in inner_val: @@ -1967,6 +1984,10 @@ def emit_appearance_value(lines, key, val, indent): lines.append(f'{indent}\t{esc_xml(actual_val)}') else: lines.append(f'{indent}\t{esc_xml(actual_val)}') + # Nested SettingsParameterValue items (СтильГраницы.Сверху/.Снизу/.Слева/.Справа). + if nested_items and isinstance(nested_items, dict): + for nk, nv in nested_items.items(): + emit_appearance_value(lines, nk, nv, f'{indent}\t') lines.append(f'{indent}') diff --git a/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 b/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 index c94321ab..9ba8e212 100644 --- a/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 +++ b/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 @@ -1,4 +1,4 @@ -# skd-decompile v0.77 — Decompile 1C DCS Template.xml to JSON DSL (draft) +# skd-decompile v0.78 — Decompile 1C DCS Template.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -1723,7 +1723,36 @@ function Build-Order { return ,$out } -# Build appearance dict from or list +# Прочитать в объект {@type:Line, width, gap, style}. +function Get-LineValue { + param($valNode) + $obj = [ordered]@{ '@type' = 'Line' } + $w = $valNode.GetAttribute("width") + $g = $valNode.GetAttribute("gap") + if ($w -ne '') { $obj['width'] = if ($w -match '^-?\d+$') { [int]$w } else { $w } } + if ($g -ne '') { $obj['gap'] = ($g -eq 'true') } + $styleNode = $valNode.SelectSingleNode("v8ui:style", $ns) + if ($styleNode) { $obj['style'] = $styleNode.InnerText } + return $obj +} + +# Прочитать в JSON-значение: Font/Line/multilang/raw text. +# Возвращает то значение которое идёт в "value" slot. +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 'Font') { return (Get-FontValue $valNode) } + if ($vt -eq 'Line') { return (Get-LineValue $valNode) } + return $valNode.InnerText +} + +# Build appearance dict from or list. +# Поддерживает Line-значения (граница) и nested SettingsParameterValue items +# (например СтильГраницы.Сверху). DSL form B (см. docs/skd-dsl-spec.md): +# - top-level Line: { "@type": "Line", "width", "gap", "style", "use"?, "items"? } +# - nested item: { "value": <значение>, "use"?: false } function Get-SettingsAppearance { param($appNode) if (-not $appNode) { return $null } @@ -1732,18 +1761,32 @@ function Get-SettingsAppearance { $pName = Get-Text $it "dcscor:parameter" $val = $it.SelectSingleNode("dcscor:value", $ns) if (-not $pName -or -not $val) { continue } - $valType = Get-LocalXsiType $val - if ($valType -eq 'LocalStringType') { - $rawVal = Get-MLText $val - } elseif ($valType -eq 'Font') { - $rawVal = Get-FontValue $val - } else { - $rawVal = $val.InnerText - } - # wrapper {use:false, value} как в Get-AppearanceDict + $rawVal = Read-AppearanceValueNode $val $useV = Get-Text $it "dcscor:use" - if ($useV -eq 'false') { - $dict[$pName] = [ordered]@{ value = $rawVal; use = $false } + # Nested dcscor:item внутри этого item — wrap form {value, use?}. + $nestedItems = [ordered]@{} + foreach ($sub in $it.SelectNodes("dcscor:item", $ns)) { + $subName = Get-Text $sub "dcscor:parameter" + $subVal = $sub.SelectSingleNode("dcscor:value", $ns) + if (-not $subName) { continue } + $subRaw = Read-AppearanceValueNode $subVal + $subUse = Get-Text $sub "dcscor:use" + $subEntry = [ordered]@{ value = $subRaw } + if ($subUse -eq 'false') { $subEntry['use'] = $false } + $nestedItems[$subName] = $subEntry + } + # Определяем форму вывода + $valIsLine = ($rawVal -is [System.Collections.IDictionary]) -and $rawVal.Contains('@type') -and ($rawVal['@type'] -eq 'Line') + if ($valIsLine) { + # top-level Line — атрибуты inline + опц. use/items + if ($useV -eq 'false') { $rawVal['use'] = $false } + if ($nestedItems.Count -gt 0) { $rawVal['items'] = $nestedItems } + $dict[$pName] = $rawVal + } elseif (($useV -eq 'false') -or ($nestedItems.Count -gt 0)) { + $wrap = [ordered]@{ value = $rawVal } + if ($useV -eq 'false') { $wrap['use'] = $false } + if ($nestedItems.Count -gt 0) { $wrap['items'] = $nestedItems } + $dict[$pName] = $wrap } else { $dict[$pName] = $rawVal } diff --git a/docs/skd-dsl-spec.md b/docs/skd-dsl-spec.md index d0f7b7a9..d735e67c 100644 --- a/docs/skd-dsl-spec.md +++ b/docs/skd-dsl-spec.md @@ -763,6 +763,31 @@ Wrapper эмитится только при наличии extra-полей; п ``` Все атрибуты исходного XML сохраняются — для bit-perfect. +#### Граница (v8ui:Line) в appearance + +Граница — объект с маркером `@type: "Line"` (атрибуты `width`/`gap` и inner `` сериализуются inline): +```json +"СтильГраницы": { "@type": "Line", "width": 0, "gap": false, "style": "None" } +``` + +Стороны (`СтильГраницы.Сверху/.Снизу/.Слева/.Справа`) — nested SettingsParameterValue, кладутся в `items` (как у outputParameters wrapper): +```json +"СтильГраницы": { + "@type": "Line", "width": 0, "gap": false, "style": "None", + "items": { + "СтильГраницы.Сверху": { + "value": { "@type": "Line", "width": 1, "gap": false, "style": "Solid" }, + "use": false + }, + "СтильГраницы.Снизу": { + "value": { "@type": "Line", "width": 1, "gap": false, "style": "Double" } + } + } +} +``` + +Top-level Line хранится **плоско** (`@type`/`width`/`gap`/`style` + `use?`/`items?` на одном уровне). Nested items используют универсальный wrapper `{ value, use? }` — у `value` тип любой (Line/Font/color/text). Значения `style`: `None`, `Solid`, `Double`, `LargeDashed`, `SmallDashed`, `Dotted` и т.п. (значения `v8ui:SpreadsheetDocumentCellLineType`). + ### dataParameters #### Автогенерация