@elementor/editor-canvas 3.33.0-99 → 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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { V1ElementConfig } from '@elementor/editor-elements';
|
|
2
2
|
|
|
3
3
|
import { type DomRenderer } from '../renderers/create-dom-renderer';
|
|
4
|
-
import { createPropsResolver
|
|
4
|
+
import { createPropsResolver } from '../renderers/create-props-resolver';
|
|
5
5
|
import { settingsTransformersRegistry } from '../settings-transformers-registry';
|
|
6
6
|
import { signalizedProcess } from '../utils/signalized-process';
|
|
7
7
|
import { createElementViewClassDeclaration } from './create-element-type';
|
|
8
8
|
import { type ElementType, type ElementView, type LegacyWindow } from './types';
|
|
9
9
|
|
|
10
|
-
type
|
|
10
|
+
export type CreateTemplatedElementTypeOptions = {
|
|
11
11
|
type: string;
|
|
12
12
|
renderer: DomRenderer;
|
|
13
13
|
element: TemplatedElementConfig;
|
|
@@ -17,30 +17,23 @@ type TemplatedElementConfig = Required<
|
|
|
17
17
|
Pick< V1ElementConfig, 'twig_templates' | 'twig_main_template' | 'atomic_props_schema' | 'base_styles_dictionary' >
|
|
18
18
|
>;
|
|
19
19
|
|
|
20
|
-
export function createTemplatedElementType( {
|
|
20
|
+
export function createTemplatedElementType( {
|
|
21
|
+
type,
|
|
22
|
+
renderer,
|
|
23
|
+
element,
|
|
24
|
+
}: CreateTemplatedElementTypeOptions ): typeof ElementType {
|
|
21
25
|
const legacyWindow = window as unknown as LegacyWindow;
|
|
22
26
|
|
|
23
|
-
Object.entries( element.twig_templates ).forEach( ( [ key, template ] ) => {
|
|
24
|
-
renderer.register( key, template );
|
|
25
|
-
} );
|
|
26
|
-
|
|
27
|
-
const propsResolver = createPropsResolver( {
|
|
28
|
-
transformers: settingsTransformersRegistry,
|
|
29
|
-
schema: element.atomic_props_schema,
|
|
30
|
-
} );
|
|
31
|
-
|
|
32
27
|
return class extends legacyWindow.elementor.modules.elements.types.Widget {
|
|
33
28
|
getType() {
|
|
34
29
|
return type;
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
getView() {
|
|
38
|
-
return
|
|
33
|
+
return createTemplatedElementView( {
|
|
39
34
|
type,
|
|
40
35
|
renderer,
|
|
41
|
-
|
|
42
|
-
baseStylesDictionary: element.base_styles_dictionary,
|
|
43
|
-
templateKey: element.twig_main_template,
|
|
36
|
+
element,
|
|
44
37
|
} );
|
|
45
38
|
}
|
|
46
39
|
};
|
|
@@ -55,23 +48,26 @@ export function canBeTemplated( element: Partial< TemplatedElementConfig > ): el
|
|
|
55
48
|
);
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
type: string;
|
|
60
|
-
renderer: DomRenderer;
|
|
61
|
-
propsResolver: PropsResolver;
|
|
62
|
-
templateKey: string;
|
|
63
|
-
baseStylesDictionary: Record< string, string >;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
function createTemplatedElementViewClassDeclaration( {
|
|
51
|
+
export function createTemplatedElementView( {
|
|
67
52
|
type,
|
|
68
53
|
renderer,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
baseStylesDictionary,
|
|
72
|
-
}: CreateViewOptions ): typeof ElementView {
|
|
54
|
+
element,
|
|
55
|
+
}: CreateTemplatedElementTypeOptions ): typeof ElementView {
|
|
73
56
|
const BaseView = createElementViewClassDeclaration();
|
|
74
57
|
|
|
58
|
+
const templateKey = element.twig_main_template;
|
|
59
|
+
|
|
60
|
+
const baseStylesDictionary = element.base_styles_dictionary;
|
|
61
|
+
|
|
62
|
+
Object.entries( element.twig_templates ).forEach( ( [ key, template ] ) => {
|
|
63
|
+
renderer.register( key, template );
|
|
64
|
+
} );
|
|
65
|
+
|
|
66
|
+
const resolveProps = createPropsResolver( {
|
|
67
|
+
transformers: settingsTransformersRegistry,
|
|
68
|
+
schema: element.atomic_props_schema,
|
|
69
|
+
} );
|
|
70
|
+
|
|
75
71
|
return class extends BaseView {
|
|
76
72
|
#abortController: AbortController | null = null;
|
|
77
73
|
|
|
@@ -83,14 +79,28 @@ function createTemplatedElementViewClassDeclaration( {
|
|
|
83
79
|
this.render();
|
|
84
80
|
}
|
|
85
81
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
// Override `render` function to support async `_renderTemplate`
|
|
83
|
+
// Note that `_renderChildren` asynchronity is still NOT supported, so only the parent element rendering can be async
|
|
84
|
+
render() {
|
|
90
85
|
this.#abortController?.abort();
|
|
91
86
|
this.#abortController = new AbortController();
|
|
92
87
|
|
|
93
88
|
const process = signalizedProcess( this.#abortController.signal )
|
|
89
|
+
.then( () => this.#beforeRender() )
|
|
90
|
+
.then( () => this._renderTemplate() )
|
|
91
|
+
.then( () => {
|
|
92
|
+
this._renderChildren();
|
|
93
|
+
this.#afterRender();
|
|
94
|
+
} );
|
|
95
|
+
|
|
96
|
+
return process.execute();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Overriding Marionette original `_renderTemplate` method to inject our renderer.
|
|
100
|
+
async _renderTemplate() {
|
|
101
|
+
this.triggerMethod( 'before:render:template' );
|
|
102
|
+
|
|
103
|
+
const process = signalizedProcess( this.#abortController?.signal as AbortSignal )
|
|
94
104
|
.then( ( _, signal ) => {
|
|
95
105
|
const settings = this.model.get( 'settings' ).toJSON();
|
|
96
106
|
|
|
@@ -99,12 +109,15 @@ function createTemplatedElementViewClassDeclaration( {
|
|
|
99
109
|
signal,
|
|
100
110
|
} );
|
|
101
111
|
} )
|
|
102
|
-
.then( (
|
|
112
|
+
.then( ( settings ) => {
|
|
113
|
+
return this.afterSettingsResolve( settings );
|
|
114
|
+
} )
|
|
115
|
+
.then( async ( settings ) => {
|
|
103
116
|
// Same as the Backend.
|
|
104
117
|
const context = {
|
|
105
118
|
id: this.model.get( 'id' ),
|
|
106
119
|
type,
|
|
107
|
-
settings
|
|
120
|
+
settings,
|
|
108
121
|
base_styles: baseStylesDictionary,
|
|
109
122
|
};
|
|
110
123
|
|
|
@@ -114,18 +127,30 @@ function createTemplatedElementViewClassDeclaration( {
|
|
|
114
127
|
|
|
115
128
|
await process.execute();
|
|
116
129
|
|
|
117
|
-
this
|
|
130
|
+
this.bindUIElements();
|
|
131
|
+
|
|
132
|
+
this.triggerMethod( 'render:template' );
|
|
118
133
|
}
|
|
119
134
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.triggerMethod( 'before:render:template' );
|
|
135
|
+
afterSettingsResolve( settings: { [ key: string ]: unknown } ) {
|
|
136
|
+
return settings;
|
|
123
137
|
}
|
|
124
138
|
|
|
125
|
-
#
|
|
126
|
-
this.
|
|
139
|
+
#beforeRender() {
|
|
140
|
+
this._ensureViewIsIntact();
|
|
127
141
|
|
|
128
|
-
this.
|
|
142
|
+
this._isRendering = true;
|
|
143
|
+
|
|
144
|
+
this.resetChildViewContainer();
|
|
145
|
+
|
|
146
|
+
this.triggerMethod( 'before:render', this );
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#afterRender() {
|
|
150
|
+
this._isRendering = false;
|
|
151
|
+
this.isRendered = true;
|
|
152
|
+
|
|
153
|
+
this.triggerMethod( 'render', this );
|
|
129
154
|
}
|
|
130
155
|
};
|
|
131
156
|
}
|
|
@@ -3,8 +3,24 @@ import { __privateListenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';
|
|
|
3
3
|
|
|
4
4
|
import { createDomRenderer } from '../renderers/create-dom-renderer';
|
|
5
5
|
import { createElementType } from './create-element-type';
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
canBeTemplated,
|
|
8
|
+
createTemplatedElementType,
|
|
9
|
+
type CreateTemplatedElementTypeOptions,
|
|
10
|
+
} from './create-templated-element-type';
|
|
11
|
+
import type { ElementType, LegacyWindow } from './types';
|
|
12
|
+
|
|
13
|
+
type ElementLegacyType = {
|
|
14
|
+
[ key: string ]: ( options: CreateTemplatedElementTypeOptions ) => typeof ElementType;
|
|
15
|
+
};
|
|
16
|
+
export const elementsLegacyTypes: ElementLegacyType = {};
|
|
17
|
+
|
|
18
|
+
export function registerElementType(
|
|
19
|
+
type: string,
|
|
20
|
+
elementTypeGenerator: ElementLegacyType[ keyof ElementLegacyType ]
|
|
21
|
+
) {
|
|
22
|
+
elementsLegacyTypes[ type ] = elementTypeGenerator;
|
|
23
|
+
}
|
|
8
24
|
|
|
9
25
|
export function initLegacyViews() {
|
|
10
26
|
__privateListenTo( v1ReadyEvent(), () => {
|
|
@@ -18,9 +34,15 @@ export function initLegacyViews() {
|
|
|
18
34
|
return;
|
|
19
35
|
}
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
let ElementType;
|
|
38
|
+
|
|
39
|
+
if ( !! elementsLegacyTypes[ type ] && canBeTemplated( element ) ) {
|
|
40
|
+
ElementType = elementsLegacyTypes[ type ]( { type, renderer, element } );
|
|
41
|
+
} else if ( canBeTemplated( element ) ) {
|
|
42
|
+
ElementType = createTemplatedElementType( { type, renderer, element } );
|
|
43
|
+
} else {
|
|
44
|
+
ElementType = createElementType( type );
|
|
45
|
+
}
|
|
24
46
|
|
|
25
47
|
legacyWindow.elementor.elementsManager.registerElementType( new ElementType() );
|
|
26
48
|
} );
|
package/src/legacy/types.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { type Props } from '@elementor/editor-props';
|
|
|
2
2
|
|
|
3
3
|
export type LegacyWindow = Window & {
|
|
4
4
|
elementor: {
|
|
5
|
+
createBackboneElementsCollection: ( children: unknown ) => BackboneCollection< ElementModel >;
|
|
6
|
+
|
|
5
7
|
modules: {
|
|
6
8
|
elements: {
|
|
7
9
|
types: {
|
|
@@ -36,6 +38,15 @@ export declare class ElementView {
|
|
|
36
38
|
|
|
37
39
|
model: BackboneModel< ElementModel >;
|
|
38
40
|
|
|
41
|
+
collection: BackboneCollection< ElementModel >;
|
|
42
|
+
|
|
43
|
+
children: {
|
|
44
|
+
length: number;
|
|
45
|
+
findByIndex: ( index: number ) => ElementView;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
constructor( ...args: unknown[] );
|
|
49
|
+
|
|
39
50
|
onRender( ...args: unknown[] ): void;
|
|
40
51
|
|
|
41
52
|
onDestroy( ...args: unknown[] ): void;
|
|
@@ -61,9 +72,29 @@ export declare class ElementView {
|
|
|
61
72
|
|
|
62
73
|
_renderTemplate(): void;
|
|
63
74
|
|
|
64
|
-
|
|
75
|
+
_renderChildren(): void;
|
|
76
|
+
|
|
77
|
+
attachBuffer( collectionView: this, buffer: DocumentFragment ): void;
|
|
78
|
+
|
|
79
|
+
triggerMethod( method: string, ...args: unknown[] ): void;
|
|
65
80
|
|
|
66
81
|
bindUIElements(): void;
|
|
82
|
+
|
|
83
|
+
_ensureViewIsIntact(): void;
|
|
84
|
+
|
|
85
|
+
_isRendering: boolean;
|
|
86
|
+
|
|
87
|
+
resetChildViewContainer(): void;
|
|
88
|
+
|
|
89
|
+
isRendered: boolean;
|
|
90
|
+
|
|
91
|
+
options?: {
|
|
92
|
+
model: BackboneModel< ElementModel >;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
ui(): Record< string, unknown >;
|
|
96
|
+
|
|
97
|
+
events(): Record< string, unknown >;
|
|
67
98
|
}
|
|
68
99
|
|
|
69
100
|
type JQueryElement = {
|
|
@@ -72,15 +103,24 @@ type JQueryElement = {
|
|
|
72
103
|
get: ( index: number ) => HTMLElement;
|
|
73
104
|
};
|
|
74
105
|
|
|
75
|
-
type BackboneModel< Model extends object > = {
|
|
106
|
+
export type BackboneModel< Model extends object > = {
|
|
76
107
|
get: < T extends keyof Model >( key: T ) => Model[ T ];
|
|
108
|
+
set: < T extends keyof Model >( key: T, value: Model[ T ] ) => void;
|
|
77
109
|
toJSON: () => ToJSON< Model >;
|
|
78
110
|
};
|
|
79
111
|
|
|
80
|
-
type
|
|
112
|
+
type BackboneCollection< Model extends object > = {
|
|
113
|
+
models: BackboneModel< Model >[];
|
|
114
|
+
forEach: ( callback: ( model: BackboneModel< Model > ) => void ) => void;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export type ElementModel = {
|
|
81
118
|
id: string;
|
|
82
119
|
settings: BackboneModel< Props >;
|
|
120
|
+
editor_settings: Record< string, unknown >;
|
|
83
121
|
widgetType: string;
|
|
122
|
+
editSettings?: BackboneModel< { inactive?: boolean } >;
|
|
123
|
+
elements?: BackboneCollection< ElementModel >;
|
|
84
124
|
};
|
|
85
125
|
|
|
86
126
|
type ToJSON< T > = {
|
|
@@ -89,5 +129,5 @@ type ToJSON< T > = {
|
|
|
89
129
|
|
|
90
130
|
type ContextMenuGroup = {
|
|
91
131
|
name: string;
|
|
92
|
-
|
|
132
|
+
actions: unknown[];
|
|
93
133
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type MCPRegistryEntry } from '@elementor/editor-mcp';
|
|
2
|
+
|
|
3
|
+
import { initWidgetsSchemaResource } from './resources/widgets-schema-resource';
|
|
4
|
+
import { initBuildCompositionsTool } from './tools/build-composition/tool';
|
|
5
|
+
import { initConfigureElementTool } from './tools/configure-element/tool';
|
|
6
|
+
import { initGetElementConfigTool } from './tools/get-element-config/tool';
|
|
7
|
+
|
|
8
|
+
export const initCanvasMcp = ( reg: MCPRegistryEntry ) => {
|
|
9
|
+
const { setMCPDescription } = reg;
|
|
10
|
+
setMCPDescription(
|
|
11
|
+
'Everything related to creative design, layout, styling and building the pages, specifically element of type "widget"'
|
|
12
|
+
);
|
|
13
|
+
initWidgetsSchemaResource( reg );
|
|
14
|
+
initBuildCompositionsTool( reg );
|
|
15
|
+
initGetElementConfigTool( reg );
|
|
16
|
+
initConfigureElementTool( reg );
|
|
17
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from './resources/widgets-schema-resource';
|
|
2
|
+
|
|
3
|
+
export const mcpDescription = `Canvas MCP - Working with widgets and styles: how to use the PropType schemas and generate PropValue structures
|
|
4
|
+
|
|
5
|
+
# Elementor's PropValue configuration system
|
|
6
|
+
|
|
7
|
+
Every widget in Elementor has a set of properties that can be configured, defined in a STRICT SCHEMA we call "PropType".
|
|
8
|
+
All widget configuration values are represented using a structure we call "PropValue".
|
|
9
|
+
|
|
10
|
+
To correctly configure a widget's properties, FOLLOW THE PropType schema defined for that widget. This schema outlines the expected structure and types for each property, ensuring that the values you provide are valid and can be properly interpreted by Elementor.
|
|
11
|
+
Every widget has it's own PropType schema, retreivable from the resource [${ WIDGET_SCHEMA_URI }].
|
|
12
|
+
ALL WIDGETS share a common _styles property with a uniform styles schema, retreivable from the resource [${ STYLE_SCHEMA_URI }].
|
|
13
|
+
The style schema is grouped by categories, such as "typography", "background", "border", etc.
|
|
14
|
+
|
|
15
|
+
# Tools and usage
|
|
16
|
+
- Use the "get-element-configuration-values" tool to retrieve the current configuration of a specific element, including its PropValues and styles. It is recommended to use this tool when you are required to make changes to an existing element, to ensure you have the correct current configuration schema.
|
|
17
|
+
If a PropValue changes it's type (only on union PropTypes), read the new schema from the resources mentioned above, and adjust the PropValue structure accordingly.
|
|
18
|
+
- Use the "build-composition" tool to create a NEW ELEMENTS in a composition on the page. You can use this tool to also create a new single element.
|
|
19
|
+
- Use the "configure-element" tool to update the configuration of an EXISTING element on the page.
|
|
20
|
+
|
|
21
|
+
All array types that can receive union types, are typed as mixed array, which means that each item in the array can be of any of the allowed types defined in the PropType schema.
|
|
22
|
+
Example: the "background" can have a background-overlay property, which can contain multiple overlays, such as color, gradient, image, etc. Each item in the array must follow the PropType schema for each overlay type.
|
|
23
|
+
All _style properties are OPTIONAL. When a _style is defined, we MERGE the values with existing styles, so only the properties you define will be changed, and the rest will remain as is.
|
|
24
|
+
|
|
25
|
+
# Styling best practices
|
|
26
|
+
Prefer using "em" and "rem" values for text-related sizes, padding and spacing. Use percentages for dynamic sizing relative to parent containers.
|
|
27
|
+
This flexboxes are by default "flex" with "stretch" alignment. To ensure proper layout, define the "justify-content" and "align-items" as in the schema.
|
|
28
|
+
|
|
29
|
+
Additionaly, some PropTypes have metadata information (meta property) that can help in understaind the PropType usage, such as description or other useful information.
|
|
30
|
+
|
|
31
|
+
Example of null values:
|
|
32
|
+
{
|
|
33
|
+
$$type: 'as-defined-for-propValue',
|
|
34
|
+
value: null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Example of "image" PropValue structure:
|
|
38
|
+
{$$type:'image',value:{src:{$$type:'image-src',value:{url:{$$type:'url',value:'https://example.com/image.jpg'}}},size:{$$type:'string',value:'full'}}}
|
|
39
|
+
|
|
40
|
+
`;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { getWidgetsCache } from '@elementor/editor-elements';
|
|
2
|
+
import { type MCPRegistryEntry, ResourceTemplate } from '@elementor/editor-mcp';
|
|
3
|
+
import {
|
|
4
|
+
type ArrayPropType,
|
|
5
|
+
type ObjectPropType,
|
|
6
|
+
type PropType,
|
|
7
|
+
Schema,
|
|
8
|
+
type TransformablePropType,
|
|
9
|
+
type UnionPropType,
|
|
10
|
+
} from '@elementor/editor-props';
|
|
11
|
+
import { getStylesSchema } from '@elementor/editor-styles';
|
|
12
|
+
|
|
13
|
+
export const WIDGET_SCHEMA_URI = 'elementor://widgets/schema/{widgetType}';
|
|
14
|
+
export const STYLE_SCHEMA_URI = 'elementor://styles/schema/{category}';
|
|
15
|
+
export const BEST_PRACTICES_URI = 'elementor://styles/best-practices';
|
|
16
|
+
|
|
17
|
+
export const initWidgetsSchemaResource = ( reg: MCPRegistryEntry ) => {
|
|
18
|
+
const { mcpServer } = reg;
|
|
19
|
+
|
|
20
|
+
mcpServer.resource( 'styles-best-practices', BEST_PRACTICES_URI, async () => {
|
|
21
|
+
return {
|
|
22
|
+
contents: [
|
|
23
|
+
{
|
|
24
|
+
uri: BEST_PRACTICES_URI,
|
|
25
|
+
text: `# Styling best practices
|
|
26
|
+
Prefer using "em" and "rem" values for text-related sizes, padding and spacing. Use percentages for dynamic sizing relative to parent containers.
|
|
27
|
+
This flexboxes are by default "flex" with "stretch" alignment. To ensure proper layout, define the "justify-content" and "align-items" as in the schema, or in custom_css, depends on your needs.
|
|
28
|
+
|
|
29
|
+
When applicable for styles, use the "custom_css" property for free-form CSS styling. This property accepts a string of CSS rules that will be applied directly to the element.
|
|
30
|
+
The css string must follow standard CSS syntax, with properties and values separated by semicolons, no selectors, or nesting rules allowed.`,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
mcpServer.resource(
|
|
37
|
+
'styles-schema',
|
|
38
|
+
new ResourceTemplate( STYLE_SCHEMA_URI, {
|
|
39
|
+
list: () => {
|
|
40
|
+
const categories = [ ...Object.keys( getStylesSchema() ), 'custom_css' ];
|
|
41
|
+
return {
|
|
42
|
+
resources: categories.map( ( category ) => ( {
|
|
43
|
+
uri: `elementor://styles/schema/${ category }`,
|
|
44
|
+
name: 'Style schema for ' + category,
|
|
45
|
+
} ) ),
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
} ),
|
|
49
|
+
{
|
|
50
|
+
description: 'Common styles schema for the specified category',
|
|
51
|
+
},
|
|
52
|
+
async ( uri, variables ) => {
|
|
53
|
+
const category = typeof variables.category === 'string' ? variables.category : variables.category?.[ 0 ];
|
|
54
|
+
if ( category === 'custom_css' ) {
|
|
55
|
+
return {
|
|
56
|
+
contents: [
|
|
57
|
+
{
|
|
58
|
+
uri: uri.toString(),
|
|
59
|
+
text: 'Free style inline CSS string of properties and their values. Applicable for a single element, only the properties and values are accepted. Use this as a last resort for properties that are not covered with the schema.',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const stylesSchema = getStylesSchema()[ category ];
|
|
65
|
+
if ( ! stylesSchema ) {
|
|
66
|
+
throw new Error( `No styles schema found for category: ${ category }` );
|
|
67
|
+
}
|
|
68
|
+
const cleanedupPropSchema = cleanupPropType( stylesSchema );
|
|
69
|
+
const asJson = Schema.propTypeToJsonSchema( cleanedupPropSchema as PropType );
|
|
70
|
+
return {
|
|
71
|
+
contents: [
|
|
72
|
+
{
|
|
73
|
+
uri: uri.toString(),
|
|
74
|
+
text: JSON.stringify( asJson ),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
mcpServer.resource(
|
|
82
|
+
'widget-schema-by-type',
|
|
83
|
+
new ResourceTemplate( WIDGET_SCHEMA_URI, {
|
|
84
|
+
list: () => {
|
|
85
|
+
const cache = getWidgetsCache() || {};
|
|
86
|
+
const availableWidgets = Object.keys( cache || {} ).filter(
|
|
87
|
+
( widgetType ) => cache[ widgetType ]?.atomic_props_schema
|
|
88
|
+
);
|
|
89
|
+
return {
|
|
90
|
+
resources: availableWidgets.map( ( widgetType ) => ( {
|
|
91
|
+
uri: `elementor://widgets/schema/${ widgetType }`,
|
|
92
|
+
name: 'Widget schema for ' + widgetType,
|
|
93
|
+
} ) ),
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
} ),
|
|
97
|
+
{
|
|
98
|
+
description: 'PropType schema for the specified widget type',
|
|
99
|
+
},
|
|
100
|
+
async ( uri, variables ) => {
|
|
101
|
+
const widgetType =
|
|
102
|
+
typeof variables.widgetType === 'string' ? variables.widgetType : variables.widgetType?.[ 0 ];
|
|
103
|
+
const propSchema = getWidgetsCache()?.[ widgetType ]?.atomic_props_schema;
|
|
104
|
+
if ( ! propSchema ) {
|
|
105
|
+
throw new Error( `No prop schema found for element type: ${ widgetType }` );
|
|
106
|
+
}
|
|
107
|
+
const cleanedupPropSchema = cleanupPropSchema( propSchema );
|
|
108
|
+
const asJson = Object.fromEntries(
|
|
109
|
+
Object.entries( cleanedupPropSchema ).map( ( [ key, propType ] ) => [
|
|
110
|
+
key,
|
|
111
|
+
Schema.propTypeToJsonSchema( propType ),
|
|
112
|
+
] )
|
|
113
|
+
);
|
|
114
|
+
Schema.nonConfigurablePropKeys.forEach( ( key ) => {
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
116
|
+
delete asJson[ key ];
|
|
117
|
+
} );
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
contents: [
|
|
121
|
+
{
|
|
122
|
+
uri: uri.toString(),
|
|
123
|
+
text: JSON.stringify( asJson ),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
function cleanupPropSchema( propSchema: Record< string, PropType > ): Record< string, PropType > {
|
|
132
|
+
const result: Record< string, Partial< PropType > > = {};
|
|
133
|
+
Object.keys( propSchema ).forEach( ( propName ) => {
|
|
134
|
+
result[ propName ] = cleanupPropType( propSchema[ propName ] );
|
|
135
|
+
} );
|
|
136
|
+
return result as Record< string, PropType >;
|
|
137
|
+
}
|
|
138
|
+
function cleanupPropType( propType: PropType & { key?: string } ): Partial< PropType > {
|
|
139
|
+
const result: Partial< PropType > = {};
|
|
140
|
+
Object.keys( propType ).forEach( ( property ) => {
|
|
141
|
+
switch ( property ) {
|
|
142
|
+
case 'key':
|
|
143
|
+
case 'kind':
|
|
144
|
+
( result as Record< string, unknown > )[ property ] = propType[ property ];
|
|
145
|
+
break;
|
|
146
|
+
case 'meta':
|
|
147
|
+
case 'settings':
|
|
148
|
+
{
|
|
149
|
+
if ( Object.keys( propType[ property ] || {} ).length > 0 ) {
|
|
150
|
+
( result as Record< string, unknown > )[ property ] = propType[ property ];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
} );
|
|
156
|
+
if ( result.kind === 'plain' ) {
|
|
157
|
+
Object.defineProperty( result, 'kind', { value: 'string' } );
|
|
158
|
+
} else if ( result.kind === 'array' ) {
|
|
159
|
+
result.item_prop_type = cleanupPropType( ( propType as ArrayPropType ).item_prop_type ) as PropType;
|
|
160
|
+
} else if ( result.kind === 'object' ) {
|
|
161
|
+
const shape = ( propType as ObjectPropType ).shape as Record< string, PropType >;
|
|
162
|
+
const cleanedShape = cleanupPropSchema( shape );
|
|
163
|
+
result.shape = cleanedShape;
|
|
164
|
+
} else if ( result.kind === 'union' ) {
|
|
165
|
+
const propTypes = ( propType as UnionPropType ).prop_types;
|
|
166
|
+
const cleanedPropTypes: Record< string, Partial< PropType > > = {};
|
|
167
|
+
Object.keys( propTypes ).forEach( ( key ) => {
|
|
168
|
+
cleanedPropTypes[ key ] = cleanupPropType( propTypes[ key ] );
|
|
169
|
+
} );
|
|
170
|
+
result.prop_types = cleanedPropTypes as Record< string, TransformablePropType >;
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { toolPrompts } from '@elementor/editor-mcp';
|
|
2
|
+
|
|
3
|
+
import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
|
|
4
|
+
|
|
5
|
+
export const generatePrompt = () => {
|
|
6
|
+
const buildCompositionsToolPrompt = toolPrompts( 'build-compositions' );
|
|
7
|
+
|
|
8
|
+
buildCompositionsToolPrompt.description( `
|
|
9
|
+
Build entire elementor widget comositions representing complex structures of nested elements.
|
|
10
|
+
|
|
11
|
+
# When to use this tool
|
|
12
|
+
Always prefer this tool when the user requires to build a composition of elements, such as cards, heros, or inspired from other pages or HTML compositions.
|
|
13
|
+
Prefer this tool over any other tool for building HTML structure, unless you are specified to use a different tool.
|
|
14
|
+
|
|
15
|
+
# **CRITICAL - REQUIRED RESOURCES (Must read before using this tool)**
|
|
16
|
+
1. [${ WIDGET_SCHEMA_URI }]
|
|
17
|
+
Required to understand which widgets are available, and what are their configuration schemas.
|
|
18
|
+
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.
|
|
19
|
+
2. [${ STYLE_SCHEMA_URI }]
|
|
20
|
+
Required to understand the styles schema for the widgets. All widgets share the same styles schema.
|
|
21
|
+
3. List of allowed custom tags for building the structure is derived from the list of widgets schema resources.
|
|
22
|
+
|
|
23
|
+
# Instructions
|
|
24
|
+
1. Understand the user requirements carefully.
|
|
25
|
+
2. Build a valid XML structure using only the allowed custom tags provided. For example, if you
|
|
26
|
+
use the "e-button" element, it would be represented as <e-button></e-button> in the XML structure.
|
|
27
|
+
3. Plan the configuration for each element according to the user requirements, using the configuration schema provided for each custom tag.
|
|
28
|
+
Every widget type has it's own configuration schema, retreivable from the resource [${ WIDGET_SCHEMA_URI }].
|
|
29
|
+
PropValues must follow the exact PropType schema provided in the resource.
|
|
30
|
+
4. For every element, provide a "configuration-id" attribute. For example:
|
|
31
|
+
\`<e-flexbox configuration-id="flex1"><e-heading configuration-id="heading2"></e-heading></e-flexbox>\`
|
|
32
|
+
In the elementConfig property, provide the actual configuration object for each configuration-id used in the XML structure.
|
|
33
|
+
In the stylesConfig property, provide the actual styles configuration object for each configuration-id used in the XML structure.
|
|
34
|
+
5. Ensure the XML structure is valid and parsable.
|
|
35
|
+
6. Do not add any attribute nodes, classes, id's, and no text nodes allowed.
|
|
36
|
+
Layout properties, such as margin, padding, align, etc. must be applied using the [${ STYLE_SCHEMA_URI }] PropValues.
|
|
37
|
+
7. Some elements allow nesting of other elements, and most of the DO NOT. The allowed elements that can have nested children are "e-div-block" and "e-flexbox".
|
|
38
|
+
8. Make sure that non-container elements do NOT have any nested elements.
|
|
39
|
+
9. Unsless the user specifically requires structure only, BE EXPRESSIVE AND VISUALLY CREATIVE AS POSSIBLE IN APPLYING STYLE CONFIGURATION.
|
|
40
|
+
In the case of doubt, prefer adding more styles to make the composition visually appealing.
|
|
41
|
+
|
|
42
|
+
# Additional Guidelines
|
|
43
|
+
- Most users expect the structure to be well designed and visually appealing.
|
|
44
|
+
- Use layout properties, ensure "white space" design approach is followed, and make sure the composition is visually balanced.
|
|
45
|
+
- Use appropriate spacing, alignment, and sizing to create a harmonious layout.
|
|
46
|
+
- Consider the visual hierarchy of elements to guide the user's attention effectively.
|
|
47
|
+
- You are encouraged to use colors, typography, and other style properties to enhance the visual appeal, as long as they are part of the configuration schema for the elements used.
|
|
48
|
+
- Always aim for a clean and professional look that aligns with modern design principles.
|
|
49
|
+
- When you are required to create placeholder texts, use texts that have a length that fits the goal. When long texts are required, use longer placeholder texts. When the user specifies exact texts, use the exact texts.
|
|
50
|
+
- Image size does not affect the actual size on the screen, only which quality to use. If you use images, specifically add _styles PropValues to define the image sizes.
|
|
51
|
+
- Attempt to use layout, margin, padding, size properties from the styles schema.
|
|
52
|
+
- If your elements library is limited, encourage use of nesting containers to achieve complex layouts.
|
|
53
|
+
|
|
54
|
+
# CONSTRAINTS
|
|
55
|
+
When a tool execution fails, retry up to 10 more times, read the error message carefully, and adjust the XML structure or the configurations accordingly.
|
|
56
|
+
If a "$$type" is missing, update the invalid object, if the XML has parsing errors, fix it, etc. and RETRY.
|
|
57
|
+
VALIDATE the XML structure before delivering it as the final result.
|
|
58
|
+
VALIDATE the JSON structure used in the "configuration" attributes for each element before delivering the final result. The configuration must MATCH the PropValue schemas.
|
|
59
|
+
NO LINKS ALLOWED. Never apply links to elements, even if they appear in the PropType schema.
|
|
60
|
+
elementConfig values must align with the widget's PropType schema, available at the resource [${ WIDGET_SCHEMA_URI }].
|
|
61
|
+
stylesConfig values must align with the common styles PropType schema, available at the resource [${ STYLE_SCHEMA_URI }].
|
|
62
|
+
|
|
63
|
+
# Parameters
|
|
64
|
+
All parameters are MANDATORY.
|
|
65
|
+
- xmlStructure
|
|
66
|
+
- elementConfig
|
|
67
|
+
- stylesConfig
|
|
68
|
+
|
|
69
|
+
If unsure about the configuration of a specific property, read the schema resources carefully.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
` );
|
|
73
|
+
|
|
74
|
+
buildCompositionsToolPrompt.example( `
|
|
75
|
+
A Heading and a button inside a flexbox
|
|
76
|
+
{
|
|
77
|
+
xmlStructure: "<e-flexbox configuration-id="flex1"><e-heading configuration-id="heading1"></e-heading><e-button configuration-id="button1"></e-button></e-flexbox>"
|
|
78
|
+
elementConfig: {
|
|
79
|
+
"flex1": {
|
|
80
|
+
"tag": {
|
|
81
|
+
"$$type": "string",
|
|
82
|
+
"value": "section"
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
stylesConfig: {
|
|
86
|
+
"heading1": {
|
|
87
|
+
"font-size": {
|
|
88
|
+
"$$type": "size",
|
|
89
|
+
"value": {
|
|
90
|
+
"size": { "$$type": "number", "value": 24 },
|
|
91
|
+
"unit": { "$$type": "string", "value": "px" }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"color": {
|
|
95
|
+
"$$type": "color",
|
|
96
|
+
"value": { "$$type": "string", "value": "#333" }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
` );
|
|
102
|
+
|
|
103
|
+
buildCompositionsToolPrompt.parameter(
|
|
104
|
+
'xmlStructure',
|
|
105
|
+
`**MANDATORY** A valid XML structure representing the composition to be built, using custom elementor tags, styling and configuration PropValues.`
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
buildCompositionsToolPrompt.parameter(
|
|
109
|
+
'elementConfig',
|
|
110
|
+
`**MANDATORY** A record mapping configuration IDs to their corresponding configuration objects, defining the PropValues for each element created.`
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
buildCompositionsToolPrompt.parameter(
|
|
114
|
+
'stylesConfig',
|
|
115
|
+
`**MANDATORY** A record mapping style PropTypes to their corresponding style configuration objects, defining the PropValues for styles to be applied to elements.`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
buildCompositionsToolPrompt.instruction(
|
|
119
|
+
`You will be provided the XML structure with element IDs. These IDs represent the actual elementor widgets created on the page/post.
|
|
120
|
+
You should use these IDs as reference for further configuration, styling or changing elements later on.`
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
buildCompositionsToolPrompt.instruction(
|
|
124
|
+
`You must use styles/variables/classes that are available in the project resources, you should prefer using them over inline styles, and you are welcome to execute relevant tools AFTER this tool execution, to apply global classes to the created elements.`
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return buildCompositionsToolPrompt.prompt();
|
|
128
|
+
};
|