From aaeb752cf1520c3465b83d7b6e1e719257bc746a Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 15 Aug 2026 11:46:50 +0300 Subject: [PATCH] =?UTF-8?q?refactor(mxl-compile,mxl-decompile):=20=D0=BA?= =?UTF-8?q?=D0=BB=D1=8E=D1=87=20=D1=8F=D1=87=D0=B5=D0=B9=D0=BA=D0=B8=20con?= =?UTF-8?q?trol=20=E2=86=92=20controlType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Тег ячейки и тег формата в выгрузке — разные вещи: первый несёт сериализованные настройки элемента управления, второй GUID его вида. Ключ DSL описывал второй, а назывался как первый, и при поддержке настроек отображение стало бы перекрёстным. Свести их в один ключ нельзя: живёт в палитре и разделяется ячейками с одинаковым оформлением, а настройки принадлежат конкретной ячейке. Синоним не заводим — он занял бы ровно то имя, которое освобождается. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/mxl-compile/SKILL.md | 4 ++-- .claude/skills/mxl-compile/reference/dsl-spec.md | 14 +++++++------- .claude/skills/mxl-compile/scripts/mxl-compile.ps1 | 8 ++++---- .claude/skills/mxl-compile/scripts/mxl-compile.py | 8 ++++---- .../skills/mxl-decompile/scripts/mxl-decompile.ps1 | 4 ++-- .../skills/mxl-decompile/scripts/mxl-decompile.py | 4 ++-- docs/mxl-dsl-spec.md | 14 +++++++------- .../cases/mxl-compile/error-value-bad-control.json | 4 ++-- .../error-value-control-without-type.json | 6 +++--- tests/skills/cases/mxl-compile/value-cells.json | 2 +- tests/skills/cases/mxl-compile/value-dedup.json | 2 +- tests/skills/cases/mxl-compile/value-states.json | 4 ++-- .../cases/mxl-decompile/roundtrip-value-cells.json | 4 ++-- .../snapshots/roundtrip-value-cells/back.json | 2 +- 14 files changed, 40 insertions(+), 40 deletions(-) diff --git a/.claude/skills/mxl-compile/SKILL.md b/.claude/skills/mxl-compile/SKILL.md index 3c2a6d8a..c95ef155 100644 --- a/.claude/skills/mxl-compile/SKILL.md +++ b/.claude/skills/mxl-compile/SKILL.md @@ -59,7 +59,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J styles: { name: { font, horizontalAlignment, verticalAlignment, textPlacement, backColor, textColor, border, borderColor, format, hidden } }, areas: [{ name, columnSet, rows: [{ height, hidden, rowStyle, cells: [ - { col, span, rowspan, style, param, detail, text, template, valueType, control } + { col, span, rowspan, style, param, detail, text, template, valueType, controlType } ]}]}], namedAreas: [{ name, rows, cols }], columnSets: { name: { columns, columnWidths, columnStyles } } @@ -80,7 +80,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J - `col` — 1-based позиция колонки - `rowspan` — объединение строк вниз (rowStyle учитывает занятые ячейки) - Содержимое ячейки задаётся одним из ключей: `param` — параметр заполнения, `text` — статический текст, `template` — текст со вставками `[Параметр]` -- `valueType` делает ячейку полем ввода (`"Number(15,3,nonneg)"`, `"String(10)"`, `"CatalogRef.Валюты"`, составной через ` + `); текста в такой ячейке быть не может, а `control: "checkbox"` меняет поле ввода на флажок +- `valueType` делает ячейку полем ввода (`"Number(15,3,nonneg)"`, `"String(10)"`, `"CatalogRef.Валюты"`, составной через ` + `); текста в такой ячейке быть не может, а `controlType: "checkbox"` меняет поле ввода на флажок Двухуровневая шапка массивами: ```json diff --git a/.claude/skills/mxl-compile/reference/dsl-spec.md b/.claude/skills/mxl-compile/reference/dsl-spec.md index 97dc4554..45a5161d 100644 --- a/.claude/skills/mxl-compile/reference/dsl-spec.md +++ b/.claude/skills/mxl-compile/reference/dsl-spec.md @@ -152,8 +152,8 @@ | `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` | Объект-элемент трактуется по его ключам: если среди них есть ключ ячейки (`span`, `rowspan`, -`style`, `param`, `detail`, `text`, `template`) — объект описывает свойства ячейки. Иначе он -целиком считается её текстом, а его ключи — идентификаторами языков. +`style`, `param`, `detail`, `text`, `template`, `valueType`, `controlType`) — объект описывает +свойства ячейки. Иначе он целиком считается её текстом, а его ключи — идентификаторами языков. ```json "rows": [ @@ -183,7 +183,7 @@ | `text` | нет | — | Статический текст. Строка или объект `{ ru, en }` — см. ниже | | `template` | нет | — | Шаблонный текст с `[Параметр]`. Строка или объект, как `text` | | `valueType` | нет | — | Тип значения: ячейка становится полем ввода (см. ниже) | -| `control` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` | +| `controlType` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` | ### Содержимое ячейки @@ -222,7 +222,7 @@ ```json { "col": 1, "valueType": "Number(15,3,nonneg)" } { "col": 2, "valueType": "String(10)" } -{ "col": 3, "valueType": "Boolean", "control": "checkbox" } +{ "col": 3, "valueType": "Boolean", "controlType": "checkbox" } { "col": 4, "valueType": "CatalogRef.Валюты" } { "col": 5, "valueType": "Boolean + String + Date + CatalogRef.Валюты" } { "col": 6, "valueType": "AnyRef" } @@ -248,9 +248,9 @@ Пустой тип `"valueType": ""` — «ячейка содержит значение», но тип не ограничен. -`control` нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, включая -`Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — форма -раундтрипа, для ручного авторинга не нужна. +`controlType` нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, +включая `Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — +форма раундтрипа, для ручного авторинга не нужна. ## `rowStyle` — оформление строки diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 index 233a00b8..16a3d0fc 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 @@ -848,10 +848,10 @@ function Get-CellValueProps { param($cell, [string]$where) $props = @{} $vt = $cell.valueType - $ctl = $cell.control + $ctl = $cell.controlType if ($null -eq $vt) { if ($null -ne $ctl -and "$ctl" -ne '') { - [Console]::Error.WriteLine("Cell 'control' requires 'valueType' ($where)") + [Console]::Error.WriteLine("Cell 'controlType' requires 'valueType' ($where)") exit 1 } return $props @@ -879,7 +879,7 @@ function Get-CellValueProps { # Неизвестный элемент управления сохраняем как есть — терять его нельзя. $props['controlType'] = $name } else { - [Console]::Error.WriteLine("Unknown 'control' value `"$ctl`" ($where). Allowed: input, checkbox, none or a GUID") + [Console]::Error.WriteLine("Unknown 'controlType' value `"$ctl`" ($where). Allowed: input, checkbox, none or a GUID") exit 1 } return $props @@ -1140,7 +1140,7 @@ function Set-CellProp { function Test-CellObject { param($el) $cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template', - 'valueType', 'control') + 'valueType', 'controlType') foreach ($p in $el.PSObject.Properties) { if ($cellKeys -contains $p.Name) { return $true } } diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.py b/.claude/skills/mxl-compile/scripts/mxl-compile.py index e1a6b697..449522ee 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.py +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.py @@ -650,10 +650,10 @@ def cell_value_props(cell, where): состав палитры разойдётся и все ссылки сдвинутся.""" props = {} vt = cell.get('valueType') - ctl = cell.get('control') + ctl = cell.get('controlType') if vt is None: if ctl is not None and str(ctl) != '': - print(f"Cell 'control' requires 'valueType' ({where})", file=sys.stderr) + print(f"Cell 'controlType' requires 'valueType' ({where})", file=sys.stderr) sys.exit(1) return props # Ячейка, содержащая значение, текста не несёт: платформа этого не допускает, и в корпусе @@ -678,7 +678,7 @@ def cell_value_props(cell, where): # Неизвестный элемент управления сохраняем как есть — терять его нельзя. props['controlType'] = name else: - print(f'Unknown \'control\' value "{ctl}" ({where}).' + print(f'Unknown \'controlType\' value "{ctl}" ({where}).' f' Allowed: input, checkbox, none or a GUID', file=sys.stderr) sys.exit(1) return props @@ -1154,7 +1154,7 @@ def main(): ключей есть ключ схемы ячейки, во втором ключи — идентификаторы языков. Пересечений нет: в корпусе это ru, en, ru1, Русский.""" cell_keys = ('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template', - 'valueType', 'control') + 'valueType', 'controlType') return any(k in el for k in cell_keys) def expand_shorthand_row(row, area_name, row_idx, open_by_col, max_cols): diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 index 900a6358..33f6a5f1 100644 --- a/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 +++ b/.claude/skills/mxl-decompile/scripts/mxl-decompile.ps1 @@ -1125,13 +1125,13 @@ foreach ($area in $blocks) { # Формат без вовсе — так пишут машинно-сгенерированные макеты # регламентированной отчётности; компилятор такое повторит только по # явному указанию. - $dslCell["control"] = "none" + $dslCell["controlType"] = "none" } else { $ctl = "$($cellFmt.Props['controlType'])" $name = if ($valueControlNames.ContainsKey($ctl.ToLowerInvariant())) { $valueControlNames[$ctl.ToLowerInvariant()] } else { $ctl } # input — умолчание компилятора, писать его значит шуметь на 63 629 # форматах корпуса. - if ($name -cne 'input') { $dslCell["control"] = $name } + if ($name -cne 'input') { $dslCell["controlType"] = $name } } } diff --git a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py index 3dd40309..e59dc9d4 100644 --- a/.claude/skills/mxl-decompile/scripts/mxl-decompile.py +++ b/.claude/skills/mxl-decompile/scripts/mxl-decompile.py @@ -1119,13 +1119,13 @@ def main(): # Формат без вовсе — так пишут машинно-сгенерированные # макеты регламентированной отчётности; компилятор такое повторит # только по явному указанию. - dsl_cell["control"] = "none" + dsl_cell["controlType"] = "none" else: name = VALUE_CONTROL_NAMES.get(ctl.lower(), ctl) # input — умолчание компилятора, писать его значит шуметь на 63 629 # форматах корпуса. if name != "input": - dsl_cell["control"] = name + dsl_cell["controlType"] = name # Content fill_type = cell_fmt["FillType"] if cell_fmt else "" diff --git a/docs/mxl-dsl-spec.md b/docs/mxl-dsl-spec.md index a5784818..c0bceafb 100644 --- a/docs/mxl-dsl-spec.md +++ b/docs/mxl-dsl-spec.md @@ -152,8 +152,8 @@ | `{ ... }` | Обычная ячейка **без** `col`; нужна для `style`, `detail`, `template` | Объект-элемент трактуется по его ключам: если среди них есть ключ ячейки (`span`, `rowspan`, -`style`, `param`, `detail`, `text`, `template`) — объект описывает свойства ячейки. Иначе он -целиком считается её текстом, а его ключи — идентификаторами языков. +`style`, `param`, `detail`, `text`, `template`, `valueType`, `controlType`) — объект описывает +свойства ячейки. Иначе он целиком считается её текстом, а его ключи — идентификаторами языков. ```json "rows": [ @@ -183,7 +183,7 @@ | `text` | нет | — | Статический текст. Строка или объект `{ ru, en }` — см. ниже | | `template` | нет | — | Шаблонный текст с `[Параметр]`. Строка или объект, как `text` | | `valueType` | нет | — | Тип значения: ячейка становится полем ввода (см. ниже) | -| `control` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` | +| `controlType` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` | ### Содержимое ячейки @@ -222,7 +222,7 @@ ```json { "col": 1, "valueType": "Number(15,3,nonneg)" } { "col": 2, "valueType": "String(10)" } -{ "col": 3, "valueType": "Boolean", "control": "checkbox" } +{ "col": 3, "valueType": "Boolean", "controlType": "checkbox" } { "col": 4, "valueType": "CatalogRef.Валюты" } { "col": 5, "valueType": "Boolean + String + Date + CatalogRef.Валюты" } { "col": 6, "valueType": "AnyRef" } @@ -248,9 +248,9 @@ Пустой тип `"valueType": ""` — «ячейка содержит значение», но тип не ограничен. -`control` нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, включая -`Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — форма -раундтрипа, для ручного авторинга не нужна. +`controlType` нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, +включая `Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — +форма раундтрипа, для ручного авторинга не нужна. ## `rowStyle` — оформление строки diff --git a/tests/skills/cases/mxl-compile/error-value-bad-control.json b/tests/skills/cases/mxl-compile/error-value-bad-control.json index 7527c8b0..8e025040 100644 --- a/tests/skills/cases/mxl-compile/error-value-bad-control.json +++ b/tests/skills/cases/mxl-compile/error-value-bad-control.json @@ -5,10 +5,10 @@ "areas": [ { "name": "Ввод", - "rows": [{ "cells": [{ "col": 1, "valueType": "Boolean", "control": "кнопка" }] }] + "rows": [{ "cells": [{ "col": 1, "valueType": "Boolean", "controlType": "кнопка" }] }] } ] }, "params": { "outputPath": "Template.xml" }, - "expectError": "Unknown 'control' value \"кнопка\" (area \"Ввод\"). Allowed: input, checkbox, none or a GUID" + "expectError": "Unknown 'controlType' value \"кнопка\" (area \"Ввод\"). Allowed: input, checkbox, none or a GUID" } diff --git a/tests/skills/cases/mxl-compile/error-value-control-without-type.json b/tests/skills/cases/mxl-compile/error-value-control-without-type.json index 59f584c7..978a0105 100644 --- a/tests/skills/cases/mxl-compile/error-value-control-without-type.json +++ b/tests/skills/cases/mxl-compile/error-value-control-without-type.json @@ -1,14 +1,14 @@ { - "name": "Ошибка: control без valueType", + "name": "Ошибка: controlType без valueType", "input": { "columns": 1, "areas": [ { "name": "Ввод", - "rows": [{ "cells": [{ "col": 1, "control": "checkbox" }] }] + "rows": [{ "cells": [{ "col": 1, "controlType": "checkbox" }] }] } ] }, "params": { "outputPath": "Template.xml" }, - "expectError": "Cell 'control' requires 'valueType' (area \"Ввод\")" + "expectError": "Cell 'controlType' requires 'valueType' (area \"Ввод\")" } diff --git a/tests/skills/cases/mxl-compile/value-cells.json b/tests/skills/cases/mxl-compile/value-cells.json index 3ef600d0..2eb5eb8b 100644 --- a/tests/skills/cases/mxl-compile/value-cells.json +++ b/tests/skills/cases/mxl-compile/value-cells.json @@ -13,7 +13,7 @@ ]}, { "cells": [ { "col": 1, "valueType": "Boolean" }, - { "col": 2, "valueType": "Boolean", "control": "checkbox" } + { "col": 2, "valueType": "Boolean", "controlType": "checkbox" } ]}, { "cells": [ { "col": 1, "valueType": "String(10)" }, diff --git a/tests/skills/cases/mxl-compile/value-dedup.json b/tests/skills/cases/mxl-compile/value-dedup.json index 587caf02..9d0b80a4 100644 --- a/tests/skills/cases/mxl-compile/value-dedup.json +++ b/tests/skills/cases/mxl-compile/value-dedup.json @@ -12,7 +12,7 @@ { "valueType": "Number(15,3,any)" }, { "valueType": "Строка(10)" }, { "valueType": "String(10,variable)" }, - { "valueType": "Булево", "control": "Поле Флажка" } + { "valueType": "Булево", "controlType": "Поле Флажка" } ] ] } diff --git a/tests/skills/cases/mxl-compile/value-states.json b/tests/skills/cases/mxl-compile/value-states.json index 93a05cb0..daf31214 100644 --- a/tests/skills/cases/mxl-compile/value-states.json +++ b/tests/skills/cases/mxl-compile/value-states.json @@ -9,8 +9,8 @@ "rows": [ { "cells": [ { "col": 1, "valueType": "" }, - { "col": 2, "valueType": "Number(15,3)", "control": "none" }, - { "col": 3, "valueType": "Boolean", "control": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }, + { "col": 2, "valueType": "Number(15,3)", "controlType": "none" }, + { "col": 3, "valueType": "Boolean", "controlType": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }, { "col": 4, "valueType": "String(50)", "style": "bordered" } ]}, { "rowStyle": "bordered", "cells": [ diff --git a/tests/skills/cases/mxl-decompile/roundtrip-value-cells.json b/tests/skills/cases/mxl-decompile/roundtrip-value-cells.json index b2b4e924..ca0dac19 100644 --- a/tests/skills/cases/mxl-decompile/roundtrip-value-cells.json +++ b/tests/skills/cases/mxl-decompile/roundtrip-value-cells.json @@ -12,11 +12,11 @@ { "cells": [ { "col": 1, "valueType": "Number(15,3,nonneg)" }, { "col": 2, "valueType": "String(10,fixed)" }, - { "col": 3, "valueType": "Boolean", "control": "checkbox" } + { "col": 3, "valueType": "Boolean", "controlType": "checkbox" } ]}, { "cells": [ { "col": 1, "valueType": "" }, - { "col": 2, "valueType": "Number(10,2)", "control": "none" }, + { "col": 2, "valueType": "Number(10,2)", "controlType": "none" }, { "col": 3, "valueType": "Boolean + CatalogRef.Валюты + String + DateTime" } ]}, { "cells": [ diff --git a/tests/skills/cases/mxl-decompile/snapshots/roundtrip-value-cells/back.json b/tests/skills/cases/mxl-decompile/snapshots/roundtrip-value-cells/back.json index 6c3abee0..b6ff7543 100644 --- a/tests/skills/cases/mxl-decompile/snapshots/roundtrip-value-cells/back.json +++ b/tests/skills/cases/mxl-decompile/snapshots/roundtrip-value-cells/back.json @@ -6,7 +6,7 @@ "areas": [ { "name": "Ввод", - "rows": [[{ "valueType": "Number(15,3,nonneg)" }, { "valueType": "String(10,fixed)" }, { "valueType": "Boolean", "control": "checkbox" }], [{ "valueType": "" }, { "valueType": "Number(10,2)", "control": "none" }, { "valueType": "Boolean + CatalogRef.Валюты + String + DateTime" }], [{ "valueType": "AnyRef" }, { "valueType": "Number(10,2)", "param": "Сумма", "detail": "Расшифровка" }]] + "rows": [[{ "valueType": "Number(15,3,nonneg)" }, { "valueType": "String(10,fixed)" }, { "valueType": "Boolean", "controlType": "checkbox" }], [{ "valueType": "" }, { "valueType": "Number(10,2)", "controlType": "none" }, { "valueType": "Boolean + CatalogRef.Валюты + String + DateTime" }], [{ "valueType": "AnyRef" }, { "valueType": "Number(10,2)", "param": "Сумма", "detail": "Расшифровка" }]] } ] } \ No newline at end of file