@elementor/editor-canvas 4.2.0-beta2 → 4.3.0-1001
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +449 -1319
- package/dist/index.mjs +314 -1197
- package/package.json +22 -22
- package/src/index.ts +1 -1
- package/src/legacy/__tests__/init-legacy-views.test.ts +1 -1
- package/src/mcp/canvas-mcp.ts +4 -2
- package/src/mcp/resources/__tests__/available-widgets-resource.test.ts +85 -0
- package/src/mcp/resources/__tests__/dynamic-tags-resource.test.ts +27 -37
- package/src/mcp/resources/__tests__/widgets-schema-resource.test.ts +82 -74
- package/src/mcp/resources/available-widgets-resource.ts +35 -41
- package/src/mcp/resources/best-practices-resource.ts +34 -0
- package/src/mcp/resources/dynamic-tags-resource.ts +28 -29
- package/src/mcp/resources/widgets-schema-resource.ts +37 -177
- package/src/mcp/tools/build-composition/tool.ts +107 -218
- package/src/sync/element-added-event.ts +8 -0
- package/src/utils/__tests__/grid-outline-utils.test.ts +39 -0
- package/src/utils/grid-outline-utils.ts +13 -2
- package/src/composition-builder/__tests__/composition-builder.test.ts +0 -352
- package/src/composition-builder/composition-builder.ts +0 -350
- package/src/composition-builder/utils/__tests__/required-children-enforcer.test.ts +0 -79
- package/src/composition-builder/utils/__tests__/required-default-child-tags.test.ts +0 -35
- package/src/composition-builder/utils/required-children-enforcer.ts +0 -56
- package/src/composition-builder/utils/required-default-child-tags.ts +0 -22
- package/src/mcp/resources/best-practices.ts +0 -159
- package/src/mcp/resources/build-llm-guidance.ts +0 -110
- package/src/mcp/tools/build-composition/__tests__/xml-leaf-wrapper.test.ts +0 -117
- package/src/mcp/tools/build-composition/prompt.ts +0 -167
- package/src/mcp/tools/build-composition/schema.ts +0 -42
- package/src/mcp/tools/build-composition/xml-leaf-wrapper.ts +0 -68
- package/src/mcp/utils/__tests__/get-composition-target-container.test.ts +0 -59
- package/src/mcp/utils/get-composition-target-container.ts +0 -15
package/dist/index.mjs
CHANGED
|
@@ -2,205 +2,37 @@
|
|
|
2
2
|
import { v1ReadyEvent } from "@elementor/editor-v1-adapters";
|
|
3
3
|
|
|
4
4
|
// src/mcp/resources/widgets-schema-resource.ts
|
|
5
|
-
import { getWidgetsCache as getWidgetsCache2 } from "@elementor/editor-elements";
|
|
6
5
|
import { ResourceTemplate } from "@elementor/editor-mcp";
|
|
7
|
-
import {
|
|
8
|
-
Schema
|
|
9
|
-
} from "@elementor/editor-props";
|
|
10
|
-
|
|
11
|
-
// src/mcp/utils/element-data-util.ts
|
|
12
|
-
import { getWidgetsCache } from "@elementor/editor-elements";
|
|
13
|
-
function hasV3Controls(controls) {
|
|
14
|
-
return typeof controls === "object" && controls !== null && Object.keys(controls).length > 0;
|
|
15
|
-
}
|
|
16
|
-
function isWidgetAvailableForLLM(config) {
|
|
17
|
-
if (!config) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
if (config.meta?.llm_support === false) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
if (config.title === "Component") {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
if (config.atomic_props_schema) {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
return hasV3Controls(config.controls);
|
|
30
|
-
}
|
|
31
|
-
function getWidgetVersion(config) {
|
|
32
|
-
return config?.atomic_props_schema ? "v4" : "v3";
|
|
33
|
-
}
|
|
34
|
-
function getAvailableWidgets() {
|
|
35
|
-
const cache = getWidgetsCache() ?? {};
|
|
36
|
-
return Object.keys(cache).filter((widgetType) => isWidgetAvailableForLLM(cache[widgetType])).sort().map((widgetType) => {
|
|
37
|
-
const config = cache[widgetType];
|
|
38
|
-
const description = typeof config?.meta?.description === "string" ? config.meta.description : void 0;
|
|
39
|
-
return {
|
|
40
|
-
type: widgetType,
|
|
41
|
-
version: getWidgetVersion(config),
|
|
42
|
-
...description && { description }
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/composition-builder/utils/required-default-child-tags.ts
|
|
48
|
-
function getRequiredDefaultChildTemplates(elementConfig) {
|
|
49
|
-
const defaultChildren = elementConfig?.default_children;
|
|
50
|
-
if (!Array.isArray(defaultChildren)) {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
return defaultChildren.filter((child) => child?.meta?.required ?? false);
|
|
54
|
-
}
|
|
55
|
-
function getRequiredDefaultChildTypes(elementConfig) {
|
|
56
|
-
return getRequiredDefaultChildTemplates(elementConfig).map((child) => child.widgetType ?? child.elType ?? "").filter((type) => Boolean(type));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// src/mcp/resources/build-llm-guidance.ts
|
|
60
|
-
var DEFAULT_STYLES_INSTRUCTION = "These are the default styles applied to the widget. Override only when necessary.";
|
|
61
|
-
var DEFAULT_SETTINGS_INSTRUCTION = "These are the default settings applied to the widget. Omit them from elementConfig unless the user explicitly asks to change them.";
|
|
62
|
-
var BASE_SETTING_PROP_HINT = "Has a widget default \u2014 omit unless user explicitly requests a change. See llm_guidance.default_settings.";
|
|
63
|
-
function mergeInstructions(existing, additional) {
|
|
64
|
-
if (typeof existing === "string" && existing.length > 0) {
|
|
65
|
-
return `${existing} ${additional}`;
|
|
66
|
-
}
|
|
67
|
-
return additional;
|
|
68
|
-
}
|
|
69
|
-
function enrichPropertiesWithBaseSettingsHints(properties, baseSettingsKeys) {
|
|
70
|
-
if (!baseSettingsKeys.length) {
|
|
71
|
-
return properties;
|
|
72
|
-
}
|
|
73
|
-
const enriched = { ...properties };
|
|
74
|
-
for (const key of baseSettingsKeys) {
|
|
75
|
-
const propSchema = enriched[key];
|
|
76
|
-
if (!propSchema) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
enriched[key] = {
|
|
80
|
-
...propSchema,
|
|
81
|
-
description: propSchema.description ? `${propSchema.description} ${BASE_SETTING_PROP_HINT}` : BASE_SETTING_PROP_HINT
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return enriched;
|
|
85
|
-
}
|
|
86
|
-
function buildLlmGuidance(widgetData, widgetType, allWidgets) {
|
|
87
|
-
const defaultStyles = {};
|
|
88
|
-
const baseStyleSchema = widgetData?.base_styles;
|
|
89
|
-
if (baseStyleSchema) {
|
|
90
|
-
Object.values(baseStyleSchema).forEach((stylePropType) => {
|
|
91
|
-
stylePropType.variants.forEach((variant) => {
|
|
92
|
-
Object.assign(defaultStyles, variant.props);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
const baseSettings = widgetData?.base_settings ?? {};
|
|
97
|
-
const hasDefaultStyles = Object.keys(defaultStyles).length > 0;
|
|
98
|
-
const hasDefaultSettings = Object.keys(baseSettings).length > 0;
|
|
99
|
-
const llmGuidance = {
|
|
100
|
-
can_have_children: !!widgetData?.meta?.is_container
|
|
101
|
-
};
|
|
102
|
-
if (hasDefaultStyles) {
|
|
103
|
-
llmGuidance.instructions = DEFAULT_STYLES_INSTRUCTION;
|
|
104
|
-
llmGuidance.default_styles = defaultStyles;
|
|
105
|
-
}
|
|
106
|
-
if (hasDefaultSettings) {
|
|
107
|
-
llmGuidance.instructions = mergeInstructions(llmGuidance.instructions, DEFAULT_SETTINGS_INSTRUCTION);
|
|
108
|
-
llmGuidance.default_settings = baseSettings;
|
|
109
|
-
}
|
|
110
|
-
const allowedChildTypes = widgetData.allowed_child_types;
|
|
111
|
-
const allowedParents = Object.entries(allWidgets).filter(([, parentConfig]) => parentConfig.allowed_child_types?.includes(widgetType)).map(([parentType]) => parentType);
|
|
112
|
-
if (allowedChildTypes?.length || allowedParents.length) {
|
|
113
|
-
llmGuidance.nesting = {
|
|
114
|
-
...allowedChildTypes?.length ? { allowed_child_types: allowedChildTypes } : {},
|
|
115
|
-
...allowedParents.length ? { allowed_parents: allowedParents } : {}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
const requiredDirectChildTags = getRequiredDefaultChildTypes(widgetData);
|
|
119
|
-
if (requiredDirectChildTags.length) {
|
|
120
|
-
llmGuidance.required_direct_children = requiredDirectChildTags;
|
|
121
|
-
}
|
|
122
|
-
return llmGuidance;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// src/mcp/resources/widgets-schema-resource.ts
|
|
126
|
-
var V3_LAYOUT_CONTROL_TYPES = /* @__PURE__ */ new Set(["section", "tab", "tabs"]);
|
|
127
|
-
function extractV3ControlsMetadata(controls) {
|
|
128
|
-
if (!hasV3Controls(controls)) {
|
|
129
|
-
return {};
|
|
130
|
-
}
|
|
131
|
-
const result = {};
|
|
132
|
-
for (const [controlKey, raw] of Object.entries(controls)) {
|
|
133
|
-
if (!raw || typeof raw !== "object") {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const control = raw;
|
|
137
|
-
const controlType = typeof control.type === "string" ? control.type : void 0;
|
|
138
|
-
if (controlType && V3_LAYOUT_CONTROL_TYPES.has(controlType)) {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const entry = {};
|
|
142
|
-
if (Object.prototype.hasOwnProperty.call(control, "default")) {
|
|
143
|
-
entry.default = control.default;
|
|
144
|
-
}
|
|
145
|
-
if (controlType) {
|
|
146
|
-
entry.type = controlType;
|
|
147
|
-
}
|
|
148
|
-
if (Object.prototype.hasOwnProperty.call(control, "options") && control.options !== void 0) {
|
|
149
|
-
const options = control.options;
|
|
150
|
-
if (options && typeof options === "object" && !Array.isArray(options)) {
|
|
151
|
-
entry.options = Object.keys(options);
|
|
152
|
-
} else {
|
|
153
|
-
entry.options = options;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
result[controlKey] = entry;
|
|
157
|
-
}
|
|
158
|
-
return result;
|
|
159
|
-
}
|
|
6
|
+
import { httpService } from "@elementor/http-client";
|
|
160
7
|
var CANVAS_SERVER_NAME = "editor-canvas";
|
|
161
8
|
var WIDGET_SCHEMA_URI = "elementor://widgets/schema/{widgetType}";
|
|
162
9
|
var WIDGET_SCHEMA_FULL_URI = `${CANVAS_SERVER_NAME}_${WIDGET_SCHEMA_URI}`;
|
|
163
|
-
var BEST_PRACTICES_URI = "elementor://
|
|
10
|
+
var BEST_PRACTICES_URI = "elementor://style/best-practices";
|
|
164
11
|
var BEST_PRACTICES_FULL_URI = `${CANVAS_SERVER_NAME}_${BEST_PRACTICES_URI}`;
|
|
12
|
+
var MCP_PROXY_URL = "elementor/v1/mcp-proxy";
|
|
13
|
+
var listWidgetTypes = async () => {
|
|
14
|
+
const { data } = await httpService().post(MCP_PROXY_URL, {
|
|
15
|
+
tool: "list-widget-schemas",
|
|
16
|
+
input: { summary: true }
|
|
17
|
+
});
|
|
18
|
+
return (data.data?.widgets ?? []).map((widget) => widget.type);
|
|
19
|
+
};
|
|
20
|
+
var fetchWidgetSchema = async (widgetType) => {
|
|
21
|
+
const { data } = await httpService().post(MCP_PROXY_URL, {
|
|
22
|
+
tool: "get-widget-schema",
|
|
23
|
+
input: { widget_type: widgetType }
|
|
24
|
+
});
|
|
25
|
+
return data.data ?? {};
|
|
26
|
+
};
|
|
165
27
|
var initWidgetsSchemaResource = (reg) => {
|
|
166
28
|
const { resource } = reg;
|
|
167
|
-
resource(
|
|
168
|
-
"styles-best-practices",
|
|
169
|
-
BEST_PRACTICES_URI,
|
|
170
|
-
{
|
|
171
|
-
description: "Styling best practices"
|
|
172
|
-
},
|
|
173
|
-
async () => {
|
|
174
|
-
return {
|
|
175
|
-
contents: [
|
|
176
|
-
{
|
|
177
|
-
uri: BEST_PRACTICES_URI,
|
|
178
|
-
text: `# Styling best practices
|
|
179
|
-
Prefer using "em" and "rem" values for text-related sizes, padding and spacing. Use percentages for dynamic sizing relative to parent containers.
|
|
180
|
-
This flexboxes are by default "flex" with "stretch" alignment. To ensure proper layout, define the "justify-content" and "align-items" as in the schema.
|
|
181
|
-
|
|
182
|
-
Styling is provided as raw CSS. The css string must follow standard CSS syntax, with properties and values separated by semicolons, no selectors, or nesting rules allowed.
|
|
183
|
-
|
|
184
|
-
** CRITICAL - VARIABLES **
|
|
185
|
-
When using global variables, ensure that the variables are defined in the ${"elementor://global-variables"} resource.
|
|
186
|
-
Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
|
|
187
|
-
|
|
188
|
-
`
|
|
189
|
-
}
|
|
190
|
-
]
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
29
|
resource(
|
|
195
30
|
"widget-schema-by-type",
|
|
196
31
|
new ResourceTemplate(WIDGET_SCHEMA_URI, {
|
|
197
|
-
list: () => {
|
|
198
|
-
const
|
|
199
|
-
const availableWidgets = Object.keys(cache).filter(
|
|
200
|
-
(widgetType) => isWidgetAvailableForLLM(cache[widgetType])
|
|
201
|
-
);
|
|
32
|
+
list: async () => {
|
|
33
|
+
const widgetTypes = await listWidgetTypes();
|
|
202
34
|
return {
|
|
203
|
-
resources:
|
|
35
|
+
resources: widgetTypes.map((widgetType) => ({
|
|
204
36
|
uri: `elementor://widgets/schema/${widgetType}`,
|
|
205
37
|
name: "Widget schema for " + widgetType
|
|
206
38
|
}))
|
|
@@ -212,52 +44,16 @@ Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
|
|
|
212
44
|
},
|
|
213
45
|
async (uri, variables) => {
|
|
214
46
|
const widgetType = typeof variables.widgetType === "string" ? variables.widgetType : variables.widgetType?.[0];
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
throw new Error(`No prop schema found for element type: ${widgetType}`);
|
|
218
|
-
}
|
|
219
|
-
const propSchema = widgetData.atomic_props_schema;
|
|
220
|
-
if (!propSchema) {
|
|
221
|
-
if (!hasV3Controls(widgetData.controls)) {
|
|
222
|
-
throw new Error(`No prop schema found for element type: ${widgetType}`);
|
|
223
|
-
}
|
|
224
|
-
const controlMetadata = extractV3ControlsMetadata(widgetData.controls);
|
|
225
|
-
return {
|
|
226
|
-
contents: [
|
|
227
|
-
{
|
|
228
|
-
uri: uri.toString(),
|
|
229
|
-
mimeType: "application/json",
|
|
230
|
-
text: JSON.stringify({
|
|
231
|
-
widget_version: "v3",
|
|
232
|
-
message: "This widget exists in the editor but has no atomic props schema (V4). Use control_metadata as non-authoritative hints from legacy controls.",
|
|
233
|
-
fields_note: "All settings are optional; there is no JSON schema for this widget type.",
|
|
234
|
-
properties: controlMetadata
|
|
235
|
-
})
|
|
236
|
-
}
|
|
237
|
-
]
|
|
238
|
-
};
|
|
47
|
+
if (!widgetType) {
|
|
48
|
+
throw new Error("No widget type provided.");
|
|
239
49
|
}
|
|
240
|
-
const
|
|
241
|
-
const asJson = enrichPropertiesWithBaseSettingsHints(
|
|
242
|
-
Object.fromEntries(
|
|
243
|
-
Object.entries(propSchema).filter(([key, propType]) => Schema.isPropKeyConfigurable(key, propType)).map(([key, propType]) => [key, Schema.propTypeToJsonSchema(propType)])
|
|
244
|
-
),
|
|
245
|
-
baseSettingsKeys
|
|
246
|
-
);
|
|
247
|
-
const description = typeof widgetData?.meta?.description === "string" ? widgetData.meta.description : void 0;
|
|
248
|
-
const allWidgets = getWidgetsCache2() || {};
|
|
249
|
-
const llmGuidance = buildLlmGuidance(widgetData, widgetType, allWidgets);
|
|
50
|
+
const schema2 = await fetchWidgetSchema(widgetType);
|
|
250
51
|
return {
|
|
251
52
|
contents: [
|
|
252
53
|
{
|
|
253
54
|
uri: uri.toString(),
|
|
254
55
|
mimeType: "application/json",
|
|
255
|
-
text: JSON.stringify(
|
|
256
|
-
type: "object",
|
|
257
|
-
properties: asJson,
|
|
258
|
-
description,
|
|
259
|
-
llm_guidance: llmGuidance
|
|
260
|
-
})
|
|
56
|
+
text: JSON.stringify(schema2)
|
|
261
57
|
}
|
|
262
58
|
]
|
|
263
59
|
};
|
|
@@ -312,11 +108,11 @@ var initBreakpointsResource = (reg) => {
|
|
|
312
108
|
};
|
|
313
109
|
|
|
314
110
|
// src/mcp/utils/convert-css-to-atomic.ts
|
|
315
|
-
import { httpService } from "@elementor/http-client";
|
|
111
|
+
import { httpService as httpService2 } from "@elementor/http-client";
|
|
316
112
|
var CSS_TO_ATOMIC_URL = "elementor/v1/css-to-atomic";
|
|
317
113
|
var SINGLE_BLOCK_KEY = "default";
|
|
318
114
|
var convertBlocks = async (blocks) => {
|
|
319
|
-
const { data } = await
|
|
115
|
+
const { data } = await httpService2().post(
|
|
320
116
|
CSS_TO_ATOMIC_URL,
|
|
321
117
|
{ blocks }
|
|
322
118
|
);
|
|
@@ -460,8 +256,8 @@ function toGridTracks(computedStyle) {
|
|
|
460
256
|
return {
|
|
461
257
|
columns: parseTrackList(computedStyle.gridTemplateColumns),
|
|
462
258
|
rows: parseTrackList(computedStyle.gridTemplateRows),
|
|
463
|
-
columnGap:
|
|
464
|
-
rowGap:
|
|
259
|
+
columnGap: resolveGapPx(computedStyle.columnGap, computedStyle.width),
|
|
260
|
+
rowGap: resolveGapPx(computedStyle.rowGap, computedStyle.height),
|
|
465
261
|
padding: {
|
|
466
262
|
top: toPx(computedStyle.paddingTop),
|
|
467
263
|
right: toPx(computedStyle.paddingRight),
|
|
@@ -536,6 +332,14 @@ function toPx(value) {
|
|
|
536
332
|
const parsed = parseFloat(value);
|
|
537
333
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
538
334
|
}
|
|
335
|
+
function resolveGapPx(value, referenceSize) {
|
|
336
|
+
if (value.trim().endsWith("%")) {
|
|
337
|
+
const percent = parseFloat(value);
|
|
338
|
+
const reference = parseFloat(referenceSize);
|
|
339
|
+
return Number.isFinite(percent) && Number.isFinite(reference) ? percent / 100 * reference : 0;
|
|
340
|
+
}
|
|
341
|
+
return toPx(value);
|
|
342
|
+
}
|
|
539
343
|
|
|
540
344
|
// src/hooks/use-grid-children.ts
|
|
541
345
|
import { useEffect as useEffect3, useState as useState2 } from "react";
|
|
@@ -2002,46 +1806,6 @@ function clipboardRootsAreAtomicForms(elements) {
|
|
|
2002
1806
|
}
|
|
2003
1807
|
return elements.every((el) => getClipboardElementType(el) === FORM_ELEMENT_TYPE);
|
|
2004
1808
|
}
|
|
2005
|
-
function hasFormAncestor(node) {
|
|
2006
|
-
return node.closest(FORM_ELEMENT_TYPE) !== null;
|
|
2007
|
-
}
|
|
2008
|
-
function collectFormAncestorErrors(xml) {
|
|
2009
|
-
const errors = [];
|
|
2010
|
-
for (const node of xml.querySelectorAll("*")) {
|
|
2011
|
-
if (!FORM_FIELD_ELEMENT_TYPES.has(node.tagName.toLowerCase())) {
|
|
2012
|
-
continue;
|
|
2013
|
-
}
|
|
2014
|
-
if (hasFormAncestor(node)) {
|
|
2015
|
-
continue;
|
|
2016
|
-
}
|
|
2017
|
-
const id = node.getAttribute("configuration-id");
|
|
2018
|
-
errors.push(
|
|
2019
|
-
`<${node.tagName}${id ? ` configuration-id="${id}"` : ""}> must be nested inside <e-form> (any ancestor depth is allowed).`
|
|
2020
|
-
);
|
|
2021
|
-
}
|
|
2022
|
-
return errors;
|
|
2023
|
-
}
|
|
2024
|
-
function collectSubmitButtonErrors(xml) {
|
|
2025
|
-
const errors = [];
|
|
2026
|
-
for (const form of xml.querySelectorAll("e-form")) {
|
|
2027
|
-
const submitButtons = form.querySelectorAll("e-form-submit-button");
|
|
2028
|
-
if (submitButtons.length === 0) {
|
|
2029
|
-
errors.push(`<e-form> has no <e-form-submit-button>.`);
|
|
2030
|
-
} else if (submitButtons.length > 1) {
|
|
2031
|
-
errors.push(`<e-form> has ${submitButtons.length} submit buttons \u2014 only 1 is allowed.`);
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
return errors;
|
|
2035
|
-
}
|
|
2036
|
-
function collectEmptyMessageErrors(xml) {
|
|
2037
|
-
const errors = [];
|
|
2038
|
-
for (const node of xml.querySelectorAll("e-form-success-message, e-form-error-message")) {
|
|
2039
|
-
if (node.children.length === 0) {
|
|
2040
|
-
errors.push(`<${node.tagName}> must have at least one child element (e.g. <e-atomic-paragraph>).`);
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
return errors;
|
|
2044
|
-
}
|
|
2045
1809
|
|
|
2046
1810
|
// src/form-structure/enforce-form-ancestor-commands.ts
|
|
2047
1811
|
var FORM_FIELDS_OUTSIDE_ALERT = {
|
|
@@ -2751,7 +2515,7 @@ function initStyleTransformers() {
|
|
|
2751
2515
|
}
|
|
2752
2516
|
|
|
2753
2517
|
// src/legacy/init-legacy-views.ts
|
|
2754
|
-
import { getWidgetsCache
|
|
2518
|
+
import { getWidgetsCache } from "@elementor/editor-elements";
|
|
2755
2519
|
import { __privateIsReady as isV1Ready, __privateListenTo, v1ReadyEvent as v1ReadyEvent2 } from "@elementor/editor-v1-adapters";
|
|
2756
2520
|
|
|
2757
2521
|
// src/renderers/create-dom-renderer.ts
|
|
@@ -4067,7 +3831,7 @@ function registerElementType(type, elementTypeGenerator) {
|
|
|
4067
3831
|
}
|
|
4068
3832
|
function initLegacyViews() {
|
|
4069
3833
|
__privateListenTo(v1ReadyEvent2(), () => {
|
|
4070
|
-
const widgetsCache =
|
|
3834
|
+
const widgetsCache = getWidgetsCache() ?? {};
|
|
4071
3835
|
const renderer = createDomRenderer();
|
|
4072
3836
|
registerProPromotionTypes(widgetsCache);
|
|
4073
3837
|
Object.keys(widgetsCache).forEach((type) => {
|
|
@@ -4076,7 +3840,7 @@ function initLegacyViews() {
|
|
|
4076
3840
|
});
|
|
4077
3841
|
}
|
|
4078
3842
|
function registerElementInLegacyManager(type, renderer) {
|
|
4079
|
-
const element = (
|
|
3843
|
+
const element = (getWidgetsCache() ?? {})[type];
|
|
4080
3844
|
if (!element?.atomic) {
|
|
4081
3845
|
return;
|
|
4082
3846
|
}
|
|
@@ -4158,68 +3922,85 @@ function initTabsModelExtensions() {
|
|
|
4158
3922
|
}
|
|
4159
3923
|
|
|
4160
3924
|
// src/mcp/canvas-mcp.ts
|
|
4161
|
-
import { Schema as
|
|
3925
|
+
import { Schema as Schema4 } from "@elementor/editor-props";
|
|
4162
3926
|
|
|
4163
3927
|
// src/mcp/resources/available-widgets-resource.ts
|
|
4164
|
-
import {
|
|
3928
|
+
import { httpService as httpService3 } from "@elementor/http-client";
|
|
3929
|
+
var MCP_PROXY_URL2 = "elementor/v1/mcp-proxy";
|
|
4165
3930
|
var AVAILABLE_WIDGETS_URI = "elementor://context/available-widgets";
|
|
4166
3931
|
var AVAILABLE_WIDGETS_URI_V4 = "elementor://context/available-widgets/v4";
|
|
4167
|
-
var
|
|
4168
|
-
const {
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
...buildContents(AVAILABLE_WIDGETS_URI)
|
|
4185
|
-
});
|
|
4186
|
-
sendResourceUpdated({
|
|
4187
|
-
uri: AVAILABLE_WIDGETS_URI_V4,
|
|
4188
|
-
...buildContents(AVAILABLE_WIDGETS_URI_V4, (w) => w.version === "v4")
|
|
4189
|
-
});
|
|
3932
|
+
var fetchWidgets = async () => {
|
|
3933
|
+
const { data } = await httpService3().post(MCP_PROXY_URL2, {
|
|
3934
|
+
tool: "list-widget-schemas",
|
|
3935
|
+
input: { summary: true }
|
|
3936
|
+
});
|
|
3937
|
+
return data.data?.widgets ?? [];
|
|
3938
|
+
};
|
|
3939
|
+
var buildContents = async (uri) => {
|
|
3940
|
+
const widgets = await fetchWidgets();
|
|
3941
|
+
return {
|
|
3942
|
+
contents: [
|
|
3943
|
+
{
|
|
3944
|
+
uri,
|
|
3945
|
+
mimeType: "application/json",
|
|
3946
|
+
text: JSON.stringify(widgets, null, 2)
|
|
3947
|
+
}
|
|
3948
|
+
]
|
|
4190
3949
|
};
|
|
3950
|
+
};
|
|
3951
|
+
var initAvailableWidgetsResource = (reg) => {
|
|
3952
|
+
const { resource } = reg;
|
|
4191
3953
|
resource(
|
|
4192
3954
|
"available-widgets-v4",
|
|
4193
3955
|
AVAILABLE_WIDGETS_URI_V4,
|
|
4194
3956
|
{
|
|
4195
3957
|
description: "All registered v4 version widgets"
|
|
4196
3958
|
},
|
|
4197
|
-
async () => buildContents(AVAILABLE_WIDGETS_URI_V4
|
|
3959
|
+
async () => buildContents(AVAILABLE_WIDGETS_URI_V4)
|
|
4198
3960
|
);
|
|
4199
3961
|
resource(
|
|
4200
3962
|
"available-widgets",
|
|
4201
3963
|
AVAILABLE_WIDGETS_URI,
|
|
4202
3964
|
{
|
|
4203
|
-
description: "All registered widget types with
|
|
3965
|
+
description: "All registered v4 widget types with description."
|
|
4204
3966
|
},
|
|
4205
3967
|
async () => buildContents(AVAILABLE_WIDGETS_URI)
|
|
4206
3968
|
);
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
3969
|
+
};
|
|
3970
|
+
|
|
3971
|
+
// src/mcp/resources/best-practices-resource.ts
|
|
3972
|
+
import { httpService as httpService4 } from "@elementor/http-client";
|
|
3973
|
+
var MCP_PROXY_URL3 = "elementor/v1/mcp-proxy";
|
|
3974
|
+
var BEST_PRACTICES_URI2 = "elementor://style/best-practices";
|
|
3975
|
+
var initBestPracticesResource = (reg) => {
|
|
3976
|
+
const { resource } = reg;
|
|
3977
|
+
resource(
|
|
3978
|
+
"style-best-practices",
|
|
3979
|
+
BEST_PRACTICES_URI2,
|
|
3980
|
+
{
|
|
3981
|
+
description: "Design quality guidelines for avoiding generic AI output: typography, color strategy, spacing, motion, and visual hierarchy best practices.",
|
|
3982
|
+
mimeType: "text/markdown"
|
|
3983
|
+
},
|
|
3984
|
+
async (uri) => {
|
|
3985
|
+
const { data } = await httpService4().get(MCP_PROXY_URL3, {
|
|
3986
|
+
params: { uri: uri.href }
|
|
3987
|
+
});
|
|
3988
|
+
return {
|
|
3989
|
+
contents: [
|
|
3990
|
+
{
|
|
3991
|
+
uri: uri.href,
|
|
3992
|
+
mimeType: "text/markdown",
|
|
3993
|
+
text: data.data
|
|
3994
|
+
}
|
|
3995
|
+
]
|
|
3996
|
+
};
|
|
4212
3997
|
}
|
|
4213
|
-
|
|
4214
|
-
notifyResourcesUpdated();
|
|
4215
|
-
};
|
|
4216
|
-
window.addEventListener(eventName, onV1Ready);
|
|
4217
|
-
onV1Ready();
|
|
3998
|
+
);
|
|
4218
3999
|
};
|
|
4219
4000
|
|
|
4220
4001
|
// src/mcp/resources/document-structure-resource.ts
|
|
4221
4002
|
import {
|
|
4222
|
-
getWidgetsCache as
|
|
4003
|
+
getWidgetsCache as getWidgetsCache2
|
|
4223
4004
|
} from "@elementor/editor-elements";
|
|
4224
4005
|
import { __privateListenTo as listenTo, commandEndEvent as commandEndEvent4 } from "@elementor/editor-v1-adapters";
|
|
4225
4006
|
var DOCUMENT_STRUCTURE_URI = "elementor://document/structure";
|
|
@@ -4285,7 +4066,7 @@ function resolveElementVersion(element) {
|
|
|
4285
4066
|
return "v4";
|
|
4286
4067
|
}
|
|
4287
4068
|
const widgetType = element.model?.attributes?.widgetType;
|
|
4288
|
-
if (widgetType &&
|
|
4069
|
+
if (widgetType && getWidgetsCache2()?.[widgetType]?.atomic_props_schema) {
|
|
4289
4070
|
return "v4";
|
|
4290
4071
|
}
|
|
4291
4072
|
return "v3";
|
|
@@ -4312,84 +4093,15 @@ function extractElementData(element) {
|
|
|
4312
4093
|
}
|
|
4313
4094
|
|
|
4314
4095
|
// src/mcp/resources/dynamic-tags-resource.ts
|
|
4315
|
-
import {
|
|
4316
|
-
|
|
4317
|
-
// src/mcp/utils/resolve-dynamic-tag.ts
|
|
4318
|
-
import { getElementorConfig } from "@elementor/editor-v1-adapters";
|
|
4319
|
-
var DYNAMIC_PROP_TYPE_KEY = "dynamic";
|
|
4320
|
-
var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
|
|
4321
|
-
var getAtomicDynamicTags = () => {
|
|
4322
|
-
const config = getElementorConfig();
|
|
4323
|
-
return config.atomicDynamicTags?.tags ?? {};
|
|
4324
|
-
};
|
|
4325
|
-
var getDynamicTagNamesByCategories = (categories) => {
|
|
4326
|
-
if (!categories.length) {
|
|
4327
|
-
return [];
|
|
4328
|
-
}
|
|
4329
|
-
const wanted = new Set(categories);
|
|
4330
|
-
return Object.values(getAtomicDynamicTags()).filter((tag) => tag.categories?.some((category) => wanted.has(category))).map((tag) => tag.name);
|
|
4331
|
-
};
|
|
4332
|
-
var dynamicTagLLMResolver = (value) => {
|
|
4333
|
-
const input = value ?? {};
|
|
4334
|
-
const tag = input.name ? getAtomicDynamicTags()[input.name] : void 0;
|
|
4335
|
-
if (!tag) {
|
|
4336
|
-
return {
|
|
4337
|
-
$$type: DYNAMIC_PROP_TYPE_KEY,
|
|
4338
|
-
value: { name: input.name ?? "", group: "", settings: {} }
|
|
4339
|
-
};
|
|
4340
|
-
}
|
|
4341
|
-
return {
|
|
4342
|
-
$$type: DYNAMIC_PROP_TYPE_KEY,
|
|
4343
|
-
value: {
|
|
4344
|
-
name: tag.name,
|
|
4345
|
-
group: tag.group,
|
|
4346
|
-
settings: buildStrictSettings(tag.props_schema ?? {}, input.settings ?? {})
|
|
4347
|
-
}
|
|
4348
|
-
};
|
|
4349
|
-
};
|
|
4350
|
-
var buildStrictSettings = (schema2, provided) => {
|
|
4351
|
-
const settings = {};
|
|
4352
|
-
for (const [key, propType] of Object.entries(schema2)) {
|
|
4353
|
-
if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
|
|
4354
|
-
continue;
|
|
4355
|
-
}
|
|
4356
|
-
const resolved = provided[key] !== void 0 ? wrapSettingValue(provided[key], propType) : defaultSettingValue(propType);
|
|
4357
|
-
if (resolved !== void 0 && resolved !== null) {
|
|
4358
|
-
settings[key] = resolved;
|
|
4359
|
-
}
|
|
4360
|
-
}
|
|
4361
|
-
return settings;
|
|
4362
|
-
};
|
|
4363
|
-
var wrapSettingValue = (raw, propType) => {
|
|
4364
|
-
if (raw !== null && typeof raw === "object") {
|
|
4365
|
-
return raw;
|
|
4366
|
-
}
|
|
4367
|
-
return propType.key ? { $$type: propType.key, value: raw } : raw;
|
|
4368
|
-
};
|
|
4369
|
-
var defaultSettingValue = (propType) => {
|
|
4370
|
-
if (propType.initial_value !== null && propType.initial_value !== void 0) {
|
|
4371
|
-
return propType.initial_value;
|
|
4372
|
-
}
|
|
4373
|
-
if (propType.default !== null && propType.default !== void 0) {
|
|
4374
|
-
return wrapSettingValue(propType.default, propType);
|
|
4375
|
-
}
|
|
4376
|
-
return void 0;
|
|
4377
|
-
};
|
|
4378
|
-
|
|
4379
|
-
// src/mcp/resources/dynamic-tags-resource.ts
|
|
4096
|
+
import { httpService as httpService5 } from "@elementor/http-client";
|
|
4380
4097
|
var DYNAMIC_TAGS_URI = "elementor://dynamic-tags";
|
|
4381
|
-
var
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
}
|
|
4386
|
-
|
|
4387
|
-
return
|
|
4388
|
-
name: tag.name,
|
|
4389
|
-
label: tag.label,
|
|
4390
|
-
categories: tag.categories,
|
|
4391
|
-
settings: settingsSchema(tag.props_schema)
|
|
4392
|
-
}));
|
|
4098
|
+
var MCP_PROXY_URL4 = "elementor/v1/mcp-proxy";
|
|
4099
|
+
var fetchDynamicTags = async () => {
|
|
4100
|
+
const { data } = await httpService5().post(MCP_PROXY_URL4, {
|
|
4101
|
+
tool: "list-dynamic-tags",
|
|
4102
|
+
input: {}
|
|
4103
|
+
});
|
|
4104
|
+
return data.data ?? [];
|
|
4393
4105
|
};
|
|
4394
4106
|
var initDynamicTagsResource = (reg) => {
|
|
4395
4107
|
const { resource } = reg;
|
|
@@ -4400,15 +4112,18 @@ var initDynamicTagsResource = (reg) => {
|
|
|
4400
4112
|
description: `List of available dynamic tags. To bind a property to a dynamic source, set its value to { "$$type": "dynamic", "value": { "name": <tag name>, "settings": { ... } } } using a tag whose name appears in that property's allowed list, and populate "settings" per the tag entry here.`,
|
|
4401
4113
|
mimeType: "application/json"
|
|
4402
4114
|
},
|
|
4403
|
-
async (uri) =>
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4115
|
+
async (uri) => {
|
|
4116
|
+
const tags = await fetchDynamicTags();
|
|
4117
|
+
return {
|
|
4118
|
+
contents: [
|
|
4119
|
+
{
|
|
4120
|
+
uri: uri.href,
|
|
4121
|
+
mimeType: "application/json",
|
|
4122
|
+
text: JSON.stringify(tags)
|
|
4123
|
+
}
|
|
4124
|
+
]
|
|
4125
|
+
};
|
|
4126
|
+
}
|
|
4412
4127
|
);
|
|
4413
4128
|
};
|
|
4414
4129
|
|
|
@@ -4582,7 +4297,7 @@ var initGeneralContextResource = (reg) => {
|
|
|
4582
4297
|
};
|
|
4583
4298
|
|
|
4584
4299
|
// src/mcp/resources/selected-element-resource.ts
|
|
4585
|
-
import { getContainer as getContainer3, getSelectedElements, getWidgetsCache as
|
|
4300
|
+
import { getContainer as getContainer3, getSelectedElements, getWidgetsCache as getWidgetsCache3 } from "@elementor/editor-elements";
|
|
4586
4301
|
import {
|
|
4587
4302
|
__privateListenTo as listenTo4,
|
|
4588
4303
|
commandEndEvent as commandEndEvent7
|
|
@@ -4683,7 +4398,7 @@ function resolveElementVersion2(container, widgetType) {
|
|
|
4683
4398
|
if (container.model?.config?.atomic) {
|
|
4684
4399
|
return "v4";
|
|
4685
4400
|
}
|
|
4686
|
-
if (widgetType &&
|
|
4401
|
+
if (widgetType && getWidgetsCache3()?.[widgetType]?.atomic_props_schema) {
|
|
4687
4402
|
return "v4";
|
|
4688
4403
|
}
|
|
4689
4404
|
return "v3";
|
|
@@ -4693,7 +4408,7 @@ function getElementProperties(container, widgetType) {
|
|
|
4693
4408
|
if (!settings || typeof settings !== "object") {
|
|
4694
4409
|
return null;
|
|
4695
4410
|
}
|
|
4696
|
-
const widgetConfig = widgetType ?
|
|
4411
|
+
const widgetConfig = widgetType ? getWidgetsCache3()?.[widgetType] : null;
|
|
4697
4412
|
const controls = widgetConfig?.controls;
|
|
4698
4413
|
const filtered = {};
|
|
4699
4414
|
for (const [key, value] of Object.entries(settings)) {
|
|
@@ -4731,33 +4446,110 @@ function getElementDisplayName(container) {
|
|
|
4731
4446
|
}
|
|
4732
4447
|
|
|
4733
4448
|
// src/mcp/tools/build-composition/tool.ts
|
|
4734
|
-
import { getCurrentDocument } from "@elementor/editor-documents";
|
|
4735
|
-
import {
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
}
|
|
4741
|
-
|
|
4449
|
+
import { getCurrentDocument, reloadCurrentDocument } from "@elementor/editor-documents";
|
|
4450
|
+
import { getContainer as getContainer4, selectElement } from "@elementor/editor-elements";
|
|
4451
|
+
import { AxiosError, httpService as httpService6 } from "@elementor/http-client";
|
|
4452
|
+
import { z } from "@elementor/schema";
|
|
4453
|
+
var MCP_PROXY_URL5 = "elementor/v1/mcp-proxy";
|
|
4454
|
+
var initBuildCompositionTool = (reg) => {
|
|
4455
|
+
const { addTool } = reg;
|
|
4456
|
+
addTool({
|
|
4457
|
+
name: "build-composition",
|
|
4458
|
+
description: "Build a V4 element composition on the Elementor canvas via the server-side MCP ability. Pass the raw XML tags directly as xmlStructure \u2014 do NOT wrap the value in <![CDATA[ ... ]]>, code fences, or quotes. The document is saved as a draft. Reload the editor after calling this tool to see the result.",
|
|
4459
|
+
schema: {
|
|
4460
|
+
xmlStructure: z.string().describe(
|
|
4461
|
+
'Valid XML structure with custom Elementor widget tags. Every element MUST have a unique configuration-id attribute (e.g. <e-heading configuration-id="hero-title"></e-heading>). No attributes, classes, IDs, or text nodes in XML. Pass raw XML \u2014 do not wrap in CDATA.'
|
|
4462
|
+
),
|
|
4463
|
+
elementConfig: z.record(
|
|
4464
|
+
z.string().describe("configuration-id"),
|
|
4465
|
+
z.record(z.string().describe("property name"), z.any().describe("PropValue"))
|
|
4466
|
+
).optional().describe("Map configuration-id \u2192 widget PropValues ($$type + value)."),
|
|
4467
|
+
style: z.record(
|
|
4468
|
+
z.string().describe("configuration-id"),
|
|
4469
|
+
z.record(z.string().describe("CSS property name"), z.string().describe("CSS value"))
|
|
4470
|
+
).optional().describe(
|
|
4471
|
+
"Map configuration-id \u2192 raw CSS declarations (property \u2192 value strings; no selectors). Server converts to native styles; unconvertible declarations become the element custom CSS."
|
|
4472
|
+
),
|
|
4473
|
+
parentId: z.string().optional().describe("ID of the parent container. Omit or pass 'document' to insert at document root."),
|
|
4474
|
+
dryRun: z.boolean().optional().describe("If true, validate and return the resolved tree without persisting.")
|
|
4475
|
+
},
|
|
4476
|
+
outputSchema: {
|
|
4477
|
+
rootElementIds: z.array(z.string()),
|
|
4478
|
+
previewUrl: z.string(),
|
|
4479
|
+
version: z.string(),
|
|
4480
|
+
resolvedXml: z.string(),
|
|
4481
|
+
llmInstructions: z.string(),
|
|
4482
|
+
warnings: z.array(z.string()).optional()
|
|
4483
|
+
},
|
|
4484
|
+
handler: async ({ xmlStructure, elementConfig, style, parentId, dryRun }) => {
|
|
4485
|
+
const document2 = getCurrentDocument();
|
|
4486
|
+
if (!document2?.id) {
|
|
4487
|
+
throw new Error("No active document found.");
|
|
4488
|
+
}
|
|
4489
|
+
try {
|
|
4490
|
+
const { data } = await httpService6().post(MCP_PROXY_URL5, {
|
|
4491
|
+
tool: "build-composition",
|
|
4492
|
+
input: {
|
|
4493
|
+
post_id: document2.id,
|
|
4494
|
+
xml_structure: xmlStructure,
|
|
4495
|
+
element_config: elementConfig ?? {},
|
|
4496
|
+
style: style ?? {},
|
|
4497
|
+
parent_id: parentId ?? "document",
|
|
4498
|
+
dry_run: dryRun ?? false
|
|
4499
|
+
}
|
|
4500
|
+
});
|
|
4501
|
+
if (!dryRun) {
|
|
4502
|
+
await reloadCurrentDocument();
|
|
4503
|
+
const [firstRootId] = data.data.root_element_ids;
|
|
4504
|
+
if (firstRootId) {
|
|
4505
|
+
selectElement(firstRootId);
|
|
4506
|
+
getContainer4(firstRootId)?.view?.el?.scrollIntoView({
|
|
4507
|
+
behavior: "smooth",
|
|
4508
|
+
block: "center"
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
return {
|
|
4513
|
+
rootElementIds: data.data.root_element_ids,
|
|
4514
|
+
previewUrl: data.data.preview_url,
|
|
4515
|
+
version: data.data.version,
|
|
4516
|
+
resolvedXml: data.data.resolved_xml,
|
|
4517
|
+
llmInstructions: data.data.llm_instructions,
|
|
4518
|
+
warnings: data.data.warnings
|
|
4519
|
+
};
|
|
4520
|
+
} catch (error) {
|
|
4521
|
+
throw new Error(getErrorMessage(error));
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
});
|
|
4525
|
+
};
|
|
4526
|
+
function getErrorMessage(error) {
|
|
4527
|
+
if (error instanceof AxiosError) {
|
|
4528
|
+
const data = error.response?.data;
|
|
4529
|
+
if (data?.message) {
|
|
4530
|
+
return data.code ? `${data.code}: ${data.message}` : data.message;
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
if (error instanceof Error) {
|
|
4534
|
+
return error.message;
|
|
4535
|
+
}
|
|
4536
|
+
return "build-composition failed with an unknown error.";
|
|
4537
|
+
}
|
|
4742
4538
|
|
|
4743
|
-
// src/
|
|
4744
|
-
import {
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
generateElementId as generateElementId2,
|
|
4748
|
-
getContainer as getContainer4,
|
|
4749
|
-
getWidgetsCache as getWidgetsCache8
|
|
4750
|
-
} from "@elementor/editor-elements";
|
|
4539
|
+
// src/mcp/tools/configure-element/tool.ts
|
|
4540
|
+
import { getContainer as getContainer5, getWidgetsCache as getWidgetsCache6 } from "@elementor/editor-elements";
|
|
4541
|
+
import { dispatchMcpStylesAppliedEvent } from "@elementor/editor-mcp";
|
|
4542
|
+
import { Schema as Schema2 } from "@elementor/editor-props";
|
|
4751
4543
|
|
|
4752
4544
|
// src/mcp/utils/do-update-element-property.ts
|
|
4753
4545
|
import {
|
|
4754
4546
|
createElementStyle,
|
|
4755
4547
|
getElementStyles,
|
|
4756
|
-
getWidgetsCache as
|
|
4548
|
+
getWidgetsCache as getWidgetsCache5,
|
|
4757
4549
|
updateElementSettings,
|
|
4758
4550
|
updateElementStyle
|
|
4759
4551
|
} from "@elementor/editor-elements";
|
|
4760
|
-
import { getPropSchemaFromCache as getPropSchemaFromCache2, Schema
|
|
4552
|
+
import { getPropSchemaFromCache as getPropSchemaFromCache2, Schema } from "@elementor/editor-props";
|
|
4761
4553
|
import { getStylesSchema as getStylesSchema2, getVariantByMeta } from "@elementor/editor-styles";
|
|
4762
4554
|
import { __privateRunCommandSync as runCommandSync2 } from "@elementor/editor-v1-adapters";
|
|
4763
4555
|
|
|
@@ -4776,7 +4568,7 @@ var readStoredCustomCssText = (raw) => {
|
|
|
4776
4568
|
};
|
|
4777
4569
|
|
|
4778
4570
|
// src/mcp/utils/resolve-canonical-prop-name.ts
|
|
4779
|
-
import { getWidgetsCache as
|
|
4571
|
+
import { getWidgetsCache as getWidgetsCache4 } from "@elementor/editor-elements";
|
|
4780
4572
|
function buildAliasToCanonicalMap(schema2) {
|
|
4781
4573
|
const aliasToCanonical = {};
|
|
4782
4574
|
for (const [canonical, propType] of Object.entries(schema2)) {
|
|
@@ -4793,14 +4585,14 @@ function buildAliasToCanonicalMap(schema2) {
|
|
|
4793
4585
|
return aliasToCanonical;
|
|
4794
4586
|
}
|
|
4795
4587
|
function resolveCanonicalPropName(elementType, propertyName) {
|
|
4796
|
-
const schema2 =
|
|
4588
|
+
const schema2 = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
|
|
4797
4589
|
if (!schema2 || schema2[propertyName]) {
|
|
4798
4590
|
return propertyName;
|
|
4799
4591
|
}
|
|
4800
4592
|
return buildAliasToCanonicalMap(schema2)[propertyName] ?? propertyName;
|
|
4801
4593
|
}
|
|
4802
4594
|
function resolveCanonicalPropKeys(elementType, props) {
|
|
4803
|
-
const schema2 =
|
|
4595
|
+
const schema2 = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
|
|
4804
4596
|
if (!schema2) {
|
|
4805
4597
|
return { ...props };
|
|
4806
4598
|
}
|
|
@@ -4827,6 +4619,68 @@ function resolveCanonicalPropKeys(elementType, props) {
|
|
|
4827
4619
|
return resolved;
|
|
4828
4620
|
}
|
|
4829
4621
|
|
|
4622
|
+
// src/mcp/utils/resolve-dynamic-tag.ts
|
|
4623
|
+
import { getElementorConfig } from "@elementor/editor-v1-adapters";
|
|
4624
|
+
var DYNAMIC_PROP_TYPE_KEY = "dynamic";
|
|
4625
|
+
var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
|
|
4626
|
+
var getAtomicDynamicTags = () => {
|
|
4627
|
+
const config = getElementorConfig();
|
|
4628
|
+
return config.atomicDynamicTags?.tags ?? {};
|
|
4629
|
+
};
|
|
4630
|
+
var getDynamicTagNamesByCategories = (categories) => {
|
|
4631
|
+
if (!categories.length) {
|
|
4632
|
+
return [];
|
|
4633
|
+
}
|
|
4634
|
+
const wanted = new Set(categories);
|
|
4635
|
+
return Object.values(getAtomicDynamicTags()).filter((tag) => tag.categories?.some((category) => wanted.has(category))).map((tag) => tag.name);
|
|
4636
|
+
};
|
|
4637
|
+
var dynamicTagLLMResolver = (value) => {
|
|
4638
|
+
const input = value ?? {};
|
|
4639
|
+
const tag = input.name ? getAtomicDynamicTags()[input.name] : void 0;
|
|
4640
|
+
if (!tag) {
|
|
4641
|
+
return {
|
|
4642
|
+
$$type: DYNAMIC_PROP_TYPE_KEY,
|
|
4643
|
+
value: { name: input.name ?? "", group: "", settings: {} }
|
|
4644
|
+
};
|
|
4645
|
+
}
|
|
4646
|
+
return {
|
|
4647
|
+
$$type: DYNAMIC_PROP_TYPE_KEY,
|
|
4648
|
+
value: {
|
|
4649
|
+
name: tag.name,
|
|
4650
|
+
group: tag.group,
|
|
4651
|
+
settings: buildStrictSettings(tag.props_schema ?? {}, input.settings ?? {})
|
|
4652
|
+
}
|
|
4653
|
+
};
|
|
4654
|
+
};
|
|
4655
|
+
var buildStrictSettings = (schema2, provided) => {
|
|
4656
|
+
const settings = {};
|
|
4657
|
+
for (const [key, propType] of Object.entries(schema2)) {
|
|
4658
|
+
if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
|
|
4659
|
+
continue;
|
|
4660
|
+
}
|
|
4661
|
+
const resolved = provided[key] !== void 0 ? wrapSettingValue(provided[key], propType) : defaultSettingValue(propType);
|
|
4662
|
+
if (resolved !== void 0 && resolved !== null) {
|
|
4663
|
+
settings[key] = resolved;
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
return settings;
|
|
4667
|
+
};
|
|
4668
|
+
var wrapSettingValue = (raw, propType) => {
|
|
4669
|
+
if (raw !== null && typeof raw === "object") {
|
|
4670
|
+
return raw;
|
|
4671
|
+
}
|
|
4672
|
+
return propType.key ? { $$type: propType.key, value: raw } : raw;
|
|
4673
|
+
};
|
|
4674
|
+
var defaultSettingValue = (propType) => {
|
|
4675
|
+
if (propType.initial_value !== null && propType.initial_value !== void 0) {
|
|
4676
|
+
return propType.initial_value;
|
|
4677
|
+
}
|
|
4678
|
+
if (propType.default !== null && propType.default !== void 0) {
|
|
4679
|
+
return wrapSettingValue(propType.default, propType);
|
|
4680
|
+
}
|
|
4681
|
+
return void 0;
|
|
4682
|
+
};
|
|
4683
|
+
|
|
4830
4684
|
// src/mcp/utils/do-update-element-property.ts
|
|
4831
4685
|
var LOCAL_STYLE_META = {
|
|
4832
4686
|
breakpoint: "desktop",
|
|
@@ -4834,7 +4688,7 @@ var LOCAL_STYLE_META = {
|
|
|
4834
4688
|
};
|
|
4835
4689
|
function resolvePropValue(value, forceKey) {
|
|
4836
4690
|
const Utils = window.elementorV2.editorVariables.Utils;
|
|
4837
|
-
return
|
|
4691
|
+
return Schema.adjustLlmPropValueSchema(value, {
|
|
4838
4692
|
forceKey,
|
|
4839
4693
|
transformers: {
|
|
4840
4694
|
...Utils.globalVariablesLLMResolvers,
|
|
@@ -4932,7 +4786,7 @@ var doUpdateElementProperty = (params) => {
|
|
|
4932
4786
|
}
|
|
4933
4787
|
return;
|
|
4934
4788
|
}
|
|
4935
|
-
const elementPropSchema =
|
|
4789
|
+
const elementPropSchema = getWidgetsCache5()?.[elementType]?.atomic_props_schema;
|
|
4936
4790
|
if (!elementPropSchema) {
|
|
4937
4791
|
throw new Error(`No prop schema found for element type: ${elementType}`);
|
|
4938
4792
|
}
|
|
@@ -4946,7 +4800,7 @@ var doUpdateElementProperty = (params) => {
|
|
|
4946
4800
|
}
|
|
4947
4801
|
const propKey = elementPropSchema[propertyName].key;
|
|
4948
4802
|
const value = resolvePropValue(propertyValue, propKey);
|
|
4949
|
-
const { valid, jsonSchema } =
|
|
4803
|
+
const { valid, jsonSchema } = Schema.validatePropValue(elementPropSchema[propertyName], propertyValue);
|
|
4950
4804
|
if (!valid) {
|
|
4951
4805
|
throw new Error(
|
|
4952
4806
|
`Invalid PropValue for elementId: ${elementId}. PropKey: ${propKey}, PropValue: ${JSON.stringify(
|
|
@@ -4965,752 +4819,11 @@ Expected Schema: ${jsonSchema}`
|
|
|
4965
4819
|
runCommandSync2("document/save/set-is-modified", { status: true }, { internal: true });
|
|
4966
4820
|
};
|
|
4967
4821
|
|
|
4968
|
-
// src/composition-builder/utils/required-children-enforcer.ts
|
|
4969
|
-
var REQUIRED_CHILD_SCHEMA_HINT = "Use the widget schema resource; under llm_guidance.required_direct_children for V4 widgets.";
|
|
4970
|
-
var RequiredChildrenEnforcer = class {
|
|
4971
|
-
elementType;
|
|
4972
|
-
requiredTemplates;
|
|
4973
|
-
constructor(elementType, widgetsCache) {
|
|
4974
|
-
this.elementType = elementType;
|
|
4975
|
-
this.requiredTemplates = getRequiredDefaultChildTemplates(widgetsCache[elementType]);
|
|
4976
|
-
}
|
|
4977
|
-
enforce(xml) {
|
|
4978
|
-
if (this.requiredTemplates.length === 0) {
|
|
4979
|
-
return;
|
|
4980
|
-
}
|
|
4981
|
-
const errors = [];
|
|
4982
|
-
for (const rootNode of Array.from(xml.children)) {
|
|
4983
|
-
this.collectMissingRequiredErrors(rootNode, errors);
|
|
4984
|
-
}
|
|
4985
|
-
if (errors.length) {
|
|
4986
|
-
throw new Error(`${errors.join("\n")}
|
|
4987
|
-
${REQUIRED_CHILD_SCHEMA_HINT}`);
|
|
4988
|
-
}
|
|
4989
|
-
}
|
|
4990
|
-
collectMissingRequiredErrors(node, errors) {
|
|
4991
|
-
if (node.tagName === this.elementType) {
|
|
4992
|
-
const existingChildTags = new Set(Array.from(node.children).map((child) => child.tagName));
|
|
4993
|
-
const missingTags = this.requiredTemplates.map((child) => child.widgetType ?? child.elType ?? "").filter((type) => type && !existingChildTags.has(type));
|
|
4994
|
-
if (missingTags.length) {
|
|
4995
|
-
const configurationId = node.getAttribute("configuration-id");
|
|
4996
|
-
const location2 = configurationId ? `<${node.tagName} configuration-id="${configurationId}">` : `<${node.tagName}>`;
|
|
4997
|
-
errors.push(
|
|
4998
|
-
`${location2} Missing required direct child element tag(s): ${missingTags.join(", ")}.`
|
|
4999
|
-
);
|
|
5000
|
-
}
|
|
5001
|
-
}
|
|
5002
|
-
for (const childNode of Array.from(node.children)) {
|
|
5003
|
-
this.collectMissingRequiredErrors(childNode, errors);
|
|
5004
|
-
}
|
|
5005
|
-
}
|
|
5006
|
-
};
|
|
5007
|
-
|
|
5008
|
-
// src/composition-builder/composition-builder.ts
|
|
5009
|
-
var CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE = "createElement did not return an element container with a model.";
|
|
5010
|
-
var CompositionBuilder = class _CompositionBuilder {
|
|
5011
|
-
elementConfig = {};
|
|
5012
|
-
elementStylesConfig = {};
|
|
5013
|
-
elementCustomCSS = {};
|
|
5014
|
-
rootContainers = [];
|
|
5015
|
-
api = {
|
|
5016
|
-
createElement: createElement12,
|
|
5017
|
-
deleteElement,
|
|
5018
|
-
getWidgetsCache: getWidgetsCache8,
|
|
5019
|
-
generateElementId: generateElementId2,
|
|
5020
|
-
getContainer: getContainer4,
|
|
5021
|
-
doUpdateElementProperty
|
|
5022
|
-
};
|
|
5023
|
-
xml;
|
|
5024
|
-
static fromXMLString(xmlString, api = {}) {
|
|
5025
|
-
const parser = new DOMParser();
|
|
5026
|
-
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
|
5027
|
-
const errorNode = xmlDoc.querySelector("parsererror");
|
|
5028
|
-
if (errorNode) {
|
|
5029
|
-
throw new Error("Failed to parse XML string: " + errorNode.textContent);
|
|
5030
|
-
}
|
|
5031
|
-
return new _CompositionBuilder({
|
|
5032
|
-
xml: xmlDoc,
|
|
5033
|
-
api
|
|
5034
|
-
});
|
|
5035
|
-
}
|
|
5036
|
-
constructor(opts) {
|
|
5037
|
-
const { api = {}, elementConfig = {}, stylesConfig = {}, customCSS = {}, xml } = opts;
|
|
5038
|
-
this.xml = xml;
|
|
5039
|
-
Object.assign(this.api, api);
|
|
5040
|
-
this.setElementConfig(elementConfig);
|
|
5041
|
-
this.setStylesConfig(stylesConfig);
|
|
5042
|
-
this.setCustomCSS(customCSS);
|
|
5043
|
-
}
|
|
5044
|
-
setElementConfig(config) {
|
|
5045
|
-
this.elementConfig = config;
|
|
5046
|
-
}
|
|
5047
|
-
setStylesConfig(config) {
|
|
5048
|
-
this.elementStylesConfig = config;
|
|
5049
|
-
}
|
|
5050
|
-
setCustomCSS(config) {
|
|
5051
|
-
this.elementCustomCSS = config;
|
|
5052
|
-
}
|
|
5053
|
-
getXML() {
|
|
5054
|
-
return this.xml;
|
|
5055
|
-
}
|
|
5056
|
-
buildModelTree(node, widgetsCache) {
|
|
5057
|
-
const elementTag = node.tagName;
|
|
5058
|
-
const isWidget = widgetsCache[elementTag]?.elType === "widget";
|
|
5059
|
-
const id = this.api.generateElementId();
|
|
5060
|
-
const children = Array.from(node.children).map((child) => this.buildModelTree(child, widgetsCache));
|
|
5061
|
-
node.setAttribute("id", id);
|
|
5062
|
-
const base = {
|
|
5063
|
-
id,
|
|
5064
|
-
skipDefaultChildren: true,
|
|
5065
|
-
elements: children,
|
|
5066
|
-
editor_settings: {
|
|
5067
|
-
title: node.getAttribute("configuration-id") ?? void 0
|
|
5068
|
-
},
|
|
5069
|
-
elType: "widget"
|
|
5070
|
-
};
|
|
5071
|
-
if (isWidget) {
|
|
5072
|
-
return { ...base, elType: "widget", widgetType: elementTag };
|
|
5073
|
-
}
|
|
5074
|
-
return { ...base, elType: elementTag };
|
|
5075
|
-
}
|
|
5076
|
-
async awaitViewRender(element) {
|
|
5077
|
-
const view = element.view;
|
|
5078
|
-
if (view?._currentRenderPromise instanceof Promise) {
|
|
5079
|
-
await view._currentRenderPromise;
|
|
5080
|
-
} else {
|
|
5081
|
-
await Promise.resolve();
|
|
5082
|
-
}
|
|
5083
|
-
}
|
|
5084
|
-
validateChildTypes(node, widgetsCache) {
|
|
5085
|
-
const errors = [];
|
|
5086
|
-
const allowedChildTypes = widgetsCache[node.tagName]?.allowed_child_types;
|
|
5087
|
-
if (allowedChildTypes?.length) {
|
|
5088
|
-
for (const child of Array.from(node.children)) {
|
|
5089
|
-
if (!allowedChildTypes.includes(child.tagName)) {
|
|
5090
|
-
errors.push(
|
|
5091
|
-
`"${child.tagName}" is not allowed as a child of "${node.tagName}". Allowed: ${allowedChildTypes.join(", ")}`
|
|
5092
|
-
);
|
|
5093
|
-
}
|
|
5094
|
-
}
|
|
5095
|
-
}
|
|
5096
|
-
for (const child of Array.from(node.children)) {
|
|
5097
|
-
errors.push(...this.validateChildTypes(child, widgetsCache));
|
|
5098
|
-
}
|
|
5099
|
-
return errors;
|
|
5100
|
-
}
|
|
5101
|
-
matchNodeByConfigId(configId) {
|
|
5102
|
-
const node = this.xml.querySelector(`[configuration-id="${configId}"]`);
|
|
5103
|
-
if (!node) {
|
|
5104
|
-
throw new Error(`Configuration id "${configId}" does not have target node.`);
|
|
5105
|
-
}
|
|
5106
|
-
const id = node.getAttribute("id");
|
|
5107
|
-
if (!id) {
|
|
5108
|
-
throw new Error(`Node with configuration id "${configId}" does not have element id.`);
|
|
5109
|
-
}
|
|
5110
|
-
const element = this.api.getContainer(id);
|
|
5111
|
-
if (!element) {
|
|
5112
|
-
throw new Error(`Element with id "${id}" not found but should exist.`);
|
|
5113
|
-
}
|
|
5114
|
-
return {
|
|
5115
|
-
element,
|
|
5116
|
-
node
|
|
5117
|
-
};
|
|
5118
|
-
}
|
|
5119
|
-
async applyProperties() {
|
|
5120
|
-
const configErrors = [];
|
|
5121
|
-
const styleErrors = [];
|
|
5122
|
-
const allConfigIds = /* @__PURE__ */ new Set([
|
|
5123
|
-
...Object.keys(this.elementConfig),
|
|
5124
|
-
...Object.keys(this.elementStylesConfig),
|
|
5125
|
-
...Object.keys(this.elementCustomCSS)
|
|
5126
|
-
]);
|
|
5127
|
-
for (const configId of allConfigIds) {
|
|
5128
|
-
let element, node;
|
|
5129
|
-
try {
|
|
5130
|
-
({ element, node } = this.matchNodeByConfigId(configId));
|
|
5131
|
-
} catch (matchErr) {
|
|
5132
|
-
const msg = matchErr.message;
|
|
5133
|
-
if (this.elementConfig[configId]) {
|
|
5134
|
-
configErrors.push(msg);
|
|
5135
|
-
}
|
|
5136
|
-
if (this.elementStylesConfig[configId] || this.elementCustomCSS[configId]) {
|
|
5137
|
-
styleErrors.push(msg);
|
|
5138
|
-
}
|
|
5139
|
-
continue;
|
|
5140
|
-
}
|
|
5141
|
-
const config = this.elementConfig[configId];
|
|
5142
|
-
if (config) {
|
|
5143
|
-
for (const [propertyName, propertyValue] of Object.entries(config)) {
|
|
5144
|
-
try {
|
|
5145
|
-
this.api.doUpdateElementProperty({
|
|
5146
|
-
elementId: element.id,
|
|
5147
|
-
propertyName,
|
|
5148
|
-
propertyValue,
|
|
5149
|
-
elementType: node.tagName
|
|
5150
|
-
});
|
|
5151
|
-
} catch (error) {
|
|
5152
|
-
configErrors.push(error.message);
|
|
5153
|
-
}
|
|
5154
|
-
}
|
|
5155
|
-
}
|
|
5156
|
-
const styleConfig = this.elementStylesConfig[configId];
|
|
5157
|
-
const hasInvalidStyles = false;
|
|
5158
|
-
if (styleConfig) {
|
|
5159
|
-
const validStylesPropValues = {};
|
|
5160
|
-
for (const [styleName, stylePropValue] of Object.entries(styleConfig)) {
|
|
5161
|
-
if (styleName === "$intention") {
|
|
5162
|
-
continue;
|
|
5163
|
-
} else {
|
|
5164
|
-
validStylesPropValues[styleName] = stylePropValue;
|
|
5165
|
-
}
|
|
5166
|
-
}
|
|
5167
|
-
if (Object.keys(validStylesPropValues).length > 0) {
|
|
5168
|
-
try {
|
|
5169
|
-
this.api.doUpdateElementProperty({
|
|
5170
|
-
elementId: element.id,
|
|
5171
|
-
propertyName: "_styles",
|
|
5172
|
-
propertyValue: validStylesPropValues,
|
|
5173
|
-
elementType: node.tagName
|
|
5174
|
-
});
|
|
5175
|
-
} catch (error) {
|
|
5176
|
-
styleErrors.push(String(error));
|
|
5177
|
-
}
|
|
5178
|
-
}
|
|
5179
|
-
}
|
|
5180
|
-
const intentionCss = typeof styleConfig?.$intention === "string" ? styleConfig.$intention.trim() : "";
|
|
5181
|
-
const fallbackCss = hasInvalidStyles && intentionCss ? intentionCss : "";
|
|
5182
|
-
const mergedCustomCss = mergeCustomCssText(this.elementCustomCSS[configId], fallbackCss);
|
|
5183
|
-
if (mergedCustomCss) {
|
|
5184
|
-
try {
|
|
5185
|
-
this.api.doUpdateElementProperty({
|
|
5186
|
-
elementId: element.id,
|
|
5187
|
-
propertyName: "_styles",
|
|
5188
|
-
propertyValue: { custom_css: mergedCustomCss },
|
|
5189
|
-
elementType: node.tagName
|
|
5190
|
-
});
|
|
5191
|
-
} catch (cssErr) {
|
|
5192
|
-
styleErrors.push(String(cssErr));
|
|
5193
|
-
}
|
|
5194
|
-
}
|
|
5195
|
-
await this.awaitViewRender(element);
|
|
5196
|
-
}
|
|
5197
|
-
return { configErrors, styleErrors };
|
|
5198
|
-
}
|
|
5199
|
-
async build(rootContainer) {
|
|
5200
|
-
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
5201
|
-
new Set(this.xml.querySelectorAll("*")).forEach((node) => {
|
|
5202
|
-
if (!widgetsCache[node.tagName]) {
|
|
5203
|
-
throw new Error(`Unknown widget type: ${node.tagName}`);
|
|
5204
|
-
}
|
|
5205
|
-
});
|
|
5206
|
-
const typesWithRequiredChildren = Object.keys(widgetsCache).filter(
|
|
5207
|
-
(elementType) => getRequiredDefaultChildTemplates(widgetsCache[elementType]).length > 0
|
|
5208
|
-
);
|
|
5209
|
-
typesWithRequiredChildren.forEach((elementType) => {
|
|
5210
|
-
new RequiredChildrenEnforcer(elementType, widgetsCache).enforce(this.xml);
|
|
5211
|
-
});
|
|
5212
|
-
const childTypeErrors = [];
|
|
5213
|
-
for (const rootChild of Array.from(this.xml.children)) {
|
|
5214
|
-
childTypeErrors.push(...this.validateChildTypes(rootChild, widgetsCache));
|
|
5215
|
-
}
|
|
5216
|
-
if (childTypeErrors.length) {
|
|
5217
|
-
throw new Error(`Invalid element structure:
|
|
5218
|
-
${childTypeErrors.join("\n")}`);
|
|
5219
|
-
}
|
|
5220
|
-
const formErrors = [
|
|
5221
|
-
...collectFormAncestorErrors(this.xml),
|
|
5222
|
-
...collectSubmitButtonErrors(this.xml),
|
|
5223
|
-
...collectEmptyMessageErrors(this.xml)
|
|
5224
|
-
];
|
|
5225
|
-
const children = Array.from(this.xml.children);
|
|
5226
|
-
for (const childNode of children) {
|
|
5227
|
-
const modelTree = this.buildModelTree(childNode, widgetsCache);
|
|
5228
|
-
try {
|
|
5229
|
-
const newElement = this.api.createElement({
|
|
5230
|
-
container: rootContainer,
|
|
5231
|
-
model: modelTree,
|
|
5232
|
-
options: { useHistory: false }
|
|
5233
|
-
});
|
|
5234
|
-
if (!newElement?.model) {
|
|
5235
|
-
throw new Error(CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE);
|
|
5236
|
-
}
|
|
5237
|
-
this.rootContainers.push(newElement);
|
|
5238
|
-
await this.awaitViewRender(newElement);
|
|
5239
|
-
} catch (e) {
|
|
5240
|
-
const attempToRestoreInvalidContainer = this.api.getContainer(modelTree.id);
|
|
5241
|
-
if (attempToRestoreInvalidContainer) {
|
|
5242
|
-
this.api.deleteElement({ container: attempToRestoreInvalidContainer });
|
|
5243
|
-
}
|
|
5244
|
-
throw e;
|
|
5245
|
-
}
|
|
5246
|
-
}
|
|
5247
|
-
const { configErrors, styleErrors } = await this.applyProperties();
|
|
5248
|
-
if (typeof window !== "undefined") {
|
|
5249
|
-
const targetWindow = window.top || window;
|
|
5250
|
-
targetWindow.dispatchEvent(
|
|
5251
|
-
new CustomEvent("elementor/composition/built", {
|
|
5252
|
-
detail: { rootContainers: this.rootContainers.map((c) => c.id) }
|
|
5253
|
-
})
|
|
5254
|
-
);
|
|
5255
|
-
}
|
|
5256
|
-
return {
|
|
5257
|
-
configErrors,
|
|
5258
|
-
styleErrors,
|
|
5259
|
-
formErrors,
|
|
5260
|
-
rootContainers: [...this.rootContainers]
|
|
5261
|
-
};
|
|
5262
|
-
}
|
|
5263
|
-
};
|
|
5264
|
-
|
|
5265
|
-
// src/utils/tracking.ts
|
|
5266
|
-
import { trackEvent } from "@elementor/events";
|
|
5267
|
-
var trackCanvasEvent = (data) => {
|
|
5268
|
-
trackEvent(data);
|
|
5269
|
-
};
|
|
5270
|
-
|
|
5271
|
-
// src/mcp/utils/get-composition-target-container.ts
|
|
5272
|
-
import { COMPONENT_DOCUMENT_TYPE } from "@elementor/editor-documents";
|
|
5273
|
-
function getCompositionTargetContainer(documentContainer, documentType) {
|
|
5274
|
-
const firstChild = documentContainer.children?.[0];
|
|
5275
|
-
if (documentType === COMPONENT_DOCUMENT_TYPE && firstChild) {
|
|
5276
|
-
return firstChild;
|
|
5277
|
-
}
|
|
5278
|
-
return documentContainer;
|
|
5279
|
-
}
|
|
5280
|
-
|
|
5281
|
-
// src/mcp/tools/build-composition/prompt.ts
|
|
5282
|
-
import { toolPrompts } from "@elementor/editor-mcp";
|
|
5283
|
-
var BUILD_COMPOSITIONS_GUIDE_URI = "elementor://canvas/tools/build-compositions-guide";
|
|
5284
|
-
var generatePrompt = () => {
|
|
5285
|
-
const buildCompositionsToolPrompt = toolPrompts("build-compositions");
|
|
5286
|
-
buildCompositionsToolPrompt.description(`
|
|
5287
|
-
# RESOURCES (Read before use)
|
|
5288
|
-
- [elementor://global-classes] - Check FIRST for reusable classes
|
|
5289
|
-
- [elementor://global-variables] - ONLY use variables defined here
|
|
5290
|
-
- [${AVAILABLE_WIDGETS_URI}/v4]
|
|
5291
|
-
|
|
5292
|
-
# TOOL SUPPORT
|
|
5293
|
-
This tool support v4 elements only
|
|
5294
|
-
|
|
5295
|
-
# WORKFLOW
|
|
5296
|
-
1. Check/create global classes via "manage-global-classes" tool
|
|
5297
|
-
2. Build composition (THIS TOOL) - minimal inline styles
|
|
5298
|
-
3. Apply classes via "apply-global-class" tool
|
|
5299
|
-
|
|
5300
|
-
# XML STRUCTURE
|
|
5301
|
-
- Use widget tags: \`<e-button configuration-id="btn1"></e-button>\`
|
|
5302
|
-
- Containers: "e-flexbox", "e-div-block", "e-tabs"
|
|
5303
|
-
- Every element needs unique "configuration-id"
|
|
5304
|
-
- No attributes, classes, IDs, or text nodes in XML
|
|
5305
|
-
|
|
5306
|
-
## NESTED ELEMENTS
|
|
5307
|
-
Some elements have internal tree structures (nesting). When using these elements, you MUST build the FULL tree in XML.
|
|
5308
|
-
- Check \`llm_guidance.nesting\` in widget schemas for structure requirements
|
|
5309
|
-
- \`llm_guidance.required_direct_children\` lists element types that must appear as direct child tags in XML (from widget defaults)
|
|
5310
|
-
- \`allowed_child_types\` lists which element types can be nested inside
|
|
5311
|
-
- \`allowed_parents\` lists which element types this element can be placed inside
|
|
5312
|
-
|
|
5313
|
-
# CONFIGURATION
|
|
5314
|
-
- Map configuration-id \u2192 elementConfig (props) + style (raw CSS declarations)
|
|
5315
|
-
- elementConfig PropValues require \`$$type\` matching schema
|
|
5316
|
-
- style is raw CSS (property \u2192 value strings); the server converts it to native styles and stores any unconvertible declarations as the element custom CSS
|
|
5317
|
-
- NO LINKS in configuration
|
|
5318
|
-
- Retry on errors up to 10x
|
|
5319
|
-
- Check \`llm_guidance.default_settings\` in widget schemas \u2014 omit only keys listed there from elementConfig unless the user explicitly asks to change them
|
|
5320
|
-
|
|
5321
|
-
# DYNAMIC TAGS
|
|
5322
|
-
- A value can be made dynamic wherever its schema exposes a \`"$$type": "dynamic"\` variant. This may be the property root OR a NESTED field (e.g. an image's \`src\`, not the whole \`image\`).
|
|
5323
|
-
- Put the dynamic object EXACTLY at that node, in place of the static variant. The variant's \`name\` lists the allowed tags; read [${DYNAMIC_TAGS_URI}] for each tag's settings schema.
|
|
5324
|
-
- Provide at that node: \`{ "$$type": "dynamic", "value": { "name": "<allowed tag>", "settings": { ... } } }\`
|
|
5325
|
-
- Example (image): \`{ "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }\`
|
|
5326
|
-
- Do NOT send \`group\` (it is resolved automatically). Populate \`settings\` strictly per the tag's schema; use \`{}\` only when it has none.
|
|
5327
|
-
|
|
5328
|
-
Note about configuration ids: These names are visible to the end-user, make sure they make sense, related and relevant.
|
|
5329
|
-
|
|
5330
|
-
# DESIGN PHILOSOPHY: CONTEXT-DRIVEN CREATIVITY
|
|
5331
|
-
|
|
5332
|
-
**Use the user's context aggressively.** Business type, brand personality, target audience, and purpose should drive every design decision. A law firm needs gravitas; a children's app needs playfulness. Don't default to generic.
|
|
5333
|
-
|
|
5334
|
-
## SIZING: DEFAULT IS NO SIZE (CRITICAL)
|
|
5335
|
-
|
|
5336
|
-
**DO NOT specify height or width unless you have a specific visual reason.**
|
|
5337
|
-
|
|
5338
|
-
Flexbox and CSS already handle sizing automatically:
|
|
5339
|
-
- Containers grow to fit their content
|
|
5340
|
-
- Flex children distribute space via flex properties, not width/height
|
|
5341
|
-
- Text elements size to their content
|
|
5342
|
-
|
|
5343
|
-
WHEN TO SPECIFY SIZE:
|
|
5344
|
-
- min-height on ROOT section for viewport-spanning hero (use min-height, NOT height)
|
|
5345
|
-
- max-width for contained content areas (e.g., max-width: 60rem)
|
|
5346
|
-
- Explicit aspect ratios for media containers
|
|
5347
|
-
|
|
5348
|
-
NEVER SPECIFY:
|
|
5349
|
-
- height on nested containers (causes overflow)
|
|
5350
|
-
- width on flex children (use flex-basis or gap instead)
|
|
5351
|
-
- 100vh on anything except root-level sections
|
|
5352
|
-
- Any size "just to be safe" - if unsure, OMIT IT
|
|
5353
|
-
|
|
5354
|
-
vh units are VIEWPORT-relative. Nested 100vh inside 100vh = 200vh overflow.
|
|
5355
|
-
|
|
5356
|
-
GOOD: \`<e-flexbox>content naturally sizes</e-flexbox>\`
|
|
5357
|
-
BAD: \`<e-flexbox style="height:100vh"><e-div-block style="height:100vh">overflow</e-div-block></e-flexbox>\`
|
|
5358
|
-
|
|
5359
|
-
## Layout Variety (Break the Template)
|
|
5360
|
-
- AVOID: Full-width 100vh hero \u2192 three columns \u2192 testimonials \u2192 CTA (every AI does this)
|
|
5361
|
-
- VARY heights: Use auto-height sections with generous padding (6rem+). Let content breathe
|
|
5362
|
-
- VARY widths: Not everything spans full width. Use contained sections (max-width: 960px) mixed with edge-to-edge
|
|
5363
|
-
- ASYMMETRIC grids: 2:1, 1:3, offset layouts. Avoid equal column widths
|
|
5364
|
-
- Negative space as design element: Large margins create focus and sophistication
|
|
5365
|
-
- Break alignment intentionally: Offset headings, overlapping elements, broken grids
|
|
5366
|
-
|
|
5367
|
-
## Visual Depth & Effects
|
|
5368
|
-
- Layer elements: Overlapping cards, text over images, floating elements
|
|
5369
|
-
- Subtle shadows with color tint (not pure black): \`box-shadow: 0 20px 60px rgba(<brand-color-here>, 0.15)\`
|
|
5370
|
-
- Gradient overlays on images for text readability
|
|
5371
|
-
- Border radius variation: Mix sharp (0) and soft (1rem+) corners purposefully
|
|
5372
|
-
- Backdrop blur for glassmorphism where appropriate
|
|
5373
|
-
- Micro-interactions via CSS: hover transforms, transitions (0.3s ease)
|
|
5374
|
-
|
|
5375
|
-
## Typography with Character
|
|
5376
|
-
- Display fonts for headlines (from user's brand or contextually appropriate)
|
|
5377
|
-
- Size contrast: 4rem+ headlines vs 1rem body. Make hierarchy unmistakable
|
|
5378
|
-
- Letter-spacing: Tight for large headlines (-0.02em), loose for small caps (0.1em)
|
|
5379
|
-
- Line-height: Tight for headlines (1.1), generous for body (1.6-1.8)
|
|
5380
|
-
- Text decoration: Underlines, highlights, gradient text for emphasis
|
|
5381
|
-
|
|
5382
|
-
## Color with Purpose
|
|
5383
|
-
- Extract palette from user context (brand colors, industry norms, mood)
|
|
5384
|
-
- 60-30-10 rule: dominant, secondary, accent
|
|
5385
|
-
- Tinted neutrals over pure grays: warm (#faf8f5, #2d2a26) or cool (#f5f7fa, #1e2430)
|
|
5386
|
-
- Color blocking: Large colored sections create visual rhythm
|
|
5387
|
-
- Gradient directions: Diagonal (135deg, 225deg) feel more dynamic than vertical
|
|
5388
|
-
|
|
5389
|
-
## Spacing Strategy
|
|
5390
|
-
- Section padding: 6rem-10rem vertical, creating breathing room
|
|
5391
|
-
- Rhythm variation: Tight groups (2rem) with generous gaps between (6rem)
|
|
5392
|
-
- Use rem/em exclusively for responsive scaling
|
|
5393
|
-
- Generous padding on CTAs: min 1rem 2.5rem
|
|
5394
|
-
|
|
5395
|
-
# HARD CONSTRAINTS
|
|
5396
|
-
- Variables ONLY from [elementor://global-variables] (others throw errors)
|
|
5397
|
-
- Avoid SVG widgets unless assets are pre-uploaded
|
|
5398
|
-
- Check \`llm_guidance\` in widget schemas (\`default_styles\`, nesting, required children)
|
|
5399
|
-
|
|
5400
|
-
# PARAMETERS
|
|
5401
|
-
- **xmlStructure**: Valid XML with configuration-id attributes
|
|
5402
|
-
- **elementConfig**: configuration-id \u2192 widget PropValues
|
|
5403
|
-
- **style**: configuration-id \u2192 raw CSS declarations (property \u2192 value strings; no selectors)
|
|
5404
|
-
`);
|
|
5405
|
-
buildCompositionsToolPrompt.example(`
|
|
5406
|
-
Section with heading + button (NO explicit heights - content sizes naturally):
|
|
5407
|
-
{
|
|
5408
|
-
xmlStructure: "<e-flexbox configuration-id="Main Section"><e-heading configuration-id="Section Title"></e-heading><e-button configuration-id="Call to Action"></e-button></e-flexbox>",
|
|
5409
|
-
elementConfig: {
|
|
5410
|
-
"section1": { "tag": { "$$type": "string", "value": "section" } }
|
|
5411
|
-
},
|
|
5412
|
-
style: {
|
|
5413
|
-
"Section Title": {
|
|
5414
|
-
"padding": "6rem 4rem",
|
|
5415
|
-
"background": "linear-gradient(135deg, #faf8f5 0%, #f0ebe4 100%)",
|
|
5416
|
-
"font-size": "3.5rem",
|
|
5417
|
-
"color": "#2d2a26"
|
|
5418
|
-
}
|
|
5419
|
-
}
|
|
5420
|
-
}
|
|
5421
|
-
Note: No height/width specified on any element - flexbox handles layout automatically.
|
|
5422
|
-
`);
|
|
5423
|
-
buildCompositionsToolPrompt.parameter(
|
|
5424
|
-
"xmlStructure",
|
|
5425
|
-
`Valid XML structure with custom elementor tags and configuration-id attributes.`
|
|
5426
|
-
);
|
|
5427
|
-
buildCompositionsToolPrompt.parameter("elementConfig", `Record mapping configuration IDs to widget PropValues.`);
|
|
5428
|
-
buildCompositionsToolPrompt.parameter(
|
|
5429
|
-
"style",
|
|
5430
|
-
`Record mapping configuration IDs to raw CSS declarations (property \u2192 value strings).`
|
|
5431
|
-
);
|
|
5432
|
-
buildCompositionsToolPrompt.instruction(
|
|
5433
|
-
`Element IDs in the returned XML represent actual widgets. Use these IDs for subsequent styling or configuration changes.`
|
|
5434
|
-
);
|
|
5435
|
-
return buildCompositionsToolPrompt.prompt();
|
|
5436
|
-
};
|
|
5437
|
-
|
|
5438
|
-
// src/mcp/tools/build-composition/schema.ts
|
|
5439
|
-
import { z } from "@elementor/schema";
|
|
5440
|
-
var inputSchema = {
|
|
5441
|
-
xmlStructure: z.string().describe("The XML structure representing the composition to be built"),
|
|
5442
|
-
elementConfig: z.record(
|
|
5443
|
-
z.string().describe("The configuration id"),
|
|
5444
|
-
z.record(
|
|
5445
|
-
z.string().describe("property name"),
|
|
5446
|
-
z.any().describe(`The PropValue for the property, refer to ${WIDGET_SCHEMA_URI}`)
|
|
5447
|
-
)
|
|
5448
|
-
).describe("A record mapping element IDs to their configuration objects. REQUIRED"),
|
|
5449
|
-
style: z.record(
|
|
5450
|
-
z.string().describe("The configuration id"),
|
|
5451
|
-
z.record(
|
|
5452
|
-
z.string().describe('A CSS property name, e.g. "color", "padding".'),
|
|
5453
|
-
z.string().describe('A CSS value, e.g. "6rem 4rem", "#2d2a26".')
|
|
5454
|
-
)
|
|
5455
|
-
).describe(
|
|
5456
|
-
"A record mapping element configuration IDs to their raw CSS declarations (property\u2192value). Converted to native styles server-side; any declaration that cannot be converted is stored as the element custom CSS."
|
|
5457
|
-
).default({})
|
|
5458
|
-
};
|
|
5459
|
-
var outputSchema = {
|
|
5460
|
-
errors: z.string().describe("Error message if the composition building failed").optional(),
|
|
5461
|
-
xmlStructure: z.string().describe(
|
|
5462
|
-
"The built XML structure as a string. Must use this XML after completion of building the composition, it contains real IDs."
|
|
5463
|
-
).optional(),
|
|
5464
|
-
llm_instructions: z.string().describe("Instructions what to do next, Important to follow these instructions!").optional()
|
|
5465
|
-
};
|
|
5466
|
-
|
|
5467
|
-
// src/mcp/tools/build-composition/xml-leaf-wrapper.ts
|
|
5468
|
-
var DIV_BLOCK_TAG = "e-div-block";
|
|
5469
|
-
var ZERO_SPACING = {
|
|
5470
|
-
$$type: "size",
|
|
5471
|
-
value: {
|
|
5472
|
-
size: {
|
|
5473
|
-
$$type: "number",
|
|
5474
|
-
value: 0
|
|
5475
|
-
},
|
|
5476
|
-
unit: {
|
|
5477
|
-
$$type: "string",
|
|
5478
|
-
value: "px"
|
|
5479
|
-
}
|
|
5480
|
-
}
|
|
5481
|
-
};
|
|
5482
|
-
function adaptLeafRootParams(params) {
|
|
5483
|
-
const doc = new DOMParser().parseFromString(params.xmlStructure, "application/xml");
|
|
5484
|
-
const rootElement = doc.documentElement;
|
|
5485
|
-
if (!isLeafWidget(rootElement.tagName, params.widgetsCache)) {
|
|
5486
|
-
return params;
|
|
5487
|
-
}
|
|
5488
|
-
const wrapperConfigId = getDivBlockWrapperConfigId(params.widgetsCache);
|
|
5489
|
-
return {
|
|
5490
|
-
...params,
|
|
5491
|
-
xmlStructure: serializeWrapped(doc, rootElement, wrapperConfigId),
|
|
5492
|
-
stylesConfig: {
|
|
5493
|
-
...params.stylesConfig,
|
|
5494
|
-
[wrapperConfigId]: {
|
|
5495
|
-
margin: ZERO_SPACING,
|
|
5496
|
-
padding: ZERO_SPACING,
|
|
5497
|
-
...params.stylesConfig[wrapperConfigId]
|
|
5498
|
-
}
|
|
5499
|
-
}
|
|
5500
|
-
};
|
|
5501
|
-
}
|
|
5502
|
-
function getDivBlockWrapperConfigId(widgetsCache) {
|
|
5503
|
-
return widgetsCache[DIV_BLOCK_TAG]?.title ?? DIV_BLOCK_TAG;
|
|
5504
|
-
}
|
|
5505
|
-
function isLeafWidget(tagName, widgetsCache) {
|
|
5506
|
-
return widgetsCache[tagName]?.elType === "widget";
|
|
5507
|
-
}
|
|
5508
|
-
function serializeWrapped(doc, rootElement, wrapperConfigId) {
|
|
5509
|
-
const wrapper = doc.createElement(DIV_BLOCK_TAG);
|
|
5510
|
-
wrapper.setAttribute("configuration-id", wrapperConfigId);
|
|
5511
|
-
wrapper.appendChild(rootElement.cloneNode(true));
|
|
5512
|
-
const wrappedDoc = new DOMParser().parseFromString(`<${DIV_BLOCK_TAG} />`, "application/xml");
|
|
5513
|
-
wrappedDoc.replaceChild(wrapper, wrappedDoc.documentElement);
|
|
5514
|
-
return new XMLSerializer().serializeToString(wrappedDoc);
|
|
5515
|
-
}
|
|
5516
|
-
|
|
5517
|
-
// src/mcp/tools/build-composition/tool.ts
|
|
5518
|
-
var ELEMENT_ADDED_EVENT = "elementor/canvas/element-added";
|
|
5519
|
-
var initBuildCompositionsTool = (reg) => {
|
|
5520
|
-
const { addTool, resource } = reg;
|
|
5521
|
-
resource(
|
|
5522
|
-
"build-compositions-guide",
|
|
5523
|
-
BUILD_COMPOSITIONS_GUIDE_URI,
|
|
5524
|
-
{
|
|
5525
|
-
title: "Build Compositions Guide",
|
|
5526
|
-
description: "Detailed guide for using the build-compositions tool",
|
|
5527
|
-
mimeType: "text/plain"
|
|
5528
|
-
},
|
|
5529
|
-
async (uri) => ({
|
|
5530
|
-
contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt() }]
|
|
5531
|
-
})
|
|
5532
|
-
);
|
|
5533
|
-
addTool({
|
|
5534
|
-
name: "build-compositions",
|
|
5535
|
-
description: "Build V4 element compositions on the Elementor canvas. Read the guide resource before use.",
|
|
5536
|
-
schema: inputSchema,
|
|
5537
|
-
requiredResources: [
|
|
5538
|
-
{ description: "Build compositions guide", uri: BUILD_COMPOSITIONS_GUIDE_URI },
|
|
5539
|
-
{ description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
|
|
5540
|
-
{ description: "Global Classes", uri: "elementor://global-classes" },
|
|
5541
|
-
{ description: "Global Variables", uri: "elementor://global-variables" },
|
|
5542
|
-
{ description: "Styles best practices", uri: BEST_PRACTICES_URI },
|
|
5543
|
-
{ description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 },
|
|
5544
|
-
{ description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
|
|
5545
|
-
],
|
|
5546
|
-
outputSchema,
|
|
5547
|
-
handler: async (rawParams) => {
|
|
5548
|
-
assertCompositionXmlUsesV4WidgetsOnly(rawParams.xmlStructure);
|
|
5549
|
-
const { stylesConfig: convertedStyles, customCSS } = await convertCompositionStyles(rawParams.style);
|
|
5550
|
-
const { xmlStructure, elementConfig, stylesConfig } = adaptLeafRootParams({
|
|
5551
|
-
...rawParams,
|
|
5552
|
-
stylesConfig: convertedStyles,
|
|
5553
|
-
widgetsCache: getWidgetsCache9() ?? {}
|
|
5554
|
-
});
|
|
5555
|
-
let generatedXML = "";
|
|
5556
|
-
const errors = [];
|
|
5557
|
-
const rootContainers = [];
|
|
5558
|
-
const documentContainer = getContainer5("document");
|
|
5559
|
-
const currentDocument = getCurrentDocument();
|
|
5560
|
-
const targetContainer = getCompositionTargetContainer(documentContainer, currentDocument?.type.value);
|
|
5561
|
-
try {
|
|
5562
|
-
const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
|
|
5563
|
-
createElement: createElement13,
|
|
5564
|
-
deleteElement: deleteElement2,
|
|
5565
|
-
getWidgetsCache: getWidgetsCache9
|
|
5566
|
-
});
|
|
5567
|
-
compositionBuilder.setElementConfig(elementConfig);
|
|
5568
|
-
compositionBuilder.setStylesConfig(stylesConfig);
|
|
5569
|
-
compositionBuilder.setCustomCSS(customCSS);
|
|
5570
|
-
const {
|
|
5571
|
-
configErrors,
|
|
5572
|
-
formErrors,
|
|
5573
|
-
rootContainers: generatedRootContainers
|
|
5574
|
-
} = await compositionBuilder.build(targetContainer);
|
|
5575
|
-
rootContainers.push(...generatedRootContainers);
|
|
5576
|
-
generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
|
|
5577
|
-
rootContainers.forEach((container) => {
|
|
5578
|
-
const elementData = container.model?.toJSON();
|
|
5579
|
-
if (elementData) {
|
|
5580
|
-
onElementAdded(elementData);
|
|
5581
|
-
}
|
|
5582
|
-
});
|
|
5583
|
-
Object.values(stylesConfig).forEach((styleValue) => {
|
|
5584
|
-
dispatchMcpStylesAppliedEvent({ styleValue });
|
|
5585
|
-
});
|
|
5586
|
-
if (configErrors.length) {
|
|
5587
|
-
errors.push(...configErrors.map((msg) => new Error(msg)));
|
|
5588
|
-
}
|
|
5589
|
-
if (formErrors.length) {
|
|
5590
|
-
errors.push(...formErrors.map((msg) => new Error(msg)));
|
|
5591
|
-
}
|
|
5592
|
-
} catch (error) {
|
|
5593
|
-
errors.push(error);
|
|
5594
|
-
}
|
|
5595
|
-
if (errors.length) {
|
|
5596
|
-
rootContainers.forEach((rootContainer) => {
|
|
5597
|
-
deleteElement2({
|
|
5598
|
-
container: rootContainer,
|
|
5599
|
-
options: { useHistory: false }
|
|
5600
|
-
});
|
|
5601
|
-
});
|
|
5602
|
-
const errorMessages = errors.map((e) => {
|
|
5603
|
-
if (typeof e === "string") {
|
|
5604
|
-
return e;
|
|
5605
|
-
}
|
|
5606
|
-
if (e instanceof Error) {
|
|
5607
|
-
return e.message || String(e);
|
|
5608
|
-
}
|
|
5609
|
-
if (typeof e === "object" && e !== null) {
|
|
5610
|
-
return JSON.stringify(e);
|
|
5611
|
-
}
|
|
5612
|
-
return String(e);
|
|
5613
|
-
}).filter(
|
|
5614
|
-
(msg) => msg && msg.trim() !== "" && msg !== "{}" && msg !== "null" && msg !== "undefined"
|
|
5615
|
-
);
|
|
5616
|
-
if (errorMessages.length === 0) {
|
|
5617
|
-
throw new Error(
|
|
5618
|
-
"Failed to build composition: Unknown error occurred. No error details available."
|
|
5619
|
-
);
|
|
5620
|
-
}
|
|
5621
|
-
const errorText = `Failed to build composition with the following errors:
|
|
5622
|
-
|
|
5623
|
-
${errorMessages.join(
|
|
5624
|
-
"\n\n"
|
|
5625
|
-
)}`;
|
|
5626
|
-
throw new Error(errorText);
|
|
5627
|
-
}
|
|
5628
|
-
return {
|
|
5629
|
-
xmlStructure: generatedXML,
|
|
5630
|
-
errors: errors?.length ? errors.map((e) => typeof e === "string" ? e : e.message).join("\n\n") : void 0,
|
|
5631
|
-
llm_instructions: `The composition was built successfully with element IDs embedded in the XML.
|
|
5632
|
-
|
|
5633
|
-
**CRITICAL NEXT STEPS** (Follow in order):
|
|
5634
|
-
1. **Apply Global Classes**: Use "apply-global-class" tool to apply the global classes you created BEFORE building this composition
|
|
5635
|
-
- Check the created element IDs in the returned XML
|
|
5636
|
-
- Apply semantic classes (heading-primary, button-cta, etc.) to appropriate elements
|
|
5637
|
-
|
|
5638
|
-
2. **Fine-tune if needed**: Use "configure-element" tool only for element-specific adjustments that don't warrant global classes
|
|
5639
|
-
|
|
5640
|
-
Remember: Global classes ensure design consistency and reusability. Don't skip applying them!
|
|
5641
|
-
`
|
|
5642
|
-
};
|
|
5643
|
-
}
|
|
5644
|
-
});
|
|
5645
|
-
};
|
|
5646
|
-
async function convertCompositionStyles(style) {
|
|
5647
|
-
const stylesConfig = {};
|
|
5648
|
-
const customCSS = {};
|
|
5649
|
-
if (!style || Object.keys(style).length === 0) {
|
|
5650
|
-
return { stylesConfig, customCSS };
|
|
5651
|
-
}
|
|
5652
|
-
const results = await convertStyleBlocksToAtomic(style);
|
|
5653
|
-
for (const [configId, { props, customCss }] of Object.entries(results)) {
|
|
5654
|
-
stylesConfig[configId] = props;
|
|
5655
|
-
if (customCss) {
|
|
5656
|
-
customCSS[configId] = customCss;
|
|
5657
|
-
}
|
|
5658
|
-
}
|
|
5659
|
-
return { stylesConfig, customCSS };
|
|
5660
|
-
}
|
|
5661
|
-
function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
|
|
5662
|
-
const doc = new DOMParser().parseFromString(xmlStructure, "application/xml");
|
|
5663
|
-
if (doc.querySelector("parsererror")) {
|
|
5664
|
-
throw new Error("Failed to parse XML string: " + doc);
|
|
5665
|
-
}
|
|
5666
|
-
const widgetsCache = getWidgetsCache9() ?? {};
|
|
5667
|
-
for (const node of doc.querySelectorAll("*")) {
|
|
5668
|
-
const type = node.tagName;
|
|
5669
|
-
const widgetData = widgetsCache[type];
|
|
5670
|
-
if (!widgetData) {
|
|
5671
|
-
continue;
|
|
5672
|
-
}
|
|
5673
|
-
if (widgetData.elType !== "widget") {
|
|
5674
|
-
continue;
|
|
5675
|
-
}
|
|
5676
|
-
if (!isWidgetAvailableForLLM(widgetData) || !widgetData.atomic_props_schema) {
|
|
5677
|
-
throw new Error(`This tool does not support element type: ${type}`);
|
|
5678
|
-
}
|
|
5679
|
-
}
|
|
5680
|
-
}
|
|
5681
|
-
function onElementAdded(element) {
|
|
5682
|
-
const elType = element.elType ?? "";
|
|
5683
|
-
const widgetType = element.widgetType ?? "";
|
|
5684
|
-
const elementName = elType === "widget" ? widgetType : elType;
|
|
5685
|
-
trackCanvasEvent({
|
|
5686
|
-
eventName: "add_element",
|
|
5687
|
-
executed_by: "mcp_tool",
|
|
5688
|
-
element_name: elementName,
|
|
5689
|
-
element_type: elType,
|
|
5690
|
-
widget_type: widgetType
|
|
5691
|
-
});
|
|
5692
|
-
const event = {
|
|
5693
|
-
element,
|
|
5694
|
-
executedBy: "mcp_tool"
|
|
5695
|
-
};
|
|
5696
|
-
window.dispatchEvent(new CustomEvent(ELEMENT_ADDED_EVENT, { detail: event }));
|
|
5697
|
-
if (element.elements?.length) {
|
|
5698
|
-
element.elements?.forEach((childElement) => {
|
|
5699
|
-
onElementAdded(childElement);
|
|
5700
|
-
});
|
|
5701
|
-
}
|
|
5702
|
-
}
|
|
5703
|
-
|
|
5704
|
-
// src/mcp/tools/configure-element/tool.ts
|
|
5705
|
-
import { getContainer as getContainer6, getWidgetsCache as getWidgetsCache10 } from "@elementor/editor-elements";
|
|
5706
|
-
import { dispatchMcpStylesAppliedEvent as dispatchMcpStylesAppliedEvent2 } from "@elementor/editor-mcp";
|
|
5707
|
-
import { Schema as Schema4 } from "@elementor/editor-props";
|
|
5708
|
-
|
|
5709
4822
|
// src/mcp/tools/configure-element/prompt.ts
|
|
5710
|
-
import { toolPrompts
|
|
4823
|
+
import { toolPrompts } from "@elementor/editor-mcp";
|
|
5711
4824
|
var CONFIGURE_ELEMENT_GUIDE_URI = "elementor://canvas/tools/configure-element-guide";
|
|
5712
|
-
var
|
|
5713
|
-
const configureElementToolPrompt =
|
|
4825
|
+
var generatePrompt = () => {
|
|
4826
|
+
const configureElementToolPrompt = toolPrompts("configure-element");
|
|
5714
4827
|
configureElementToolPrompt.description(`
|
|
5715
4828
|
Configure an existing element on the page.
|
|
5716
4829
|
|
|
@@ -5818,11 +4931,11 @@ NO Advanced tab. Never mention Advanced tab.
|
|
|
5818
4931
|
`);
|
|
5819
4932
|
return configureElementToolPrompt.prompt();
|
|
5820
4933
|
};
|
|
5821
|
-
var CONFIGURE_ELEMENT_GUIDE_TEXT =
|
|
4934
|
+
var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt();
|
|
5822
4935
|
|
|
5823
4936
|
// src/mcp/tools/configure-element/schema.ts
|
|
5824
4937
|
import { z as z2 } from "@elementor/schema";
|
|
5825
|
-
var
|
|
4938
|
+
var inputSchema = {
|
|
5826
4939
|
propertiesToChange: z2.record(
|
|
5827
4940
|
z2.string().describe("The property name."),
|
|
5828
4941
|
z2.any().describe(`PropValue, refer to [${WIDGET_SCHEMA_URI}] by correct type, as appears in elementType`),
|
|
@@ -5839,7 +4952,7 @@ var inputSchema2 = {
|
|
|
5839
4952
|
elementType: z2.string().describe("The type of the element to retrieve the schema"),
|
|
5840
4953
|
elementId: z2.string().describe("The unique id of the element to configure")
|
|
5841
4954
|
};
|
|
5842
|
-
var
|
|
4955
|
+
var outputSchema = {
|
|
5843
4956
|
success: z2.boolean().describe(
|
|
5844
4957
|
"Whether the configuration change was successful, only if propertyName and propertyValue are provided"
|
|
5845
4958
|
)
|
|
@@ -5857,27 +4970,27 @@ var initConfigureElementTool = (reg) => {
|
|
|
5857
4970
|
mimeType: "text/plain"
|
|
5858
4971
|
},
|
|
5859
4972
|
async (uri) => ({
|
|
5860
|
-
contents: [{ uri: uri.href, mimeType: "text/plain", text:
|
|
4973
|
+
contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt() }]
|
|
5861
4974
|
})
|
|
5862
4975
|
);
|
|
5863
4976
|
addTool({
|
|
5864
4977
|
name: "configure-element",
|
|
5865
4978
|
description: "Configure an existing V4 element's properties and styles. Read the guide resource before use.",
|
|
5866
|
-
schema:
|
|
5867
|
-
outputSchema
|
|
4979
|
+
schema: inputSchema,
|
|
4980
|
+
outputSchema,
|
|
5868
4981
|
requiredResources: [
|
|
5869
4982
|
{ description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
|
|
5870
4983
|
{ description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI },
|
|
5871
4984
|
{ description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
|
|
5872
4985
|
],
|
|
5873
4986
|
handler: async ({ elementId, propertiesToChange, elementType, style }) => {
|
|
5874
|
-
const widgetData =
|
|
4987
|
+
const widgetData = getWidgetsCache6()?.[elementType];
|
|
5875
4988
|
if (!widgetData) {
|
|
5876
4989
|
throw new Error(
|
|
5877
4990
|
`Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
|
|
5878
4991
|
);
|
|
5879
4992
|
}
|
|
5880
|
-
const container =
|
|
4993
|
+
const container = getContainer5(elementId);
|
|
5881
4994
|
if (!container) {
|
|
5882
4995
|
throw new Error(`Element with id ${elementId} not found`);
|
|
5883
4996
|
}
|
|
@@ -5893,7 +5006,7 @@ var initConfigureElementTool = (reg) => {
|
|
|
5893
5006
|
const propertiesToUpdate = resolveCanonicalPropKeys(elementType, propertiesToChange);
|
|
5894
5007
|
const toUpdate = Object.entries(propertiesToUpdate);
|
|
5895
5008
|
for (const [propertyName, propertyValue] of toUpdate) {
|
|
5896
|
-
if (!
|
|
5009
|
+
if (!Schema2.isPropKeyConfigurable(propertyName)) {
|
|
5897
5010
|
throw new Error(`Not allowed to update ${propertyName}`);
|
|
5898
5011
|
}
|
|
5899
5012
|
try {
|
|
@@ -5942,7 +5055,7 @@ async function applyStyleFromCss(opts) {
|
|
|
5942
5055
|
propertyValue: styleValue,
|
|
5943
5056
|
customCssWriteMode: "merge-with-stored"
|
|
5944
5057
|
});
|
|
5945
|
-
|
|
5058
|
+
dispatchMcpStylesAppliedEvent({ styleValue });
|
|
5946
5059
|
} catch (error) {
|
|
5947
5060
|
throw new Error(
|
|
5948
5061
|
createUpdateErrorMessage({
|
|
@@ -5969,13 +5082,13 @@ Provide styling as raw CSS via the "style" parameter (a flat map of CSS property
|
|
|
5969
5082
|
}
|
|
5970
5083
|
|
|
5971
5084
|
// src/mcp/tools/get-element-config/tool.ts
|
|
5972
|
-
import { getContainer as
|
|
5973
|
-
import { Schema as
|
|
5085
|
+
import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
|
|
5086
|
+
import { Schema as Schema3 } from "@elementor/editor-props";
|
|
5974
5087
|
import { z as z3 } from "@elementor/schema";
|
|
5975
5088
|
var schema = {
|
|
5976
5089
|
elementId: z3.string()
|
|
5977
5090
|
};
|
|
5978
|
-
var
|
|
5091
|
+
var outputSchema2 = {
|
|
5979
5092
|
properties: z3.record(z3.string(), z3.any()).describe("A record mapping PropTypes to their corresponding PropValues"),
|
|
5980
5093
|
style: z3.record(z3.string(), z3.any()).describe("A record mapping StyleSchema properties to their corresponding PropValues"),
|
|
5981
5094
|
childElements: z3.array(
|
|
@@ -6002,14 +5115,14 @@ var initGetElementConfigTool = (reg) => {
|
|
|
6002
5115
|
name: "get-element-configuration-values",
|
|
6003
5116
|
description: "Retrieve the element's configuration PropValues for a specific element by unique ID.",
|
|
6004
5117
|
schema,
|
|
6005
|
-
outputSchema:
|
|
5118
|
+
outputSchema: outputSchema2,
|
|
6006
5119
|
handler: async ({ elementId }) => {
|
|
6007
|
-
const element =
|
|
5120
|
+
const element = getContainer6(elementId);
|
|
6008
5121
|
if (!element) {
|
|
6009
5122
|
throw new Error(`Element with ID ${elementId} not found.`);
|
|
6010
5123
|
}
|
|
6011
5124
|
const elementType = element.model.get("widgetType") || element.model.get("elType") || "";
|
|
6012
|
-
const widgetData =
|
|
5125
|
+
const widgetData = getWidgetsCache7()?.[elementType];
|
|
6013
5126
|
if (!widgetData) {
|
|
6014
5127
|
throw new Error(
|
|
6015
5128
|
`Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
|
|
@@ -6021,13 +5134,13 @@ var initGetElementConfigTool = (reg) => {
|
|
|
6021
5134
|
);
|
|
6022
5135
|
}
|
|
6023
5136
|
const elementRawSettings = element.settings;
|
|
6024
|
-
const propSchema =
|
|
5137
|
+
const propSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
|
|
6025
5138
|
if (!elementRawSettings || !propSchema) {
|
|
6026
5139
|
throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
|
|
6027
5140
|
}
|
|
6028
5141
|
const propValues = {};
|
|
6029
5142
|
const stylePropValues = {};
|
|
6030
|
-
|
|
5143
|
+
Schema3.configurableKeys(propSchema).forEach((key) => {
|
|
6031
5144
|
propValues[key] = structuredClone(elementRawSettings.get(key));
|
|
6032
5145
|
});
|
|
6033
5146
|
const elementStyles = getElementStyles2(elementId) || {};
|
|
@@ -6063,7 +5176,7 @@ var initGetElementConfigTool = (reg) => {
|
|
|
6063
5176
|
|
|
6064
5177
|
// src/mcp/canvas-mcp.ts
|
|
6065
5178
|
var initCanvasMcp = (reg) => {
|
|
6066
|
-
|
|
5179
|
+
Schema4.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
|
|
6067
5180
|
initWidgetsSchemaResource(reg);
|
|
6068
5181
|
initAvailableWidgetsResource(reg);
|
|
6069
5182
|
initDocumentStructureResource(reg);
|
|
@@ -6071,9 +5184,10 @@ var initCanvasMcp = (reg) => {
|
|
|
6071
5184
|
initSelectedElementResource(reg);
|
|
6072
5185
|
initEditorStateResource(reg);
|
|
6073
5186
|
initGeneralContextResource(reg);
|
|
6074
|
-
|
|
5187
|
+
initBestPracticesResource(reg);
|
|
6075
5188
|
initGetElementConfigTool(reg);
|
|
6076
5189
|
initConfigureElementTool(reg);
|
|
5190
|
+
initBuildCompositionTool(reg);
|
|
6077
5191
|
initBreakpointsResource(reg);
|
|
6078
5192
|
};
|
|
6079
5193
|
|
|
@@ -6273,7 +5387,7 @@ function shouldBlock(sourceElements, targetElements) {
|
|
|
6273
5387
|
}
|
|
6274
5388
|
|
|
6275
5389
|
// src/style-commands/paste-style.ts
|
|
6276
|
-
import { getContainer as
|
|
5390
|
+
import { getContainer as getContainer7, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
|
|
6277
5391
|
import { classesPropTypeUtil } from "@elementor/editor-props";
|
|
6278
5392
|
import {
|
|
6279
5393
|
__privateListenTo as listenTo5,
|
|
@@ -6282,7 +5396,7 @@ import {
|
|
|
6282
5396
|
} from "@elementor/editor-v1-adapters";
|
|
6283
5397
|
|
|
6284
5398
|
// src/utils/command-utils.ts
|
|
6285
|
-
import { getElementLabel as getElementLabel2, getWidgetsCache as
|
|
5399
|
+
import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
|
|
6286
5400
|
import { CLASSES_PROP_KEY } from "@elementor/editor-props";
|
|
6287
5401
|
import { __ as __5 } from "@wordpress/i18n";
|
|
6288
5402
|
function hasAtomicWidgets(args) {
|
|
@@ -6307,7 +5421,7 @@ function getClassesProp(container) {
|
|
|
6307
5421
|
}
|
|
6308
5422
|
function getContainerSchema(container) {
|
|
6309
5423
|
const type = container?.model.get("widgetType") || container?.model.get("elType");
|
|
6310
|
-
const widgetsCache =
|
|
5424
|
+
const widgetsCache = getWidgetsCache8();
|
|
6311
5425
|
const elementType = widgetsCache?.[type];
|
|
6312
5426
|
return elementType?.atomic_props_schema ?? null;
|
|
6313
5427
|
}
|
|
@@ -6426,7 +5540,7 @@ function pasteStyles(args, pasteLocalStyle) {
|
|
|
6426
5540
|
}
|
|
6427
5541
|
const clipboardElements = getClipboardElements(storageKey);
|
|
6428
5542
|
const [clipboardElement] = clipboardElements ?? [];
|
|
6429
|
-
const clipboardContainer =
|
|
5543
|
+
const clipboardContainer = getContainer7(clipboardElement.id);
|
|
6430
5544
|
if (!clipboardElement || !clipboardContainer || !isAtomicWidget(clipboardContainer)) {
|
|
6431
5545
|
return;
|
|
6432
5546
|
}
|
|
@@ -6703,10 +5817,10 @@ function useEscapeOnCanvas(canvasDocument, onEscape) {
|
|
|
6703
5817
|
}
|
|
6704
5818
|
|
|
6705
5819
|
// src/utils/after-render.ts
|
|
6706
|
-
import { getContainer as
|
|
5820
|
+
import { getContainer as getContainer8 } from "@elementor/editor-elements";
|
|
6707
5821
|
function doAfterRender(elementIds, callback) {
|
|
6708
5822
|
const pending = elementIds.map((elementId) => {
|
|
6709
|
-
const view =
|
|
5823
|
+
const view = getContainer8(elementId)?.view;
|
|
6710
5824
|
if (!view || !hasDoAfterRender(view)) {
|
|
6711
5825
|
return void 0;
|
|
6712
5826
|
}
|
|
@@ -6721,6 +5835,9 @@ function doAfterRender(elementIds, callback) {
|
|
|
6721
5835
|
function hasDoAfterRender(view) {
|
|
6722
5836
|
return typeof view?._doAfterRender === "function";
|
|
6723
5837
|
}
|
|
5838
|
+
|
|
5839
|
+
// src/sync/element-added-event.ts
|
|
5840
|
+
var ELEMENT_ADDED_EVENT = "elementor/canvas/element-added";
|
|
6724
5841
|
export {
|
|
6725
5842
|
BREAKPOINTS_SCHEMA_FULL_URI,
|
|
6726
5843
|
BREAKPOINTS_SCHEMA_URI,
|