Add rowspan and numberFormat support to mxl-compile

- rowspan: vertical cell merging with rowStyle gap-fill awareness
- numberFormat: style field generates nested <format> in palette
- Updated SKILL.md: new fields, removed from limitations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-02-08 18:33:41 +03:00
co-authored by Claude Opus 4.6
parent 2f260ddb7f
commit f0bb8c860f
2 changed files with 67 additions and 27 deletions
+3 -3
View File
@@ -142,6 +142,7 @@ powershell.exe -NoProfile -File .claude/skills/mxl-compile/scripts/mxl-compile.p
| `border` | — | Стороны рамки: `all`, `top`, `bottom`, `left`, `right`, `none`. Через запятую: `"top,bottom"` | | `border` | — | Стороны рамки: `all`, `top`, `bottom`, `left`, `right`, `none`. Через запятую: `"top,bottom"` |
| `borderWidth` | `"thin"` | Толщина рамки: `thin` (1px) или `thick` (2px) | | `borderWidth` | `"thin"` | Толщина рамки: `thin` (1px) или `thick` (2px) |
| `wrap` | `false` | Перенос текста | | `wrap` | `false` | Перенос текста |
| `numberFormat` | — | Формат числа 1С, например `"ЧЦ=15; ЧДЦ=2"` |
### Области (`areas[]`) ### Области (`areas[]`)
@@ -165,7 +166,8 @@ powershell.exe -NoProfile -File .claude/skills/mxl-compile/scripts/mxl-compile.p
| Поле | Обяз. | По умолч. | Описание | | Поле | Обяз. | По умолч. | Описание |
|------|:-----:|-----------|----------| |------|:-----:|-----------|----------|
| `col` | да | — | Позиция колонки (1-based) | | `col` | да | — | Позиция колонки (1-based) |
| `span` | нет | `1` | Количество объединённых колонок | | `span` | нет | `1` | Объединение по горизонтали (количество колонок) |
| `rowspan` | нет | `1` | Объединение по вертикали (количество строк) |
| `style` | нет | rowStyle | Стиль ячейки (переопределяет rowStyle) | | `style` | нет | rowStyle | Стиль ячейки (переопределяет rowStyle) |
| `param` | нет | — | Параметр заполнения | | `param` | нет | — | Параметр заполнения |
| `detail` | нет | — | Параметр расшифровки (только с `param`) | | `detail` | нет | — | Параметр расшифровки (только с `param`) |
@@ -187,8 +189,6 @@ powershell.exe -NoProfile -File .claude/skills/mxl-compile/scripts/mxl-compile.p
- Множественные наборы колонок - Множественные наборы колонок
- Области типа Columns / Rectangle - Области типа Columns / Rectangle
- Рисунки (штрихкоды, картинки) - Рисунки (штрихкоды, картинки)
- Объединение по строкам (rowspan)
- Числовые форматы
- Фон ячеек - Фон ячеек
Эти возможности будут добавлены в будущих версиях без переделки основной архитектуры. Эти возможности будут добавлены в будущих версиях без переделки основной архитектуры.
@@ -138,7 +138,7 @@ function Resolve-Style {
$fontIdx = $fontMap["default"] $fontIdx = $fontMap["default"]
$lb = -1; $tb = -1; $rb = -1; $bb = -1 $lb = -1; $tb = -1; $rb = -1; $bb = -1
$ha = ""; $va = "" $ha = ""; $va = ""; $nf = ""
$wrap = $false $wrap = $false
if ($styleName -and $def.styles) { if ($styleName -and $def.styles) {
@@ -180,6 +180,9 @@ function Resolve-Style {
# Wrap # Wrap
if ($style.wrap -eq $true) { $wrap = $true } if ($style.wrap -eq $true) { $wrap = $true }
# Number format
if ($style.numberFormat) { $nf = $style.numberFormat }
} }
} }
@@ -189,6 +192,7 @@ function Resolve-Style {
HA = $ha; VA = $va HA = $ha; VA = $va
Wrap = $wrap Wrap = $wrap
FillType = $fillType FillType = $fillType
NumberFormat = $nf
} }
} }
@@ -204,10 +208,11 @@ function Get-FormatKey {
[string]$ha = "", [string]$va = "", [string]$ha = "", [string]$va = "",
[bool]$wrap = $false, [bool]$wrap = $false,
[string]$fillType = "", [string]$fillType = "",
[string]$numberFormat = "",
[int]$width = -1, [int]$width = -1,
[int]$height = -1 [int]$height = -1
) )
return "f=$fontIdx|lb=$lb|tb=$tb|rb=$rb|bb=$bb|ha=$ha|va=$va|wr=$wrap|ft=$fillType|w=$width|h=$height" return "f=$fontIdx|lb=$lb|tb=$tb|rb=$rb|bb=$bb|ha=$ha|va=$va|wr=$wrap|ft=$fillType|nf=$numberFormat|w=$width|h=$height"
} }
function Register-Format { function Register-Format {
@@ -263,7 +268,8 @@ function Register-CellFormat {
$key = Get-FormatKey -fontIdx $resolved.FontIdx ` $key = Get-FormatKey -fontIdx $resolved.FontIdx `
-lb $resolved.LB -tb $resolved.TB -rb $resolved.RB -bb $resolved.BB ` -lb $resolved.LB -tb $resolved.TB -rb $resolved.RB -bb $resolved.BB `
-ha $resolved.HA -va $resolved.VA ` -ha $resolved.HA -va $resolved.VA `
-wrap $resolved.Wrap -fillType $resolved.FillType -wrap $resolved.Wrap -fillType $resolved.FillType `
-numberFormat $resolved.NumberFormat
$props = @{ $props = @{
FontIdx = $resolved.FontIdx FontIdx = $resolved.FontIdx
LB = $resolved.LB; TB = $resolved.TB LB = $resolved.LB; TB = $resolved.TB
@@ -271,6 +277,7 @@ function Register-CellFormat {
HA = $resolved.HA; VA = $resolved.VA HA = $resolved.HA; VA = $resolved.VA
Wrap = $resolved.Wrap Wrap = $resolved.Wrap
FillType = $resolved.FillType FillType = $resolved.FillType
NumberFormat = $resolved.NumberFormat
} }
return Register-Format -key $key -props $props return Register-Format -key $key -props $props
} }
@@ -351,8 +358,20 @@ $totalRowCount = 0
foreach ($area in $def.areas) { foreach ($area in $def.areas) {
$areaStartRow = $globalRow $areaStartRow = $globalRow
$areaName = $area.name $areaName = $area.name
$activeRowspans = @() # @{ColStart=1-based; ColEnd=1-based; EndLocalRow=int}
$localRow = 0
foreach ($row in $area.rows) { foreach ($row in $area.rows) {
# Build set of columns occupied by rowspans from previous rows
$rowspanOccupied = @{} # 1-based col -> $true
foreach ($rs in $activeRowspans) {
if ($localRow -gt $rs.StartLocalRow -and $localRow -le $rs.EndLocalRow) {
for ($c = $rs.ColStart; $c -le $rs.ColEnd; $c++) {
$rowspanOccupied[$c] = $true
}
}
}
$rowHasContent = $false $rowHasContent = $false
$rowCells = @() # array of { Col(0-based), FormatIdx, Content } $rowCells = @() # array of { Col(0-based), FormatIdx, Content }
@@ -371,8 +390,9 @@ foreach ($area in $def.areas) {
if ($row.cells -and $row.cells.Count -gt 0) { if ($row.cells -and $row.cells.Count -gt 0) {
$rowHasContent = $true $rowHasContent = $true
# Build set of occupied columns (1-based) # Build set of occupied columns (1-based): explicit cells + rowspan from above
$occupiedCols = @{} $occupiedCols = @{}
foreach ($rsk in $rowspanOccupied.Keys) { $occupiedCols[$rsk] = $true }
foreach ($cell in $row.cells) { foreach ($cell in $row.cells) {
$colStart = [int]$cell.col $colStart = [int]$cell.col
$colSpan = if ($cell.span) { [int]$cell.span } else { 1 } $colSpan = if ($cell.span) { [int]$cell.span } else { 1 }
@@ -385,6 +405,7 @@ foreach ($area in $def.areas) {
foreach ($cell in $row.cells) { foreach ($cell in $row.cells) {
$colStart = [int]$cell.col $colStart = [int]$cell.col
$colSpan = if ($cell.span) { [int]$cell.span } else { 1 } $colSpan = if ($cell.span) { [int]$cell.span } else { 1 }
$rowspan = if ($cell.rowspan) { [int]$cell.rowspan } else { 1 }
$cellStyle = if ($cell.style) { $cell.style } elseif ($row.rowStyle) { $row.rowStyle } else { "default" } $cellStyle = if ($cell.style) { $cell.style } elseif ($row.rowStyle) { $row.rowStyle } else { "default" }
$ft = Get-FillType $cell $ft = Get-FillType $cell
$fmtIdx = Register-CellFormat -styleName $cellStyle -fillType $ft $fmtIdx = Register-CellFormat -styleName $cellStyle -fillType $ft
@@ -399,14 +420,22 @@ foreach ($area in $def.areas) {
} }
$rowCells += $cellInfo $rowCells += $cellInfo
# Collect merge # Track rowspan for subsequent rows
if ($colSpan -gt 1) { if ($rowspan -gt 1) {
$merges += @{ $activeRowspans += @{
R = $globalRow ColStart = $colStart
C = $colStart - 1 ColEnd = $colStart + $colSpan - 1
W = $colSpan - 1 StartLocalRow = $localRow
EndLocalRow = $localRow + $rowspan - 1
} }
} }
# Collect merge (horizontal, vertical, or both)
if ($colSpan -gt 1 -or $rowspan -gt 1) {
$merge = @{ R = $globalRow; C = $colStart - 1; W = $colSpan - 1 }
if ($rowspan -gt 1) { $merge.H = $rowspan - 1 }
$merges += $merge
}
} }
# Generate gap-fill cells for rowStyle # Generate gap-fill cells for rowStyle
@@ -430,12 +459,13 @@ foreach ($area in $def.areas) {
$rowCells = $rowCells | Sort-Object { $_.Col } $rowCells = $rowCells | Sort-Object { $_.Col }
} elseif ($row.rowStyle) { } elseif ($row.rowStyle) {
# Row with only rowStyle, no explicit cells — fill all columns # Row with only rowStyle, no explicit cells — fill non-rowspan columns
$rowHasContent = $true $rowHasContent = $true
$gapFmtIdx = Register-CellFormat -styleName $row.rowStyle -fillType "" $gapFmtIdx = Register-CellFormat -styleName $row.rowStyle -fillType ""
for ($c = 0; $c -lt $totalColumns; $c++) { for ($c = 1; $c -le $totalColumns; $c++) {
if ($rowspanOccupied.ContainsKey($c)) { continue }
$rowCells += @{ $rowCells += @{
Col = $c Col = $c - 1
FormatIdx = $gapFmtIdx FormatIdx = $gapFmtIdx
Param = $null Param = $null
Detail = $null Detail = $null
@@ -496,6 +526,7 @@ foreach ($area in $def.areas) {
X "`t`t</row>" X "`t`t</row>"
X "`t</rowsItem>" X "`t</rowsItem>"
$localRow++
$globalRow++ $globalRow++
} }
@@ -520,6 +551,7 @@ foreach ($m in $merges) {
X "`t<merge>" X "`t<merge>"
X "`t`t<r>$($m.R)</r>" X "`t`t<r>$($m.R)</r>"
X "`t`t<c>$($m.C)</c>" X "`t`t<c>$($m.C)</c>"
if ($m.H) { X "`t`t<h>$($m.H)</h>" }
X "`t`t<w>$($m.W)</w>" X "`t`t<w>$($m.W)</w>"
X "`t</merge>" X "`t</merge>"
} }
@@ -593,6 +625,14 @@ foreach ($key in $formatRegistry.Keys) {
if ($fmt.FillType) { if ($fmt.FillType) {
X "`t`t<fillType>$($fmt.FillType)</fillType>" X "`t`t<fillType>$($fmt.FillType)</fillType>"
} }
if ($fmt.NumberFormat) {
X "`t`t<format>"
X "`t`t`t<v8:item>"
X "`t`t`t`t<v8:lang>ru</v8:lang>"
X "`t`t`t`t<v8:content>$(Esc-Xml $fmt.NumberFormat)</v8:content>"
X "`t`t`t</v8:item>"
X "`t`t</format>"
}
X "`t</format>" X "`t</format>"
} }