feat: Divi tools edit Theme Builder layouts; visible layouts need confirm

TB layouts load through ThemeBuilderAccess (P12), live layouts (attached +
enabled, Q8) need a dry run and confirm (Q9) and get the shared restore point
of VisibleEdits, extracted from GlobalEdits (Q2: backup when revisions are
off, P9 refusals, P14 naming). divi/post-content only in body layouts (F22),
also for nested stored nodes. SaveScope::ThemeBuilderLayout sets Divi flags
and clears site CSS + TB cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 15:45:07 +02:00
co-authored by Claude Opus 5.5
parent 4ff1c1e2a5
commit b1dd35abd8
27 changed files with 1106 additions and 132 deletions
@@ -10,8 +10,10 @@ use LIT\MCP\Integrations\Divi\BlockTree\GlobalInstance;
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
use LIT\MCP\Integrations\Divi\BlockTree\Serializer;
use LIT\MCP\Integrations\Divi\Compose\StructureRules;
use LIT\MCP\Integrations\Divi\Compose\TreeEditor;
use LIT\MCP\Tests\Support\ComposeFixture;
use LIT\MCP\Tests\Support\DiviFixtures;
use PHPUnit\Framework\TestCase;
final class TreeEditorTest extends TestCase
@@ -220,4 +222,26 @@ final class TreeEditorTest extends TestCase
$editor->insert($editor->position($index, []), [GlobalInstance::of(Block::create('divi/text'), 1059)])
);
}
/**
* F22 for stored nodes (node-copy, library-insert, duplicate): a copied section that holds divi/post-content
* is refused outside a body layout, however deep the module sits.
*/
public function testInsertChecksTheThemeBuilderAreaOfNestedModules(): void
{
$body = (new Parser())->parse(DiviFixtures::markup('stage3/tb-body--body-mit-dynamic-content.html'));
$section = NodeIndex::build($body)->find('s0')?->copy();
self::assertInstanceOf(Block::class, $section);
$header = (new Parser())->parse(DiviFixtures::markup('stage3/tb-header--globaler-header.html'));
$headerIndex = NodeIndex::build($header);
$editor = new TreeEditor(new StructureRules(DiviFixtures::catalog(), 'header'));
self::assertSame(
'divi/post-content zeigt Divi im Bereich „header“ nicht (nur body)',
$editor->insert($editor->position($headerIndex, []), [$section->copy()])
);
$bodyEditor = new TreeEditor(new StructureRules(DiviFixtures::catalog(), 'body'));
self::assertNull($bodyEditor->insert($bodyEditor->position(NodeIndex::build($body), []), [$section->copy()]));
}
}