Files
cc-1c-skills/docs/mxl-dsl-spec.md
T
Nick ShirokovandClaude Opus 5 1e06e43e92 feat(mxl-compile,mxl-decompile): многоязычный текст ячейки
Платформа хранит текст ячейки по элементу на язык. Декомпилятор брал ПЕРВЫЙ и терял
остальное, компилятор писал всегда ru захардкоженно. В корпусе ERP текст лежит и под
ru, и под en у 10 730 макетов из 10 924 — то есть терялось у 98%.

Форма взята готовая — конвенция ML-значений из docs/meta-dsl-spec.md §4.4, по которой
уже живут synonym, tooltip и title в метаданных и формах: строка означает русский
текст, объект даёт по надписи на язык в порядке ключей. Новых понятий не вводили,
ключи те же (text и template).

Документный ключ для языков НЕ заводили, и это измерение, а не экономия: блок
languageSettings мы и так эмитим байт в байт как платформа (currentLanguage ru,
defaultLanguage ru, один languageInfo). Отклонения редки — 8 макетов ERP с
currentLanguage en, 3 без него, 157 с объявленной парой ru+en, всего около 1,6%.
Заодно это снимает развилку из разбора PR #62: список языков компилятор дописывать
не должен, платформа его не дописывает.

Эффект на пилоте: категория row[].cell[].tl упала с 47 252 до 12 488 — минус 74%,
самое крупное улучшение кампании. Остаток частично классифицирован: 348 строк — ячейки
с ПУСТЫМ <tl> у оригинала, которых мы не эмитим вовсе; полностью разобрать мешает
обрезка диффа по 400 строк на объект.

Снэпшоты не дрейфанули: кейсы пишут текст строкой, и вывод для неё прежний.

Проверено: тесты 54/54 на обоих рантаймах, verify-snapshots 24/24, вывод портов
совпадает байт в байт и в компиляции, и в декомпиляции.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 20:52:03 +03:00

16 KiB
Raw Blame History

Спецификация MXL DSL — JSON-формат описания табличного документа

Компактный JSON-формат для описания макетов табличных документов 1С (SpreadsheetDocument). Используется навыками /mxl-compile (JSON → XML) и /mxl-decompile (XML → JSON).

Пример

{
  "columns": 10,
  "defaultWidth": 30,
  "columnWidths": { "1": 15, "2-8": 40, "9-10": 50 },

  "fonts": {
    "default": { "face": "Arial", "size": 10 },
    "bold": { "face": "Arial", "size": 10, "bold": true },
    "header": { "face": "Arial", "size": 14, "bold": true }
  },

  "styles": {
    "default": {},
    "header": { "font": "header", "align": "center" },
    "label": { "font": "bold" },
    "bordered": { "border": "all" },
    "bordered-right": { "border": "all", "align": "right" },
    "total-right": { "font": "bold", "border": "top", "align": "right" }
  },

  "areas": [
    {
      "name": "Заголовок",
      "rows": [
        { "height": 20, "cells": [
          { "col": 1, "span": 10, "style": "header", "param": "ТекстЗаголовка" }
        ]}
      ]
    },
    {
      "name": "ШапкаТаблицы",
      "rows": [
        { "rowStyle": "bordered", "cells": [
          { "col": 1, "text": "№" },
          { "col": 2, "span": 6, "text": "Наименование" },
          { "col": 9, "text": "Кол-во" },
          { "col": 10, "text": "Сумма" }
        ]}
      ]
    },
    {
      "name": "Строка",
      "rows": [
        { "rowStyle": "bordered", "cells": [
          { "col": 1, "param": "НомерСтроки" },
          { "col": 2, "span": 6, "param": "Товар", "detail": "Номенклатура" },
          { "col": 9, "style": "bordered-right", "param": "Количество" },
          { "col": 10, "style": "bordered-right", "param": "Сумма" }
        ]}
      ]
    },
    {
      "name": "Итого",
      "rows": [
        { "cells": [
          { "col": 8, "span": 2, "style": "total-right", "text": "Итого:" },
          { "col": 10, "style": "total-right", "param": "Всего" }
        ]}
      ]
    }
  ]
}

Верхний уровень

Поле Обяз. По умолч. Описание
columns да Количество колонок в раскладке по умолчанию. 0 допустимо: значит, все строки живут в раскладках из columnSets
page нет Формат страницы: "A4-landscape" (780), "A4-portrait" (540) или число. Автоматически вычисляет defaultWidth из суммы пропорций "Nx"
defaultWidth нет 10 Ширина колонок по умолчанию. Игнорируется если задан page и все колонки используют "Nx"
columnWidths нет {} Ширины колонок. Ключи 1-based: "1", "3-14", "5,7,9". Значения: число (абсолют) или "Nx" (множитель от defaultWidth, напр. "2x", "0.5x")
fonts нет Именованные шрифты (если не задано, создаётся Arial 10)
styles нет {} Именованные стили
areas да Массив областей — диапазонов подряд идущих строк (порядок = порядок в документе); имя необязательно
namedAreas нет Именованные области, заданные координатами (см. ниже)
columnSets нет Дополнительные колоночные раскладки: своя ширина колонок у группы строк (см. ниже)

Шрифты (fonts.<name>)

Поле По умолч. Описание
face "Arial" Имя шрифта
size 10 Размер
bold false Жирный
italic false Курсив
underline false Подчёркнутый
strikeout false Зачёркнутый

Шрифт "default" используется когда стиль не указывает шрифт явно. Если не определён, создаётся автоматически (Arial 10).

Стили (styles.<name>)

Поле По умолч. Описание
font "default" Ссылка на имя шрифта
align left, center, right
valign top, center
border Стороны рамки: all, top, bottom, left, right, none. Через запятую: "top,bottom"
borderWidth "thin" Толщина рамки: thin (1px) или thick (2px)
wrap false Перенос текста
format Формат данных 1С: "ЧЦ=15; ЧДЦ=2", "ДФ=dd.MM.yyyy" и т.д.

Области (areas[])

Поле Обяз. Описание
name нет Имя области для Макет.ПолучитьОбласть("Имя")
rows да Массив строк

Макет собирается из областей — диапазонов подряд идущих строк. Имя делает область именованной: она доступна в коде как Макет.ПолучитьОбласть("Имя") и занимает строки своего диапазона. Область без имени — просто кусок сетки: так описываются строки, не принадлежащие ни одной именованной области.

Именованные области координатами (namedAreas[])

Для областей, которые диапазоном подряд идущих строк не описываются: полоса колонок, прямоугольник, ячейка, а также пересекающиеся с другими.

Поле Обяз. Описание
name да Имя области
rows * Строки: число или диапазон "N-M", 1-based
cols * Колонки: число или диапазон "N-M", 1-based

* Обязательна хотя бы одна из осей.

Тип области не указывается — он следует из того, какие оси заданы, как в ТабличныйДокумент.Область(): только строки → полоса строк, только колонки → полоса колонок, обе оси → прямоугольник, одиночные значения по обеим осям → одна ячейка.

"namedAreas": [
  { "name": "ОбластьПечатиПоВысоте", "rows": "1-48" },
  { "name": "ОбластьПечатиПоШирине", "cols": "1-35" },
  { "name": "HZY", "rows": 9, "cols": "16-17" }
]

Диапазон — та же грамматика, что у columnWidths, но только число или "N-M": список через запятую запрещён, область непрерывна. Имя обязательно, и хотя бы одна ось должна быть задана; нарушение любого из этих правил → ненулевой код выхода и сообщение в stderr.

Колоночные раскладки (columnSets)

Группа строк может иметь собственные ширины колонок — в 1С это «индивидуальная ширина колонок». Документные columns и columnWidths описывают раскладку по умолчанию; дополнительные объявляются в columnSets, а область ссылается на нужную ключом columnSet — так же, как ячейка ссылается на styles через style.

{
  "columns": 52,
  "columnWidths": { "1": 8 },
  "columnSets": {
    "таблица": { "columns": 52, "columnWidths": { "1": 7, "2-52": 24 } }
  },
  "areas": [
    { "name": "Шапка", "rows": [ ... ] },
    { "name": "ТабличнаяЧасть", "columnSet": "таблица", "rows": [ ... ] }
  ]
}

Раскладка описывается той же парой полей, что и документная: columns — количество колонок (у раскладок оно обычно разное), columnWidths — ширины. Ключ словаря — имя раскладки; в макетах, полученных через /mxl-decompile, это идентификатор из исходного файла, при описании с нуля — любая строка.

Все строки области получают раскладку области, поэтому одна область не может смешивать раскладки. Позиции колонок (col, span) проверяются по ширине раскладки СВОЕЙ области, а не документной.

Ссылка на необъявленную раскладку → ненулевой код выхода и сообщение в stderr.

Строки (rows[])

Поле По умолч. Описание
height Высота строки (если не задана, используется авто)
rowStyle Стиль для ВСЕХ колонок (заполняет пустоты рамками)
cells [] Массив ячеек
empty Количество подряд идущих пустых строк (заменяет N отдельных {})

Строка без cells и rowStyle → пустая строка. { "empty": 3 } эквивалентно трём {}.

Короткая форма: строка массивом

Вместо объекта строка может быть массивом ячеек — позиция определяется порядком, col не указывается.

Элемент Значение
"текст" Статический текст (text)
"{Имя}" Параметр (param)
">" Продолжение ячейки слева — увеличивает её span
`" "`
null Пустая колонка: позиция занята, ячейка не создаётся
{ ... } Обычная ячейка без col; нужна для style, detail, template
"rows": [
  ["Вид", "Остаток", ">", "Итог"],
  ["|",   "начало",  "конец", "|"],
  ["{Вид}", "{Нач}", "{Кон}", "{Итог}"]
]

Здесь «Вид» и «Итог» объединены по вертикали на две строки, «Остаток» — по горизонтали на две колонки.

Ограничения короткой формы:

  • не задать height и rowStyle — это свойства строки, а не ячейки;
  • не выразить текст, совпадающий с ">", "|" или с шаблоном "{...}".

Маркеру нужно, что продолжать: ">" требует ячейку слева в той же строке, "|" — ячейку сверху. Объектный элемент не должен нести col: позиция уже задана порядком. Число элементов не может превышать columns. Нарушение любого из этих правил → ненулевой код выхода и сообщение в stderr.

Ячейки (cells[])

Поле Обяз. По умолч. Описание
col да Позиция колонки (1-based). В короткой форме строки не указывается — позиция берётся из порядка
span нет 1 Объединение по горизонтали (количество колонок)
rowspan нет 1 Объединение по вертикали (количество строк)
style нет rowStyle Стиль ячейки (переопределяет rowStyle)
param нет Параметр заполнения
detail нет Параметр расшифровки (только с param)
text нет Статический текст. Строка или объект { ru, en } — см. ниже
template нет Шаблонный текст с [Параметр]. Строка или объект, как text

Содержимое ячейки

Задаётся ровно одним из ключей, объявлять способ заполнения отдельно не нужно:

  • param — параметр заполнения;
  • template — текст со вставками [Параметр];
  • text — статический текст;
  • ничего — пустая ячейка (нужна, например, ради рамки).

Текст на нескольких языках

text и template принимают строку или объект «язык → текст». Строка означает русский; объект даёт по надписи на каждый язык, в порядке ключей.

{ "col": 1, "text": "Наименование" }
{ "col": 2, "text": { "ru": "Поставщик", "en": "Supplier" } }

Языки макета объявляются отдельно (languageSettings в XML) и по умолчанию ограничены русским; писать текст на языке, которого нет в объявленных, допустимо — так устроено большинство типовых макетов.

rowStyle — автозаполнение

Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки.

Если в предыдущих строках той же области есть ячейки с rowspan, их колонки при автозаполнении пропускаются.

Ограничения

DSL описывает не все конструкции табличного документа. Перечисленное ниже теряется при round-trip (/mxl-decompile/mxl-compile): в JSON оно не попадает, в сгенерированный XML не возвращается.

  • ячейки-поля ввода (containsValue / valueType / controlType);
  • объединения, не привязанные к ячейке (по всей высоте или ширине документа);
  • рисунки и картинки, в том числе штрихкоды;
  • цвет текста, цвет фона ячейки, скрытые строки и колонки, отступ;
  • рамка с разным стилем у разных сторон; стили линий кроме сплошной;
  • группировки строк и колонок;
  • колонтитулы, параметры печати, область печати;
  • объявление языков макета: список языков и текущий язык всегда описываются как русский — сами надписи при этом сохраняются на всех языках.

Пересборка макета из DSL — это полная перегенерация, а не точечная правка XML, поэтому diff после round-trip обычно шире фактической доработки.