@@ -29,13 +29,17 @@ class InspectorHints implements TemplateEngineInterface
2929 * @param Random $random
3030 * @param BlockCacheCollector $cacheCollector
3131 * @param File $fileDriver
32+ * @param string[] $excludedClassPrefixes Block class prefixes to skip inspector wrapping for
33+ * @param string[] $excludedTemplatePaths Template path substrings to skip inspector wrapping for
3234 */
3335 public function __construct (
3436 private readonly TemplateEngineInterface $ subject ,
3537 private readonly bool $ showBlockHints ,
3638 private readonly Random $ random ,
3739 private readonly BlockCacheCollector $ cacheCollector ,
3840 private readonly File $ fileDriver ,
41+ private readonly array $ excludedClassPrefixes = [],
42+ private readonly array $ excludedTemplatePaths = [],
3943 ) {
4044 $ this ->magentoRoot = $ this ->resolveMagentoRoot ();
4145 }
@@ -58,6 +62,55 @@ private function resolveMagentoRoot(): string
5862 return $ path ;
5963 }
6064
65+ /**
66+ * Check if a block class should be excluded from inspector wrapping
67+ *
68+ * @param string $blockClass
69+ * @return bool
70+ */
71+ private function isExcluded (string $ blockClass ): bool
72+ {
73+ foreach ($ this ->excludedClassPrefixes as $ prefix ) {
74+ if (str_starts_with ($ blockClass , $ prefix )) {
75+ return true ;
76+ }
77+ }
78+
79+ return false ;
80+ }
81+
82+ /**
83+ * Check if a template path should be excluded from inspector wrapping
84+ *
85+ * @param string $templateFile
86+ * @return bool
87+ */
88+ private function isExcludedTemplate (string $ templateFile ): bool
89+ {
90+ $ normalized = str_replace ('\\' , '/ ' , strtolower ($ templateFile ));
91+ foreach ($ this ->excludedTemplatePaths as $ path ) {
92+ if (str_contains ($ normalized , str_replace ('\\' , '/ ' , strtolower (trim ($ path ))))) {
93+ return true ;
94+ }
95+ }
96+
97+ return false ;
98+ }
99+
100+ /**
101+ * Check if rendered HTML contains wire attributes (Magewire/Livewire components)
102+ *
103+ * Wrapping these in HTML comments breaks wire:id injection which relies on
104+ * finding the first root element via regex.
105+ *
106+ * @param string $html
107+ * @return bool
108+ */
109+ private function containsWireAttributes (string $ html ): bool
110+ {
111+ return str_contains ($ html , 'wire:id= ' ) || str_contains ($ html , 'wire:initial-data= ' );
112+ }
113+
61114 /**
62115 * Insert inspector data attributes into the rendered block contents
63116 *
@@ -78,6 +131,33 @@ public function render(BlockInterface $block, $templateFile, array $dictionary =
78131 return $ result ;
79132 }
80133
134+ // Skip inspector wrapping for excluded block classes (e.g. Magewire components)
135+ if ($ this ->isExcluded (get_class ($ block ))) {
136+ return $ result ;
137+ }
138+
139+ // Skip inspector wrapping for templates in excluded paths (e.g. /magewire/ directories).
140+ // Magewire injects wire:id AFTER the template engine returns via regex on the root element.
141+ // Wrapping the output in HTML comments before that element breaks the injection.
142+ if ($ this ->isExcludedTemplate ($ templateFile )) {
143+ return $ result ;
144+ }
145+
146+ // Skip inspector wrapping for Magewire component blocks.
147+ // Magewire sets a 'magewire' data key on the block before rendering and injects wire:id
148+ // via regex AFTER the template engine returns. Wrapping the output in HTML comments
149+ // shifts the offset used by insertAttributesIntoHtmlRoot(), causing broken components.
150+ // Soft dependency: hasData() is a Magento DataObject method, not a Magewire class.
151+ if (method_exists ($ block , 'hasData ' ) && $ block ->hasData ('magewire ' )) {
152+ return $ result ;
153+ }
154+
155+ // Skip inspector wrapping if the rendered HTML contains wire attributes (Magewire/Livewire).
156+ // This catches container blocks whose children have already been rendered with wire attributes.
157+ if ($ this ->containsWireAttributes ($ result )) {
158+ return $ result ;
159+ }
160+
81161 // Only inject attributes if there's actual HTML content
82162 if (empty (trim ($ result ))) {
83163 return $ result ;
0 commit comments