← All changes
|
modules/mcp/abilities/utils/widget-context-helper.php
+45
-8
4.3.0-beta1
→
4.3.1
View file →
| @@ -4,8 +4,9 @@ | ||
| 4 | 4 | |
| 5 | 5 | use Elementor\Modules\AtomicWidgets\PropTypes\Base\Array_Prop_Type; |
| 6 | 6 | use Elementor\Modules\AtomicWidgets\PropTypes\Base\Object_Prop_Type; |
| 7 | 7 | use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Prop_Type; |
| 8 | +use Elementor\Modules\AtomicWidgets\PropTypes\Escaped_Html_Prop_Type; | |
| 8 | 9 | use Elementor\Modules\AtomicWidgets\PropTypes\Utils\Plain_Llm_Schema_Converter; |
| 9 | 10 | use Elementor\Modules\GlobalClasses\Utils\Atomic_Elements_Utils; |
| 10 | 11 | use Elementor\Modules\Mcp\Abilities\Appliers\V3\V3_Widget_Bridge_Registry; |
| 11 | 12 | use Elementor\Plugin; |
| @@ -41,8 +42,10 @@ | ||
| 41 | 42 | const V3_FALLBACK_MESSAGE = '`properties` lists the only keys accepted in `element_config` / `manage-elements.settings` for this widget. Put all visual styling in the `style` (CSS) input.'; |
| 42 | 43 | |
| 43 | 44 | const V3_FALLBACK_FIELDS_NOTE = 'All properties are optional. Object-typed properties describe common shapes but do not include exhaustive inner validation.'; |
| 44 | 45 | |
| 46 | + const ALLOWED_HTML_TAGS_NOTE = 'May contain inline HTML written directly in the string (e.g. "Hello <strong>world</strong>"), limited to these tags: %s. Any other tag is stripped on save.'; | |
| 47 | + | |
| 45 | 48 | /** |
| 46 | 49 | * @return array<string, array> widget_type => config, filtered to widgets eligible for LLM use. |
| 47 | 50 | */ |
| 48 | 51 | public static function get_llm_eligible_widgets(): array { |
| @@ -166,9 +169,9 @@ | ||
| 166 | 169 | 'additionalProperties' => false, |
| 167 | 170 | ] ); |
| 168 | 171 | } |
| 169 | 172 | |
| 170 | - $properties = self::build_configurable_properties_schema( $props_schema ); | |
| 173 | + $properties = self::build_configurable_properties_schema( $props_schema, $widget_type ); | |
| 171 | 174 | |
| 172 | 175 | return self::filter_nulls( [ |
| 173 | 176 | 'type' => 'object', |
| 174 | 177 | 'properties' => $properties, |
| @@ -179,9 +182,9 @@ | ||
| 179 | 182 | |
| 180 | 183 | /** |
| 181 | 184 | * @param array<string, Prop_Type> $props_schema |
| 182 | 185 | */ |
| 183 | - private static function build_configurable_properties_schema( array $props_schema ): array { | |
| 186 | + private static function build_configurable_properties_schema( array $props_schema, string $widget_type ): array { | |
| 184 | 187 | $properties = []; |
| 185 | 188 | |
| 186 | 189 | foreach ( $props_schema as $key => $prop_type ) { |
| 187 | 190 | if ( ! $prop_type instanceof Prop_Type || ! self::is_prop_key_configurable( $key, $prop_type ) ) { |
| @@ -187,20 +190,54 @@ | ||
| 187 | 190 | if ( ! $prop_type instanceof Prop_Type || ! self::is_prop_key_configurable( $key, $prop_type ) ) { |
| 188 | 191 | continue; |
| 189 | 192 | } |
| 190 | 193 | |
| 191 | - $properties[ $key ] = $prop_type->to_json_schema(); | |
| 194 | + $schema = self::to_plain_llm_schema_from_json( $prop_type->to_json_schema() ); | |
| 195 | + $allowed_html_tags = Escaped_Html_Prop_Type::get_allowed_html_tags_for_prop( $widget_type, $key ); | |
| 196 | + | |
| 197 | + if ( null !== $allowed_html_tags ) { | |
| 198 | + $schema = self::describe_allowed_html_tags( $schema, $allowed_html_tags ); | |
| 199 | + } | |
| 200 | + | |
| 201 | + $properties[ $key ] = $schema; | |
| 192 | 202 | } |
| 193 | 203 | |
| 194 | - return self::apply_llm_schema_filters( $properties ); | |
| 204 | + return $properties; | |
| 195 | 205 | } |
| 196 | 206 | |
| 197 | - private static function apply_llm_schema_filters( array $properties ): array { | |
| 198 | - foreach ( $properties as $key => $schema ) { | |
| 199 | - $properties[ $key ] = self::to_plain_llm_schema_from_json( $schema ); | |
| 207 | + /** | |
| 208 | + * Keeps the machine-readable tag list while spelling out in the description that the string | |
| 209 | + * itself may carry that markup — a bare `allowed_html_tags` key is non-standard JSON Schema | |
| 210 | + * and reads as ambiguous next to `type: string`. | |
| 211 | + */ | |
| 212 | + private static function describe_allowed_html_tags( array $schema, array $allowed_html_tags ): array { | |
| 213 | + $schema['allowed_html_tags'] = $allowed_html_tags; | |
| 214 | + | |
| 215 | + $tag_list = implode( ', ', array_map( fn( $tag ) => "<{$tag}>", $allowed_html_tags ) ); | |
| 216 | + $note = sprintf( self::ALLOWED_HTML_TAGS_NOTE, $tag_list ); | |
| 217 | + | |
| 218 | + if ( ! isset( $schema['anyOf'] ) || ! is_array( $schema['anyOf'] ) ) { | |
| 219 | + return self::append_description( $schema, $note ); | |
| 200 | 220 | } |
| 201 | 221 | |
| 202 | - return $properties; | |
| 222 | + // The markup rule belongs on the static string variant only — a dynamic-tag branch | |
| 223 | + // resolves its own value and never carries inline HTML from the caller. | |
| 224 | + $schema['anyOf'] = array_map( | |
| 225 | + fn( $branch ) => is_array( $branch ) && 'string' === ( $branch['type'] ?? null ) | |
| 226 | + ? self::append_description( $branch, $note ) | |
| 227 | + : $branch, | |
| 228 | + $schema['anyOf'] | |
| 229 | + ); | |
| 230 | + | |
| 231 | + return $schema; | |
| 232 | + } | |
| 233 | + | |
| 234 | + private static function append_description( array $schema, string $note ): array { | |
| 235 | + $schema['description'] = isset( $schema['description'] ) | |
| 236 | + ? $schema['description'] . ' ' . $note | |
| 237 | + : $note; | |
| 238 | + | |
| 239 | + return $schema; | |
| 203 | 240 | } |
| 204 | 241 | |
| 205 | 242 | public static function to_plain_llm_schema( Prop_Type $prop_type ): array { |
| 206 | 243 | $schema = self::to_plain_llm_schema_from_json( $prop_type->to_json_schema() ); |