@elementor/editor-canvas 3.33.0-98 → 3.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +133 -10
- package/dist/index.d.ts +133 -10
- package/dist/index.js +1413 -212
- package/dist/index.mjs +1399 -180
- package/package.json +18 -14
- package/src/__tests__/settings-props-resolver.test.ts +0 -40
- package/src/__tests__/styles-prop-resolver.test.ts +13 -0
- package/src/components/__tests__/__snapshots__/style-renderer.test.tsx.snap +2 -6
- package/src/components/__tests__/elements-overlays.test.tsx +96 -12
- package/src/components/__tests__/inline-editor-overlay.test.tsx +245 -0
- package/src/components/__tests__/style-renderer.test.tsx +2 -2
- package/src/components/elements-overlays.tsx +33 -10
- package/src/components/inline-editor-overlay.tsx +79 -0
- package/src/components/interactions-renderer.tsx +33 -0
- package/src/components/{element-overlay.tsx → outline-overlay.tsx} +8 -7
- package/src/components/style-renderer.tsx +2 -4
- package/src/hooks/__tests__/use-has-overlapping.test.ts +187 -0
- package/src/hooks/use-floating-on-element.ts +11 -8
- package/src/hooks/use-has-overlapping.ts +21 -0
- package/src/hooks/use-interactions-items.ts +108 -0
- package/src/hooks/use-style-items.ts +34 -8
- package/src/index.ts +9 -0
- package/src/init-settings-transformers.ts +4 -0
- package/src/init.tsx +18 -0
- package/src/legacy/create-templated-element-type.ts +67 -42
- package/src/legacy/init-legacy-views.ts +27 -5
- package/src/legacy/types.ts +44 -4
- package/src/mcp/canvas-mcp.ts +17 -0
- package/src/mcp/mcp-description.ts +40 -0
- package/src/mcp/resources/widgets-schema-resource.ts +173 -0
- package/src/mcp/tools/build-composition/prompt.ts +128 -0
- package/src/mcp/tools/build-composition/schema.ts +31 -0
- package/src/mcp/tools/build-composition/tool.ts +163 -0
- package/src/mcp/tools/configure-element/prompt.ts +93 -0
- package/src/mcp/tools/configure-element/schema.ts +25 -0
- package/src/mcp/tools/configure-element/tool.ts +67 -0
- package/src/mcp/tools/get-element-config/tool.ts +69 -0
- package/src/mcp/utils/do-update-element-property.ts +129 -0
- package/src/mcp/utils/generate-available-tags.ts +23 -0
- package/src/renderers/__tests__/__snapshots__/create-styles-renderer.test.ts.snap +2 -0
- package/src/renderers/__tests__/create-styles-renderer.test.ts +25 -0
- package/src/renderers/create-props-resolver.ts +8 -1
- package/src/renderers/create-styles-renderer.ts +20 -9
- package/src/renderers/errors.ts +6 -0
- package/src/sync/drag-element-from-panel.ts +49 -0
- package/src/sync/types.ts +32 -1
- package/src/transformers/settings/__tests__/attributes-transformer.test.ts +15 -0
- package/src/transformers/settings/__tests__/classes-transformer.test.ts +83 -0
- package/src/transformers/settings/attributes-transformer.ts +1 -23
- package/src/transformers/settings/classes-transformer.ts +21 -21
- package/src/transformers/settings/date-time-transformer.ts +12 -0
- package/src/transformers/settings/query-transformer.ts +10 -0
- package/src/transformers/styles/__tests__/transform-origin-transformer.test.ts +24 -0
- package/src/transformers/styles/__tests__/transition-transformer.test.ts +52 -0
- package/src/transformers/styles/background-transformer.ts +3 -1
- package/src/transformers/styles/transform-origin-transformer.ts +12 -2
- package/src/transformers/styles/transition-transformer.ts +34 -4
- package/src/types/element-overlay.ts +18 -0
- package/src/utils/inline-editing-utils.ts +43 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from '@elementor/schema';
|
|
2
|
+
|
|
3
|
+
import { STYLE_SCHEMA_URI } from '../../resources/widgets-schema-resource';
|
|
4
|
+
|
|
5
|
+
export const inputSchema = {
|
|
6
|
+
xmlStructure: z.string().describe( 'The XML structure representing the composition to be built' ),
|
|
7
|
+
elementConfig: z
|
|
8
|
+
.record(
|
|
9
|
+
z.string().describe( 'The configuration id' ),
|
|
10
|
+
z.record( z.string().describe( 'property name' ), z.any().describe( 'The PropValue for the property' ) )
|
|
11
|
+
)
|
|
12
|
+
.describe( 'A record mapping element IDs to their configuration objects. REQUIRED' ),
|
|
13
|
+
stylesConfig: z
|
|
14
|
+
.record(
|
|
15
|
+
z.string().describe( 'The configuration id' ),
|
|
16
|
+
z.record(
|
|
17
|
+
z.string().describe( '_styles property name' ),
|
|
18
|
+
z.any().describe( 'The PropValue for the style property. MANDATORY' )
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
.describe(
|
|
22
|
+
`A record mapping element IDs to their styles configuration objects. Use the actual styles schema from [${ STYLE_SCHEMA_URI }].`
|
|
23
|
+
)
|
|
24
|
+
.default( {} ),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const outputSchema = {
|
|
28
|
+
errors: z.string().describe( 'Error message if the composition building failed' ).optional(),
|
|
29
|
+
xmlStructure: z.string().describe( 'The built XML structure as a string' ).optional(),
|
|
30
|
+
llmInstructions: z.string().describe( 'Instructions used to further actions for you' ).optional(),
|
|
31
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createElement,
|
|
3
|
+
deleteElement,
|
|
4
|
+
generateElementId,
|
|
5
|
+
getContainer,
|
|
6
|
+
getWidgetsCache,
|
|
7
|
+
type V1Element,
|
|
8
|
+
} from '@elementor/editor-elements';
|
|
9
|
+
import { type MCPRegistryEntry } from '@elementor/editor-mcp';
|
|
10
|
+
import { type PropValue } from '@elementor/editor-props';
|
|
11
|
+
|
|
12
|
+
import { doUpdateElementProperty } from '../../utils/do-update-element-property';
|
|
13
|
+
import { generatePrompt } from './prompt';
|
|
14
|
+
import { inputSchema as schema, outputSchema } from './schema';
|
|
15
|
+
|
|
16
|
+
export const initBuildCompositionsTool = ( reg: MCPRegistryEntry ) => {
|
|
17
|
+
const { addTool } = reg;
|
|
18
|
+
|
|
19
|
+
addTool( {
|
|
20
|
+
name: 'build-compositions',
|
|
21
|
+
description: generatePrompt(),
|
|
22
|
+
schema,
|
|
23
|
+
outputSchema,
|
|
24
|
+
handler: async ( params ) => {
|
|
25
|
+
let xml: Document | null = null;
|
|
26
|
+
const { xmlStructure, elementConfig, stylesConfig } = params;
|
|
27
|
+
const errors: Error[] = [];
|
|
28
|
+
const softErrors: Error[] = [];
|
|
29
|
+
const rootContainers: V1Element[] = [];
|
|
30
|
+
const widgetsCache = getWidgetsCache() || {};
|
|
31
|
+
const documentContainer = getContainer( 'document' ) as unknown as V1Element;
|
|
32
|
+
try {
|
|
33
|
+
const parser = new DOMParser();
|
|
34
|
+
xml = parser.parseFromString( xmlStructure, 'application/xml' );
|
|
35
|
+
const errorNode = xml.querySelector( 'parsererror' );
|
|
36
|
+
if ( errorNode ) {
|
|
37
|
+
throw new Error( 'Failed to parse XML structure: ' + errorNode.textContent );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const children = Array.from( xml.children );
|
|
41
|
+
const iterate = ( node: Element, containerElement: V1Element = documentContainer ) => {
|
|
42
|
+
const elementTag = node.tagName;
|
|
43
|
+
if ( ! widgetsCache[ elementTag ] ) {
|
|
44
|
+
errors.push( new Error( `Unknown widget type: ${ elementTag }` ) );
|
|
45
|
+
}
|
|
46
|
+
const isContainer = elementTag === 'e-flexbox' || elementTag === 'e-div-block';
|
|
47
|
+
const newElement = isContainer
|
|
48
|
+
? createElement( {
|
|
49
|
+
containerId: containerElement.id,
|
|
50
|
+
model: {
|
|
51
|
+
elType: elementTag,
|
|
52
|
+
id: generateElementId(),
|
|
53
|
+
},
|
|
54
|
+
options: { useHistory: false },
|
|
55
|
+
} )
|
|
56
|
+
: createElement( {
|
|
57
|
+
containerId: containerElement.id,
|
|
58
|
+
model: {
|
|
59
|
+
elType: 'widget',
|
|
60
|
+
widgetType: elementTag,
|
|
61
|
+
id: generateElementId(),
|
|
62
|
+
},
|
|
63
|
+
options: { useHistory: false },
|
|
64
|
+
} );
|
|
65
|
+
if ( containerElement === documentContainer ) {
|
|
66
|
+
rootContainers.push( newElement );
|
|
67
|
+
}
|
|
68
|
+
node.setAttribute( 'id', newElement.id );
|
|
69
|
+
const configId = node.getAttribute( 'configuration-id' ) || '';
|
|
70
|
+
try {
|
|
71
|
+
const configObject = elementConfig[ configId ] || {};
|
|
72
|
+
const styleObject = stylesConfig[ configId ] || {};
|
|
73
|
+
configObject._styles = styleObject;
|
|
74
|
+
for ( const [ propertyName, propertyValue ] of Object.entries( configObject ) ) {
|
|
75
|
+
// validate property existance
|
|
76
|
+
const widgetSchema = widgetsCache[ elementTag ];
|
|
77
|
+
if (
|
|
78
|
+
! widgetSchema?.atomic_props_schema?.[ propertyName ] &&
|
|
79
|
+
propertyName !== '_styles' &&
|
|
80
|
+
propertyName !== 'custom_css'
|
|
81
|
+
) {
|
|
82
|
+
softErrors.push(
|
|
83
|
+
new Error(
|
|
84
|
+
`Property "${ propertyName }" does not exist on element type "${ elementTag }".`
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
doUpdateElementProperty( {
|
|
91
|
+
elementId: newElement.id,
|
|
92
|
+
propertyName,
|
|
93
|
+
propertyValue:
|
|
94
|
+
propertyName === 'custom_css'
|
|
95
|
+
? { _styles: propertyValue }
|
|
96
|
+
: ( propertyValue as unknown as PropValue ),
|
|
97
|
+
elementType: elementTag,
|
|
98
|
+
} );
|
|
99
|
+
} catch ( error ) {
|
|
100
|
+
softErrors.push( error as Error );
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if ( isContainer ) {
|
|
104
|
+
for ( const child of node.children ) {
|
|
105
|
+
iterate( child, newElement );
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
node.innerHTML = '';
|
|
109
|
+
node.removeAttribute( 'configuration' );
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
for ( const childNode of children ) {
|
|
116
|
+
iterate( childNode, documentContainer );
|
|
117
|
+
try {
|
|
118
|
+
} catch ( error ) {
|
|
119
|
+
errors.push( error as Error );
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch ( error ) {
|
|
123
|
+
errors.push( error as Error );
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if ( errors.length ) {
|
|
127
|
+
rootContainers.forEach( ( rootContainer ) => {
|
|
128
|
+
deleteElement( {
|
|
129
|
+
elementId: rootContainer.id,
|
|
130
|
+
options: { useHistory: false },
|
|
131
|
+
} );
|
|
132
|
+
} );
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if ( errors.length > 0 ) {
|
|
136
|
+
const errorText = `Failed to build composition with the following errors:\n\n
|
|
137
|
+
${ errors.map( ( e ) => ( typeof e === 'string' ? e : e.message ) ).join( '\n\n' ) }
|
|
138
|
+
"Missing $$type" errors indicate that the configuration objects are invalid. Try again and apply **ALL** object entries with correct $$type.
|
|
139
|
+
Now that you have these errors, fix them and try again. Errors regarding configuration objects, please check again the PropType schemas`;
|
|
140
|
+
throw new Error( errorText );
|
|
141
|
+
}
|
|
142
|
+
if ( ! xml ) {
|
|
143
|
+
throw new Error( 'XML structure is null after parsing.' );
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
xmlStructure: new XMLSerializer().serializeToString( xml ),
|
|
147
|
+
llmInstructions:
|
|
148
|
+
( softErrors.length
|
|
149
|
+
? `The composition was built successfully, but there were some issues with the provided configurations:
|
|
150
|
+
|
|
151
|
+
${ softErrors.map( ( e ) => `- ${ e.message }` ).join( '\n' ) }
|
|
152
|
+
|
|
153
|
+
Please use confiugure-element tool to fix these issues. Now that you have information about these issues, use the configure-element tool to fix them!`
|
|
154
|
+
: '' ) +
|
|
155
|
+
`
|
|
156
|
+
Next Steps:
|
|
157
|
+
- Use "apply-global-class" tool as there may be global styles ready to be applied to elements.
|
|
158
|
+
- Use "configure-element" tool to further configure elements as needed, including styles.
|
|
159
|
+
`,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
} );
|
|
163
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
|
|
2
|
+
|
|
3
|
+
export const configureElementToolPrompt = `Configure an existing element on the page.
|
|
4
|
+
|
|
5
|
+
# **CRITICAL - REQUIRED INFORMATION (Must read before using this tool)**
|
|
6
|
+
1. [${ WIDGET_SCHEMA_URI }]
|
|
7
|
+
Required to understand which widgets are available, and what are their configuration schemas.
|
|
8
|
+
Every widgetType (i.e. e-heading, e-button) that is supported has it's own property schema, that you must follow in order to apply property values correctly.
|
|
9
|
+
2. [${ STYLE_SCHEMA_URI }]
|
|
10
|
+
Required to understand the styles schema for the widgets. All widgets share the same styles schema, grouped by categories.
|
|
11
|
+
Use this resource to understand which style properties are available for each element, and how to structure the "_styles" configuration property.
|
|
12
|
+
3. If not sure about the PropValues schema, you can use the "get-element-configuration-values" tool to retreive the current PropValues configuration of the element.
|
|
13
|
+
|
|
14
|
+
Before using this tool, check the definitions of the elements PropTypes at the resource "widget-schema-by-type" at editor-canvas__elementor://widgets/schema/{widgetType}
|
|
15
|
+
All widgets share a common _style property for styling, which uses the common styles schema.
|
|
16
|
+
Retreive and check the common styles schema at the resource list "styles-schema" at editor-canvas__elementor://styles/schema/{category}
|
|
17
|
+
|
|
18
|
+
# Parameters
|
|
19
|
+
- propertiesToChange: An object containing the properties to change, with their new values. MANDATORY
|
|
20
|
+
- elementId: The ID of the element to configure. MANDATORY
|
|
21
|
+
- elementType: The type of the element to configure (i.e. e-heading, e-button). MANDATORY
|
|
22
|
+
|
|
23
|
+
# When to use this tool
|
|
24
|
+
When a user requires to change anything in an element, such as updating text, colors, sizes, or other configurable properties.
|
|
25
|
+
This tool handles elements of type "widget".
|
|
26
|
+
This tool handles styling elements, using the _styles property in the configuration.
|
|
27
|
+
|
|
28
|
+
The element's schema must be known before using this tool.
|
|
29
|
+
|
|
30
|
+
Attached resource link describing how PropType schema should be parsed as PropValue for this tool.
|
|
31
|
+
|
|
32
|
+
Read carefully the PropType Schema of the element and it's styles, then apply correct PropValue according to the schema.
|
|
33
|
+
|
|
34
|
+
PropValue structure:
|
|
35
|
+
{
|
|
36
|
+
"$$type": string, // MANDATORY as defined in the PropType schema under the "key" property
|
|
37
|
+
value: unknown // The value according to the PropType schema for kinds of "array", use array with PropValues items inside. For "object", read the shape property of the PropType schema. For "plain", use strings.
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
<IMPORTANT>
|
|
41
|
+
ALWAYS MAKE SURE you have the PropType schemas for the element you are configuring, and the common-styles schema for styling. If you are not sure, retreive the schema from the resources mentioned above.
|
|
42
|
+
</IMPORTANT>
|
|
43
|
+
|
|
44
|
+
You can use multiple property changes at once by providing multiple entries in the propertiesToChange object, including _style alongside non-style props.
|
|
45
|
+
Some properties are nested, use the root property name, then objects with nested values inside, as the complete schema suggests.
|
|
46
|
+
Nested properties, such as for the _styles, should include a "_styles" property with object containing the definitions to change.
|
|
47
|
+
|
|
48
|
+
Make sure you have the "widget-schema-by-type" resource available to retreive the PropType schema for the element type you are configuring.
|
|
49
|
+
|
|
50
|
+
# How to configure elements
|
|
51
|
+
We use a dedicated PropType Schema for configuring elements, including styles. When you configure an element, you must use the EXACT PropType Value as defined in the schema.
|
|
52
|
+
For _styles, use the style schema provided, as it also uses the PropType format.
|
|
53
|
+
For all non-primitive types, provide the key property as defined in the schema as $$type in the generated objecct, as it is MANDATORY for parsing.
|
|
54
|
+
|
|
55
|
+
Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property from the original configuration for every property you are changing.
|
|
56
|
+
|
|
57
|
+
# Example
|
|
58
|
+
\`\`\`json
|
|
59
|
+
{
|
|
60
|
+
propertiesToChange: {
|
|
61
|
+
// List of properties TO CHANGE, following the PropType schema for the element as defined in the resource [${ WIDGET_SCHEMA_URI }]
|
|
62
|
+
title: {
|
|
63
|
+
$$type: 'string',
|
|
64
|
+
value: 'New Title Text'
|
|
65
|
+
},
|
|
66
|
+
border: {
|
|
67
|
+
$$type: 'boolean',
|
|
68
|
+
value: false
|
|
69
|
+
},
|
|
70
|
+
_styles: {
|
|
71
|
+
// List of available keys available at the [${ STYLE_SCHEMA_URI }] dynamic resource
|
|
72
|
+
'line-height': {
|
|
73
|
+
$$type: 'size', // MANDATORY do not forget to include the correct $$type for every property
|
|
74
|
+
value: {
|
|
75
|
+
size: {
|
|
76
|
+
$$type: 'number',
|
|
77
|
+
value: 20
|
|
78
|
+
},
|
|
79
|
+
unit: {
|
|
80
|
+
$$type: 'string',
|
|
81
|
+
value: 'px'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
<IMPORTANT>
|
|
91
|
+
The $$type property is MANDATORY for every value, it is required to parse the value and apply application-level effects.
|
|
92
|
+
</IMPORTANT>
|
|
93
|
+
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from '@elementor/schema';
|
|
2
|
+
|
|
3
|
+
export const inputSchema = {
|
|
4
|
+
propertiesToChange: z
|
|
5
|
+
.record(
|
|
6
|
+
z
|
|
7
|
+
.string()
|
|
8
|
+
.describe(
|
|
9
|
+
'The property name. If nested property, provide the root property name, and the object delta only.'
|
|
10
|
+
),
|
|
11
|
+
z.any().describe( "The property's value" )
|
|
12
|
+
)
|
|
13
|
+
.describe( 'An object record containing property names and their new values to be set on the element' )
|
|
14
|
+
.optional(),
|
|
15
|
+
elementType: z.string().describe( 'The type of the element to retreive the schema' ),
|
|
16
|
+
elementId: z.string().describe( 'The unique id of the element to configure' ),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const outputSchema = {
|
|
20
|
+
success: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.describe(
|
|
23
|
+
'Whether the configuration change was successful, only if propertyName and propertyValue are provided'
|
|
24
|
+
),
|
|
25
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type MCPRegistryEntry } from '@elementor/editor-mcp';
|
|
2
|
+
|
|
3
|
+
import { WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
|
|
4
|
+
import { doUpdateElementProperty } from '../../utils/do-update-element-property';
|
|
5
|
+
import { configureElementToolPrompt } from './prompt';
|
|
6
|
+
import { inputSchema as schema, outputSchema } from './schema';
|
|
7
|
+
|
|
8
|
+
export const initConfigureElementTool = ( reg: MCPRegistryEntry ) => {
|
|
9
|
+
const { addTool } = reg;
|
|
10
|
+
|
|
11
|
+
addTool( {
|
|
12
|
+
name: 'configure-element',
|
|
13
|
+
description: configureElementToolPrompt,
|
|
14
|
+
schema,
|
|
15
|
+
outputSchema,
|
|
16
|
+
handler: ( { elementId, propertiesToChange, elementType } ) => {
|
|
17
|
+
if ( ! propertiesToChange ) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'propertiesToChange is required to configure an element. Now that you have this information, ensure you have the schema and try again.'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const toUpdate = Object.entries( propertiesToChange );
|
|
23
|
+
for ( const [ propertyName, propertyValue ] of toUpdate ) {
|
|
24
|
+
if ( ! propertyName && ! elementId && ! elementType ) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'propertyName, elementId, elementType are required to configure an element. If you want to retreive the schema, use the get-element-configuration-schema tool.'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
doUpdateElementProperty( {
|
|
32
|
+
elementId,
|
|
33
|
+
elementType,
|
|
34
|
+
propertyName,
|
|
35
|
+
propertyValue,
|
|
36
|
+
} );
|
|
37
|
+
} catch ( error ) {
|
|
38
|
+
const errorMessage = createUpdateErrorMessage( {
|
|
39
|
+
propertyName,
|
|
40
|
+
elementId,
|
|
41
|
+
elementType,
|
|
42
|
+
error: error as Error,
|
|
43
|
+
} );
|
|
44
|
+
throw new Error( errorMessage );
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
} );
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function createUpdateErrorMessage( opts: {
|
|
55
|
+
propertyName: string;
|
|
56
|
+
elementId: string;
|
|
57
|
+
elementType: string;
|
|
58
|
+
error: Error;
|
|
59
|
+
} ) {
|
|
60
|
+
const { propertyName, elementId, elementType, error } = opts;
|
|
61
|
+
return `Failed to update property "${ propertyName }" on element "${ elementId }": ${ error.message }.
|
|
62
|
+
Check the element's PropType schema at the resource [${ WIDGET_SCHEMA_URI.replace(
|
|
63
|
+
'{widgetType}',
|
|
64
|
+
elementType
|
|
65
|
+
) }] for type "${ elementType }" to ensure the property exists and the value matches the expected PropType.
|
|
66
|
+
Now that you have this information, ensure you have the schema and try again.`;
|
|
67
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { getContainer, getElementStyles, getWidgetsCache } from '@elementor/editor-elements';
|
|
2
|
+
import { type MCPRegistryEntry } from '@elementor/editor-mcp';
|
|
3
|
+
import { type PropValue, Schema } from '@elementor/editor-props';
|
|
4
|
+
import { z } from '@elementor/schema';
|
|
5
|
+
|
|
6
|
+
const schema = {
|
|
7
|
+
elementId: z.string(),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const outputSchema = {
|
|
11
|
+
propValues: z
|
|
12
|
+
.record( z.string(), z.any() )
|
|
13
|
+
.describe(
|
|
14
|
+
'A record mapping PropTypes to their corresponding PropValues, with _styles record for style-related PropValues'
|
|
15
|
+
),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const initGetElementConfigTool = ( reg: MCPRegistryEntry ) => {
|
|
19
|
+
const { addTool } = reg;
|
|
20
|
+
|
|
21
|
+
addTool( {
|
|
22
|
+
name: 'get-element-configuration-values',
|
|
23
|
+
description: "Retrieve the element's configuration PropValues for a specific element by unique ID.",
|
|
24
|
+
schema,
|
|
25
|
+
outputSchema,
|
|
26
|
+
handler: async ( { elementId } ) => {
|
|
27
|
+
const element = getContainer( elementId );
|
|
28
|
+
if ( ! element ) {
|
|
29
|
+
throw new Error( `Element with ID ${ elementId } not found.` );
|
|
30
|
+
}
|
|
31
|
+
const elementRawSettings = element.settings;
|
|
32
|
+
const propSchema = getWidgetsCache()?.[ element.model.get( 'widgetType' ) || '' ]?.atomic_props_schema;
|
|
33
|
+
|
|
34
|
+
if ( ! elementRawSettings || ! propSchema ) {
|
|
35
|
+
throw new Error( `No settings or prop schema found for element ID: ${ elementId }` );
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const propValues: Record< string, PropValue > = {};
|
|
39
|
+
const stylePropValues: Record< string, PropValue > = {};
|
|
40
|
+
|
|
41
|
+
Schema.configurableKeys( propSchema ).forEach( ( key ) => {
|
|
42
|
+
propValues[ key ] = structuredClone( elementRawSettings.get( key ) );
|
|
43
|
+
} );
|
|
44
|
+
const elementStyles = getElementStyles( elementId ) || {};
|
|
45
|
+
const localStyle = Object.values( elementStyles ).find( ( style ) => style.label === 'local' );
|
|
46
|
+
|
|
47
|
+
if ( localStyle ) {
|
|
48
|
+
const defaultVariant = localStyle.variants.find(
|
|
49
|
+
( variant ) => variant.meta.breakpoint === 'desktop' && ! variant.meta.state
|
|
50
|
+
);
|
|
51
|
+
if ( defaultVariant ) {
|
|
52
|
+
const styleProps = defaultVariant.props || {};
|
|
53
|
+
Object.keys( styleProps ).forEach( ( stylePropName ) => {
|
|
54
|
+
if ( typeof styleProps[ stylePropName ] !== 'undefined' ) {
|
|
55
|
+
stylePropValues[ stylePropName ] = structuredClone( styleProps[ stylePropName ] );
|
|
56
|
+
}
|
|
57
|
+
} );
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
propValues: {
|
|
63
|
+
...propValues,
|
|
64
|
+
_styles: stylePropValues,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
} );
|
|
69
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createElementStyle,
|
|
3
|
+
getElementStyles,
|
|
4
|
+
getWidgetsCache,
|
|
5
|
+
updateElementSettings,
|
|
6
|
+
updateElementStyle,
|
|
7
|
+
} from '@elementor/editor-elements';
|
|
8
|
+
import {
|
|
9
|
+
getPropSchemaFromCache,
|
|
10
|
+
type PropValue,
|
|
11
|
+
Schema,
|
|
12
|
+
stringPropTypeUtil,
|
|
13
|
+
type TransformablePropValue,
|
|
14
|
+
} from '@elementor/editor-props';
|
|
15
|
+
import { type CustomCss, getStylesSchema } from '@elementor/editor-styles';
|
|
16
|
+
|
|
17
|
+
type OwnParams = {
|
|
18
|
+
elementId: string;
|
|
19
|
+
elementType: string;
|
|
20
|
+
propertyName: string;
|
|
21
|
+
propertyValue: string | PropValue | TransformablePropValue< string, unknown >;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function resolvePropValue( value: unknown, forceKey?: string ): PropValue {
|
|
25
|
+
return Schema.adjustLlmPropValueSchema( value as PropValue, forceKey );
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const doUpdateElementProperty = ( params: OwnParams ) => {
|
|
29
|
+
const { elementId, propertyName, propertyValue, elementType } = params;
|
|
30
|
+
|
|
31
|
+
if ( propertyName === '_styles' ) {
|
|
32
|
+
const elementStyles = getElementStyles( elementId ) || {};
|
|
33
|
+
const propertyMapValue = propertyValue as Record< string, PropValue >;
|
|
34
|
+
const styleSchema = getStylesSchema();
|
|
35
|
+
const transformedStyleValues = Object.fromEntries(
|
|
36
|
+
Object.entries( propertyMapValue ).map( ( [ key, val ] ) => {
|
|
37
|
+
if ( key === 'custom_css' ) {
|
|
38
|
+
return [ key, val ];
|
|
39
|
+
}
|
|
40
|
+
const { key: propKey, kind } = styleSchema?.[ key ] || {};
|
|
41
|
+
if ( ! propKey && kind !== 'union' ) {
|
|
42
|
+
throw new Error( `_styles property ${ key } is not supported.` );
|
|
43
|
+
}
|
|
44
|
+
return [ key, resolvePropValue( val, propKey ) ];
|
|
45
|
+
} )
|
|
46
|
+
);
|
|
47
|
+
let customCss: CustomCss | undefined;
|
|
48
|
+
Object.keys( propertyMapValue as Record< string, unknown > ).forEach( ( stylePropName ) => {
|
|
49
|
+
const propertyRawSchema = styleSchema[ stylePropName ];
|
|
50
|
+
if ( stylePropName === 'custom_css' ) {
|
|
51
|
+
let customCssValue = propertyMapValue[ stylePropName ] as object | string;
|
|
52
|
+
if ( typeof customCssValue === 'object' ) {
|
|
53
|
+
customCssValue =
|
|
54
|
+
stringPropTypeUtil.extract( customCssValue ) ||
|
|
55
|
+
( customCssValue as { value: unknown } )?.value ||
|
|
56
|
+
'';
|
|
57
|
+
}
|
|
58
|
+
customCss = {
|
|
59
|
+
raw: btoa( customCssValue as string ),
|
|
60
|
+
};
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const isSupported = !! propertyRawSchema;
|
|
64
|
+
if ( ! isSupported ) {
|
|
65
|
+
throw new Error( `_styles property ${ stylePropName } is not supported.` );
|
|
66
|
+
}
|
|
67
|
+
if ( propertyRawSchema.kind === 'plain' ) {
|
|
68
|
+
if ( typeof ( propertyMapValue as Record< string, unknown > )[ stylePropName ] !== 'object' ) {
|
|
69
|
+
const propUtil = getPropSchemaFromCache( propertyRawSchema.key );
|
|
70
|
+
if ( propUtil ) {
|
|
71
|
+
const plainValue = propUtil.create( propertyMapValue[ stylePropName ] );
|
|
72
|
+
propertyMapValue[ stylePropName ] = plainValue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} );
|
|
77
|
+
const localStyle = Object.values( elementStyles ).find( ( style ) => style.label === 'local' );
|
|
78
|
+
if ( ! localStyle ) {
|
|
79
|
+
createElementStyle( {
|
|
80
|
+
elementId,
|
|
81
|
+
...( typeof customCss !== 'undefined' ? { custom_css: customCss } : {} ),
|
|
82
|
+
classesProp: 'classes',
|
|
83
|
+
label: 'local',
|
|
84
|
+
meta: {
|
|
85
|
+
breakpoint: 'desktop',
|
|
86
|
+
state: null,
|
|
87
|
+
},
|
|
88
|
+
props: {
|
|
89
|
+
...transformedStyleValues,
|
|
90
|
+
},
|
|
91
|
+
} );
|
|
92
|
+
} else {
|
|
93
|
+
updateElementStyle( {
|
|
94
|
+
elementId,
|
|
95
|
+
styleId: localStyle.id,
|
|
96
|
+
meta: {
|
|
97
|
+
breakpoint: 'desktop',
|
|
98
|
+
state: null,
|
|
99
|
+
},
|
|
100
|
+
...( typeof customCss !== 'undefined' ? { custom_css: customCss } : {} ),
|
|
101
|
+
props: {
|
|
102
|
+
...transformedStyleValues,
|
|
103
|
+
},
|
|
104
|
+
} );
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const elementPropSchema = getWidgetsCache()?.[ elementType ]?.atomic_props_schema;
|
|
110
|
+
if ( ! elementPropSchema ) {
|
|
111
|
+
throw new Error( `No prop schema found for element type: ${ elementType }` );
|
|
112
|
+
}
|
|
113
|
+
if ( ! elementPropSchema[ propertyName ] ) {
|
|
114
|
+
const propertyNames = Object.keys( elementPropSchema );
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Property "${ propertyName }" does not exist on element type "${ elementType }". Available properties are: ${ propertyNames.join(
|
|
117
|
+
', '
|
|
118
|
+
) }`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const value = resolvePropValue( propertyValue );
|
|
122
|
+
updateElementSettings( {
|
|
123
|
+
id: elementId,
|
|
124
|
+
props: {
|
|
125
|
+
[ propertyName ]: value,
|
|
126
|
+
},
|
|
127
|
+
withHistory: false,
|
|
128
|
+
} );
|
|
129
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getWidgetsCache, type V1ElementConfig } from '@elementor/editor-elements';
|
|
2
|
+
|
|
3
|
+
type ElTypedElementConfig = V1ElementConfig< {
|
|
4
|
+
elType?: string;
|
|
5
|
+
} >;
|
|
6
|
+
|
|
7
|
+
export const generateAvailableTags = () => {
|
|
8
|
+
const cache = getWidgetsCache< ElTypedElementConfig >();
|
|
9
|
+
if ( ! cache ) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const customTags = Object.entries( cache )
|
|
13
|
+
.filter( ( [ , widgetData ] ) => !! widgetData.atomic_controls )
|
|
14
|
+
.map( ( [ widgetType, widgetData ] ) => {
|
|
15
|
+
const configurationSchema = widgetData; //getElementSchemaAsJsonSchema( widgetType );
|
|
16
|
+
return {
|
|
17
|
+
tag: `${ widgetType }`,
|
|
18
|
+
description: widgetData.title || widgetData.elType || `A ${ widgetType } element`,
|
|
19
|
+
configurationSchema: JSON.stringify( configurationSchema ),
|
|
20
|
+
};
|
|
21
|
+
} );
|
|
22
|
+
return customTags;
|
|
23
|
+
};
|
|
@@ -5,11 +5,13 @@ exports[`renderStyles should render styles 1`] = `
|
|
|
5
5
|
{
|
|
6
6
|
"breakpoint": "desktop",
|
|
7
7
|
"id": "test",
|
|
8
|
+
"state": null,
|
|
8
9
|
"value": ".test{font-size:10px;}.test:hover{font-size:20px;}@media(max-width:992px){.test{font-size:30px;}}@media(max-width:768px){.test:focus{font-size:40px;}}",
|
|
9
10
|
},
|
|
10
11
|
{
|
|
11
12
|
"breakpoint": "desktop",
|
|
12
13
|
"id": "test-2",
|
|
14
|
+
"state": null,
|
|
13
15
|
"value": ".custom-name{font-size:50px;}",
|
|
14
16
|
},
|
|
15
17
|
]
|
|
@@ -112,6 +112,7 @@ describe( 'renderStyles', () => {
|
|
|
112
112
|
breakpoint: 'desktop',
|
|
113
113
|
id: 'test',
|
|
114
114
|
value: '.elementor-prefix .test{font-size:24px;}',
|
|
115
|
+
state: null,
|
|
115
116
|
},
|
|
116
117
|
] );
|
|
117
118
|
} );
|
|
@@ -200,4 +201,28 @@ describe( 'custom_css rendering', () => {
|
|
|
200
201
|
// Assert.
|
|
201
202
|
expect( result[ 0 ].value ).toContain( css );
|
|
202
203
|
} );
|
|
204
|
+
|
|
205
|
+
it( 'should render class state with selector', async () => {
|
|
206
|
+
// Arrange.
|
|
207
|
+
const styleDef: RendererStyleDefinition = {
|
|
208
|
+
id: 'test',
|
|
209
|
+
type: 'class',
|
|
210
|
+
cssName: 'test',
|
|
211
|
+
label: 'Test',
|
|
212
|
+
variants: [
|
|
213
|
+
{
|
|
214
|
+
meta: { breakpoint: null, state: 'e--selected' },
|
|
215
|
+
props: {},
|
|
216
|
+
custom_css: null,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Act.
|
|
222
|
+
const renderStyles = createStylesRenderer( { breakpoints: {} as BreakpointsMap, resolve: async () => ( {} ) } );
|
|
223
|
+
const result = await renderStyles( { styles: [ styleDef ] } );
|
|
224
|
+
|
|
225
|
+
// Assert.
|
|
226
|
+
expect( result[ 0 ].value ).toContain( '.test.e--selected{}' );
|
|
227
|
+
} );
|
|
203
228
|
} );
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isTransformable,
|
|
3
|
+
migratePropValue,
|
|
3
4
|
type PropKey,
|
|
4
5
|
type Props,
|
|
5
6
|
type PropsSchema,
|
|
@@ -44,7 +45,7 @@ export function createPropsResolver( { transformers, schema: initialSchema, onPr
|
|
|
44
45
|
Object.entries( schema ).map( async ( [ key, type ] ) => {
|
|
45
46
|
const value = props[ key ] ?? type.default;
|
|
46
47
|
|
|
47
|
-
const transformed = await transform( { value, key, type, signal } );
|
|
48
|
+
const transformed = ( await transform( { value, key, type, signal } ) ) as PropValue;
|
|
48
49
|
|
|
49
50
|
onPropResolve?.( { key, value: transformed } );
|
|
50
51
|
|
|
@@ -76,6 +77,12 @@ export function createPropsResolver( { transformers, schema: initialSchema, onPr
|
|
|
76
77
|
return null;
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
value = migratePropValue( value, type );
|
|
81
|
+
|
|
82
|
+
if ( ! isTransformable( value ) ) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
if ( type.kind === 'union' ) {
|
|
80
87
|
type = type.prop_types[ value.$$type ];
|
|
81
88
|
|