feat: PresetBook nennt Presets mit Verweisen auf Farben/Variablen und ersetzt Variablen-Verweise durch ihren Wert

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 14:03:40 +02:00
co-authored by Claude Sonnet 5.5
parent a4ea487174
commit 653169a133
2 changed files with 175 additions and 0 deletions
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Design;
use InvalidArgumentException;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Abilities\Rev;
defined('ABSPATH') || exit;
@@ -21,6 +22,9 @@ final class PresetBook
private const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
/** The keys of a preset that hold its settings (formats §3). */
private const SETTINGS = ['attrs', 'styleAttrs', 'renderAttrs'];
/** @param array{module: array<string, array{default: string, items: array<string, array<string, mixed>>}>,
* group: array<string, array{default: string, items: array<string, array<string, mixed>>}>} $data */
private function __construct(private array $data)
@@ -226,6 +230,59 @@ final class PresetBook
return new self($data);
}
/**
* Presets whose settings (attrs, styleAttrs, renderAttrs) name the global color or variable $id – as a whole
* reference or inside a longer value (ruling V8: warnings of lit/divi-colors-set and lit/divi-variables-set).
*
* @return list<array{id: string, name: string, scope: string}> scope = module or group name
*/
public function referencing(string $id): array
{
$users = [];
foreach ($this->data as $records) {
foreach ($records as $scope => $record) {
foreach ($record['items'] as $presetId => $item) {
if (ReferenceUsageQuery::mentions(array_intersect_key($item, array_flip(self::SETTINGS)), [$id])) {
$users[] = [
'id' => (string) $presetId,
'name' => is_string($item['name'] ?? null) ? $item['name'] : (string) $presetId,
'scope' => (string) $scope,
];
}
}
}
}
return $users;
}
/**
* The inventory with every whole reference to global variable $id in the presets' settings replaced by $value
* (ruling V9, lit/divi-design-detach with variable). With $affixes the reference's own before/after settings frame
* the value (variables of type strings). References inside a longer value stay. Presets without a reference stay
* exactly as they are.
*/
public function withVariableResolved(string $id, string $value, bool $affixes = false): self
{
$data = $this->data;
foreach ($this->data as $kind => $records) {
foreach ($records as $name => $record) {
foreach ($record['items'] as $presetId => $item) {
foreach (self::SETTINGS as $key) {
if (! is_array($item[$key] ?? null)) {
continue;
}
$resolved = self::resolved($item[$key], $id, $value, $affixes);
if ($resolved !== $item[$key]) {
$data[$kind][$name]['items'][$presetId][$key] = $resolved;
}
}
}
}
}
return new self($data);
}
/**
* IDs of the presets outside $touchedIds whose settings (attrs, styleAttrs, renderAttrs) hold a string with "<"
* or ">" – HTML or CSS that Divi's prepare_data() strips when a user without unfiltered_html saves the inventory
@@ -302,6 +359,28 @@ final class PresetBook
return $nested;
}
/**
* @param array<array-key, mixed> $settings
* @return array<array-key, mixed>
*/
private static function resolved(array $settings, string $id, string $value, bool $affixes): array
{
foreach ($settings as $key => $inner) {
if (is_array($inner)) {
$settings[$key] = self::resolved($inner, $id, $value, $affixes);
} elseif (is_string($inner) && VariableRef::nameOf($inner) === $id) {
$own = $affixes ? VariableRef::settingsOf($inner) : [];
$settings[$key] = self::text($own['before'] ?? '') . $value . self::text($own['after'] ?? '');
}
}
return $settings;
}
private static function text(mixed $value): string
{
return is_string($value) ? $value : '';
}
private static function hasMarkup(mixed $value): bool
{
if (is_string($value)) {