fix: Task 6 review round 2 - builder column classes, sum check only for exact rows, unknown path wins, column structure text (S43)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-26 15:31:05 +02:00
co-authored by Claude Opus 5.5
parent 68ca4f45ec
commit 0fddece18d
6 changed files with 153 additions and 10 deletions
@@ -48,6 +48,12 @@ final class Composer
private const DEFAULT_LEVEL = 'h2';
/**
* The builder's "Column Class" choices (Divi 5.2.1 visual-builder/build/field-library.js):
* n_24, the special fractions and "none" (no width class). Ruling S43.
*/
private const COLUMN_CLASSES = ['1_5', '2_5', '3_5', '1_7', '1_9', '1_10', '1_11', 'none'];
/** Advice of the S40 overlap error for new nodes. */
private const ONE_FORM = 'für diesen Pfad nur Kurzform oder attrs angeben';
@@ -467,6 +473,13 @@ final class Composer
}
$result = $this->attrWriter->apply($name, $attrs, $spec['attrs'], $path . '.attrs');
// An unknown path is the more useful message on its key (it decides invalid_attr_path).
foreach ($result['invalid_paths'] as $invalid) {
$key = $path . '.attrs.' . $invalid;
if (isset($result['errors'][$key])) {
$this->errors[$key] = $result['errors'][$key];
}
}
$this->errors += $result['errors'];
array_push($this->invalidPaths, ...$result['invalid_paths']);
@@ -658,6 +671,17 @@ final class Composer
return $error;
}
/** Ruling S43: whether $width is a column class the Visual Builder offers. */
public static function isColumnClass(mixed $width): bool
{
if (! is_string($width)) {
return false;
}
return in_array($width, self::COLUMN_CLASSES, true)
|| (preg_match('/^([1-9]|1\d|2[0-4])_24$/D', $width) === 1);
}
/**
* Whether the fractions add up to exactly 1: numerators scaled to the least common
* denominator (denominators are divisors of 24 or 5, 7, 9, 10, 11 – at most 27720).