feat: Divi save pipeline – tree_rev lock, kses-safe save, revision, flags, cache, ToolResult

DiviSaver/DiviWriter/DiviComposition/DiviChange, WpValueSanitizer (S4),
WpMediaResolver, DiviFlags (S11), DynamicContentMap from Divi's live
registry (S22), S31 wiring, S35 control strip at apply() entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 14:56:47 +02:00
co-authored by Claude Opus 5.5
parent ea1fd607b2
commit 7762b76305
20 changed files with 1716 additions and 2 deletions
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Pages;
use LIT\MCP\Integrations\Divi\BlockTree\Block;
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
/**
* What a Divi write did to the tree: summary, changed nodes (for ToolResult::changed – IDs of
* surviving nodes are resolved after the change), touched nodes (for the quality warnings)
* and warnings of its own.
*/
final class DiviChange
{
/** @var list<array{block: ?Block, id: ?string, type: string, action: string, label: string}> */
private array $changed = [];
/** @var list<Block> */
private array $touched = [];
/** @var list<string> */
private array $warnings = [];
public function __construct(public readonly string $summary)
{
}
/**
* A node that still exists after the change: "inserted", "updated", "moved" or "duplicated".
* Every action but "moved" counts for the quality warnings.
*/
public function changed(Block $block, string $action): self
{
$this->changed[] = [
'block' => $block,
'id' => null,
'type' => $block->name(),
'action' => $action,
'label' => NodeSummary::label($block),
];
if ($action !== 'moved') {
$this->touched[] = $block;
}
return $this;
}
/** A node that is gone; $idBefore is its ID before the change. */
public function removed(string $idBefore, Block $block): self
{
$this->changed[] = [
'block' => null,
'id' => $idBefore,
'type' => $block->name(),
'action' => 'deleted',
'label' => NodeSummary::label($block),
];
return $this;
}
public function warning(string $warning): self
{
$this->warnings[] = $warning;
return $this;
}
/** @return list<array{block: ?Block, id: ?string, type: string, action: string, label: string}> */
public function entries(): array
{
return $this->changed;
}
/** @return list<Block> */
public function touched(): array
{
return $this->touched;
}
/** @return list<string> */
public function warnings(): array
{
return $this->warnings;
}
}