PluginProbe
Elementor Website Builder – more than just a page builder / trunk
Elementor Website Builder – more than just a page builder vtrunk
4.3.3 4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 All 456 releases
← All changes | modules/mcp/abilities/get-structure-ability.php +41 -21 4.3.0-beta2 → trunk View file →
@@ -11,8 +11,9 @@
11 11 use Elementor\Modules\Mcp\Abilities\Appliers\V3_Node_Bridge;
12 12 use Elementor\Modules\Mcp\Abilities\Appliers\V3\V3_Non_Style_Allowlist;
13 13 use Elementor\Modules\Mcp\Abilities\Appliers\V3\V3_Style_Serializer;
14 14 use Elementor\Modules\Mcp\Abilities\Appliers\V3\V3_Widget_Bridge_Registry;
15 +use Elementor\Modules\Mcp\Abilities\Utils\Atomic_Container_Presentation;
15 16 use Elementor\Modules\Mcp\Abilities\Utils\Element_Default_Styles_Builder;
16 17 use Elementor\Modules\Mcp\Abilities\Utils\Element_Tag_Resolver;
17 18 use Elementor\Modules\Mcp\Abilities\Utils\Widget_Context_Helper;
18 19 use Elementor\Plugin;
@@ -36,9 +37,9 @@
36 37
37 38 protected function get_definition(): Ability_Definition {
38 39 return new Ability_Definition(
39 40 __( 'Get Elementor Page Structure', 'elementor' ),
40 - __( 'Returns a lean Elementor element tree skeleton (id, elType, widgetType, version, title, nested elements) for a single post or page ID. Each node is tagged with version=3 (legacy) or version=4 (atomic). Only version=4 nodes can be modified via elementor/manage-elements or referenced by elementor/build-composition element_config; version=3 nodes are returned for context only and must be edited directly in the Elementor editor. Optionally scope to a subtree via element_id. Set include_content=true (requires element_id) to also return each V4 node\'s settings, styles (as { __style_id, css } where css is a raw CSS string round-trippable to manage-elements.update.style / build-composition.style in replace mode), interactions, its rendered HTML tag when known, and default_styles (a raw CSS string of the widget base layer followed by the kit\'s site-wide default layer for that tag, in browser cascade order — includes selectors, @media(--breakpoint) blocks, and pseudo-states as the frontend renders them). V3 nodes are returned with empty settings and styles. Only works for posts that were saved with Elementor.', 'elementor' ),
41 + __( 'Returns a lean Elementor element tree skeleton (id, elType, widgetType, version, title, nested elements) for a single post or page ID. Each node is tagged with version=3 (legacy) or version=4 (atomic). Only version=4 nodes can be modified via elementor/manage-elements or referenced by elementor/build-composition element_config; version=3 nodes are returned for context only and must be edited directly in the Elementor editor. Optionally scope to a subtree via element_id. Set include_content=true (requires element_id) to also return each V4 node\'s settings in the plain shape that manage-elements.update.settings / build-composition.element_config accept, so a value can be sent back as-is, styles (as { __style_id, css } where css is a raw CSS string round-trippable to manage-elements.update.style / build-composition.style in replace mode), interactions, its rendered HTML tag when known, and default_styles (a raw CSS string of the widget base layer followed by the kit\'s site-wide default layer for that tag, in browser cascade order — includes selectors, @media(--breakpoint) blocks, and pseudo-states as the frontend renders them). V3 nodes are returned with empty settings and styles. Only works for posts that were saved with Elementor.', 'elementor' ),
41 42 'elementor',
42 43 [
43 44 'type' => 'object',
44 45 'properties' => [
@@ -43,9 +44,9 @@
43 44 'type' => 'object',
44 45 'properties' => [
45 46 'elements' => [
46 47 'type' => 'array',
47 - 'description' => 'Skeleton of Elementor elements (id, elType, widgetType, version, title, nested elements). When include_content is true, V4 nodes also include settings, styles (as { __style_id, css } — raw CSS string with @media(--breakpoint) + &:hover/&:focus/&:active), interactions, tag (rendered HTML wrapper tag when known), and default_styles (raw CSS string: widget base layer + kit site-wide default for that tag, in cascade order). V3 nodes always have empty settings and styles.',
48 + 'description' => 'Skeleton of Elementor elements (id, elType, widgetType, version, title, nested elements). When include_content is true, V4 nodes also include settings, styles (as { __style_id, css } — raw CSS string with @media(--breakpoint) + &:hover/&:focus/&:active), interactions, tag (rendered HTML wrapper tag when known), and default_styles (raw CSS string, in cascade order: `.e-con` shell styles of atomic elements, including document-root rules for top-level elements + widget base layer + kit site-wide default for that tag). V3 nodes always have empty settings and styles.',
48 49 ],
49 50 ],
50 51 ],
51 52 [
@@ -72,9 +73,9 @@
72 73 ],
73 74 'include_content' => [
74 75 'type' => 'boolean',
75 76 'default' => false,
76 - 'description' => 'If true, includes each V4 node\'s settings, styles (as { __style_id, css } — raw CSS string), interactions, rendered tag, and default_styles (raw CSS string: base layer + kit default for that tag). The styles.css value is round-trippable to build-composition.style / manage-elements.update.style in replace mode. Requires element_id.',
77 + 'description' => 'If true, includes each V4 node\'s settings (plain shape, writable as-is), styles (as { __style_id, css } — raw CSS string), interactions, rendered tag, and default_styles (raw CSS string: base layer + kit default for that tag). The styles.css value is round-trippable to build-composition.style / manage-elements.update.style in replace mode. Requires element_id.',
77 78 ],
78 79 ],
79 80 ]
80 81 );
@@ -104,8 +105,10 @@
104 105 [ 'status' => \WP_Http::BAD_REQUEST ]
105 106 );
106 107 }
107 108
109 + $document_root_ids = array_column( $elements, 'id' );
110 +
108 111 if ( '' !== $element_id ) {
109 112 $subtree = Utils::find_element_recursive( $elements, $element_id );
110 113
111 114 if ( ! $subtree ) {
@@ -119,14 +122,14 @@
119 122 $elements = [ $subtree ];
120 123 }
121 124
122 125 return [
123 - 'elements' => $this->prune_elements( $elements, $include_content ),
126 + 'elements' => $this->prune_elements( $elements, $include_content, $document_root_ids ),
124 127 ];
125 128 }
126 129
127 - private function prune_elements( array $elements, bool $include_content = false ): array {
128 - return Plugin::$instance->db->iterate_data( $elements, function ( $node ) use ( $include_content ) {
130 + private function prune_elements( array $elements, bool $include_content = false, array $document_root_ids = [] ): array {
131 + return Plugin::$instance->db->iterate_data( $elements, function ( $node ) use ( $include_content, $document_root_ids ) {
129 132 $skeleton = [
130 133 'id' => $node['id'] ?? null,
131 134 'elType' => $node['elType'] ?? null,
132 135 ];
@@ -145,9 +148,9 @@
145 148 $skeleton['elements'] = $node['elements'];
146 149 }
147 150
148 151 if ( $include_content ) {
149 - $this->populate_content( $skeleton, $node );
152 + $this->populate_content( $skeleton, $node, in_array( $node['id'] ?? null, $document_root_ids, true ) );
150 153 }
151 154
152 155 return $skeleton;
153 156 } );
@@ -152,9 +155,9 @@
152 155 return $skeleton;
153 156 } );
154 157 }
155 158
156 - private function populate_content( array &$skeleton, array $node ): void {
159 + private function populate_content( array &$skeleton, array $node, bool $is_document_root ): void {
157 160 $skeleton['interactions'] = $this->normalize_interactions( $node['interactions'] ?? null );
158 161
159 162 if ( V3_Node_Bridge::is_v3_node( $node ) ) {
160 163 $this->populate_v3_content( $skeleton, $node );
@@ -173,10 +176,10 @@
173 176
174 177 $skeleton['settings'] = $this->serialize_settings_for_llm( $props_schema, $raw_settings );
175 178 $skeleton['styles'] = Local_Style_Serializer::serialize( $node['styles'] ?? [] );
176 179
177 - $resolved_settings = is_array( $skeleton['settings'] ) ? $skeleton['settings'] : [];
178 - $this->populate_default_styles( $skeleton, $node, $config, $resolved_settings );
180 + $rendered_settings = $this->resolve_rendered_settings( $props_schema, $raw_settings );
181 + $this->populate_default_styles( $skeleton, $node, $config, $rendered_settings, $is_document_root );
179 182 }
180 183
181 184 private function serialize_settings_for_llm( array $props_schema, $raw_settings ) {
182 185 if ( ! is_array( $raw_settings ) ) {
@@ -182,17 +185,36 @@
182 185 if ( ! is_array( $raw_settings ) ) {
183 186 return (object) [];
184 187 }
185 188
186 - $schema = array_intersect_key( $props_schema, $raw_settings );
187 - $serialized = [];
189 + [ $serialized, $static_schema ] = $this->split_dynamic_settings( $props_schema, $raw_settings );
190 +
191 + if ( ! empty( $static_schema ) ) {
192 + $serialized += Render_Props_Resolver::for_plain()->resolve( $static_schema, $raw_settings );
193 + }
194 +
195 + return ! empty( $serialized ) ? $serialized : (object) [];
196 + }
197 +
198 + private function resolve_rendered_settings( array $props_schema, $raw_settings ): array {
199 + if ( ! is_array( $raw_settings ) ) {
200 + return [];
201 + }
202 +
203 + [ , $static_schema ] = $this->split_dynamic_settings( $props_schema, $raw_settings );
204 +
205 + return empty( $static_schema ) ? [] : Render_Props_Resolver::for_settings()->resolve( $static_schema, $raw_settings );
206 + }
207 +
208 + private function split_dynamic_settings( array $props_schema, array $raw_settings ): array {
209 + $dynamic_settings = [];
188 210 $static_schema = [];
189 211
190 - foreach ( $schema as $key => $prop_type ) {
212 + foreach ( array_intersect_key( $props_schema, $raw_settings ) as $key => $prop_type ) {
191 213 $dynamic = Dynamic_Tag_Llm_Resolver::try_serialize( $raw_settings[ $key ] );
192 214
193 215 if ( null !== $dynamic ) {
194 - $serialized[ $key ] = $dynamic;
216 + $dynamic_settings[ $key ] = $dynamic;
195 217 continue;
196 218 }
197 219
198 220 $static_schema[ $key ] = $prop_type;
@@ -197,16 +219,12 @@
197 219
198 220 $static_schema[ $key ] = $prop_type;
199 221 }
200 222
201 - if ( ! empty( $static_schema ) ) {
202 - $serialized += Render_Props_Resolver::for_settings()->resolve( $static_schema, $raw_settings );
203 - }
204 -
205 - return ! empty( $serialized ) ? $serialized : (object) [];
223 + return [ $dynamic_settings, $static_schema ];
206 224 }
207 225
208 - private function populate_default_styles( array &$skeleton, array $node, array $config, array $resolved_settings ): void {
226 + private function populate_default_styles( array &$skeleton, array $node, array $config, array $resolved_settings, bool $is_document_root ): void {
209 227 $base_styles = is_array( $config['base_styles'] ?? null ) ? $config['base_styles'] : [];
210 228 $props_schema = is_array( $config['atomic_props_schema'] ?? null ) ? $config['atomic_props_schema'] : [];
211 229 $tag = $this->resolve_html_tag( $node, $resolved_settings );
212 230
@@ -212,9 +230,11 @@
212 230
213 231 $default_styles_css = Element_Default_Styles_Builder::render(
214 232 $base_styles,
215 233 $tag,
216 - $this->get_default_styles_repository()
234 + $this->get_default_styles_repository(),
235 + null,
236 + Atomic_Container_Presentation::applies_to( $config ) ? Atomic_Container_Presentation::to_css_string( $is_document_root ) : ''
217 237 );
218 238
219 239 if ( null !== $tag ) {
220 240 $skeleton['tag'] = $tag;