@elementor/editor-canvas 4.3.0-992 → 4.3.0-994
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 +181 -901
- package/dist/index.mjs +124 -855
- package/package.json +20 -20
- package/src/index.ts +1 -1
- package/src/mcp/canvas-mcp.ts +2 -2
- package/src/mcp/tools/build-composition/tool.ts +107 -218
- package/src/sync/element-added-event.ts +8 -0
- 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/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
|
@@ -1806,46 +1806,6 @@ function clipboardRootsAreAtomicForms(elements) {
|
|
|
1806
1806
|
}
|
|
1807
1807
|
return elements.every((el) => getClipboardElementType(el) === FORM_ELEMENT_TYPE);
|
|
1808
1808
|
}
|
|
1809
|
-
function hasFormAncestor(node) {
|
|
1810
|
-
return node.closest(FORM_ELEMENT_TYPE) !== null;
|
|
1811
|
-
}
|
|
1812
|
-
function collectFormAncestorErrors(xml) {
|
|
1813
|
-
const errors = [];
|
|
1814
|
-
for (const node of xml.querySelectorAll("*")) {
|
|
1815
|
-
if (!FORM_FIELD_ELEMENT_TYPES.has(node.tagName.toLowerCase())) {
|
|
1816
|
-
continue;
|
|
1817
|
-
}
|
|
1818
|
-
if (hasFormAncestor(node)) {
|
|
1819
|
-
continue;
|
|
1820
|
-
}
|
|
1821
|
-
const id = node.getAttribute("configuration-id");
|
|
1822
|
-
errors.push(
|
|
1823
|
-
`<${node.tagName}${id ? ` configuration-id="${id}"` : ""}> must be nested inside <e-form> (any ancestor depth is allowed).`
|
|
1824
|
-
);
|
|
1825
|
-
}
|
|
1826
|
-
return errors;
|
|
1827
|
-
}
|
|
1828
|
-
function collectSubmitButtonErrors(xml) {
|
|
1829
|
-
const errors = [];
|
|
1830
|
-
for (const form of xml.querySelectorAll("e-form")) {
|
|
1831
|
-
const submitButtons = form.querySelectorAll("e-form-submit-button");
|
|
1832
|
-
if (submitButtons.length === 0) {
|
|
1833
|
-
errors.push(`<e-form> has no <e-form-submit-button>.`);
|
|
1834
|
-
} else if (submitButtons.length > 1) {
|
|
1835
|
-
errors.push(`<e-form> has ${submitButtons.length} submit buttons \u2014 only 1 is allowed.`);
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
return errors;
|
|
1839
|
-
}
|
|
1840
|
-
function collectEmptyMessageErrors(xml) {
|
|
1841
|
-
const errors = [];
|
|
1842
|
-
for (const node of xml.querySelectorAll("e-form-success-message, e-form-error-message")) {
|
|
1843
|
-
if (node.children.length === 0) {
|
|
1844
|
-
errors.push(`<${node.tagName}> must have at least one child element (e.g. <e-atomic-paragraph>).`);
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
return errors;
|
|
1848
|
-
}
|
|
1849
1809
|
|
|
1850
1810
|
// src/form-structure/enforce-form-ancestor-commands.ts
|
|
1851
1811
|
var FORM_FIELDS_OUTSIDE_ALERT = {
|
|
@@ -4486,23 +4446,100 @@ function getElementDisplayName(container) {
|
|
|
4486
4446
|
}
|
|
4487
4447
|
|
|
4488
4448
|
// src/mcp/tools/build-composition/tool.ts
|
|
4489
|
-
import { getCurrentDocument } from "@elementor/editor-documents";
|
|
4490
|
-
import {
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
}
|
|
4496
|
-
|
|
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
|
+
}
|
|
4497
4538
|
|
|
4498
|
-
// src/
|
|
4499
|
-
import {
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
generateElementId as generateElementId2,
|
|
4503
|
-
getContainer as getContainer4,
|
|
4504
|
-
getWidgetsCache as getWidgetsCache6
|
|
4505
|
-
} 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";
|
|
4506
4543
|
|
|
4507
4544
|
// src/mcp/utils/do-update-element-property.ts
|
|
4508
4545
|
import {
|
|
@@ -4782,782 +4819,11 @@ Expected Schema: ${jsonSchema}`
|
|
|
4782
4819
|
runCommandSync2("document/save/set-is-modified", { status: true }, { internal: true });
|
|
4783
4820
|
};
|
|
4784
4821
|
|
|
4785
|
-
// src/composition-builder/utils/required-default-child-tags.ts
|
|
4786
|
-
function getRequiredDefaultChildTemplates(elementConfig) {
|
|
4787
|
-
const defaultChildren = elementConfig?.default_children;
|
|
4788
|
-
if (!Array.isArray(defaultChildren)) {
|
|
4789
|
-
return [];
|
|
4790
|
-
}
|
|
4791
|
-
return defaultChildren.filter((child) => child?.meta?.required ?? false);
|
|
4792
|
-
}
|
|
4793
|
-
|
|
4794
|
-
// src/composition-builder/utils/required-children-enforcer.ts
|
|
4795
|
-
var REQUIRED_CHILD_SCHEMA_HINT = "Use the widget schema resource; under llm_guidance.required_direct_children for V4 widgets.";
|
|
4796
|
-
var RequiredChildrenEnforcer = class {
|
|
4797
|
-
elementType;
|
|
4798
|
-
requiredTemplates;
|
|
4799
|
-
constructor(elementType, widgetsCache) {
|
|
4800
|
-
this.elementType = elementType;
|
|
4801
|
-
this.requiredTemplates = getRequiredDefaultChildTemplates(widgetsCache[elementType]);
|
|
4802
|
-
}
|
|
4803
|
-
enforce(xml) {
|
|
4804
|
-
if (this.requiredTemplates.length === 0) {
|
|
4805
|
-
return;
|
|
4806
|
-
}
|
|
4807
|
-
const errors = [];
|
|
4808
|
-
for (const rootNode of Array.from(xml.children)) {
|
|
4809
|
-
this.collectMissingRequiredErrors(rootNode, errors);
|
|
4810
|
-
}
|
|
4811
|
-
if (errors.length) {
|
|
4812
|
-
throw new Error(`${errors.join("\n")}
|
|
4813
|
-
${REQUIRED_CHILD_SCHEMA_HINT}`);
|
|
4814
|
-
}
|
|
4815
|
-
}
|
|
4816
|
-
collectMissingRequiredErrors(node, errors) {
|
|
4817
|
-
if (node.tagName === this.elementType) {
|
|
4818
|
-
const existingChildTags = new Set(Array.from(node.children).map((child) => child.tagName));
|
|
4819
|
-
const missingTags = this.requiredTemplates.map((child) => child.widgetType ?? child.elType ?? "").filter((type) => type && !existingChildTags.has(type));
|
|
4820
|
-
if (missingTags.length) {
|
|
4821
|
-
const configurationId = node.getAttribute("configuration-id");
|
|
4822
|
-
const location2 = configurationId ? `<${node.tagName} configuration-id="${configurationId}">` : `<${node.tagName}>`;
|
|
4823
|
-
errors.push(
|
|
4824
|
-
`${location2} Missing required direct child element tag(s): ${missingTags.join(", ")}.`
|
|
4825
|
-
);
|
|
4826
|
-
}
|
|
4827
|
-
}
|
|
4828
|
-
for (const childNode of Array.from(node.children)) {
|
|
4829
|
-
this.collectMissingRequiredErrors(childNode, errors);
|
|
4830
|
-
}
|
|
4831
|
-
}
|
|
4832
|
-
};
|
|
4833
|
-
|
|
4834
|
-
// src/composition-builder/composition-builder.ts
|
|
4835
|
-
var CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE = "createElement did not return an element container with a model.";
|
|
4836
|
-
var CompositionBuilder = class _CompositionBuilder {
|
|
4837
|
-
elementConfig = {};
|
|
4838
|
-
elementStylesConfig = {};
|
|
4839
|
-
elementCustomCSS = {};
|
|
4840
|
-
rootContainers = [];
|
|
4841
|
-
api = {
|
|
4842
|
-
createElement: createElement12,
|
|
4843
|
-
deleteElement,
|
|
4844
|
-
getWidgetsCache: getWidgetsCache6,
|
|
4845
|
-
generateElementId: generateElementId2,
|
|
4846
|
-
getContainer: getContainer4,
|
|
4847
|
-
doUpdateElementProperty
|
|
4848
|
-
};
|
|
4849
|
-
xml;
|
|
4850
|
-
static fromXMLString(xmlString, api = {}) {
|
|
4851
|
-
const parser = new DOMParser();
|
|
4852
|
-
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
|
4853
|
-
const errorNode = xmlDoc.querySelector("parsererror");
|
|
4854
|
-
if (errorNode) {
|
|
4855
|
-
throw new Error("Failed to parse XML string: " + errorNode.textContent);
|
|
4856
|
-
}
|
|
4857
|
-
return new _CompositionBuilder({
|
|
4858
|
-
xml: xmlDoc,
|
|
4859
|
-
api
|
|
4860
|
-
});
|
|
4861
|
-
}
|
|
4862
|
-
constructor(opts) {
|
|
4863
|
-
const { api = {}, elementConfig = {}, stylesConfig = {}, customCSS = {}, xml } = opts;
|
|
4864
|
-
this.xml = xml;
|
|
4865
|
-
Object.assign(this.api, api);
|
|
4866
|
-
this.setElementConfig(elementConfig);
|
|
4867
|
-
this.setStylesConfig(stylesConfig);
|
|
4868
|
-
this.setCustomCSS(customCSS);
|
|
4869
|
-
}
|
|
4870
|
-
setElementConfig(config) {
|
|
4871
|
-
this.elementConfig = config;
|
|
4872
|
-
}
|
|
4873
|
-
setStylesConfig(config) {
|
|
4874
|
-
this.elementStylesConfig = config;
|
|
4875
|
-
}
|
|
4876
|
-
setCustomCSS(config) {
|
|
4877
|
-
this.elementCustomCSS = config;
|
|
4878
|
-
}
|
|
4879
|
-
getXML() {
|
|
4880
|
-
return this.xml;
|
|
4881
|
-
}
|
|
4882
|
-
buildModelTree(node, widgetsCache) {
|
|
4883
|
-
const elementTag = node.tagName;
|
|
4884
|
-
const isWidget = widgetsCache[elementTag]?.elType === "widget";
|
|
4885
|
-
const id = this.api.generateElementId();
|
|
4886
|
-
const children = Array.from(node.children).map((child) => this.buildModelTree(child, widgetsCache));
|
|
4887
|
-
node.setAttribute("id", id);
|
|
4888
|
-
const base = {
|
|
4889
|
-
id,
|
|
4890
|
-
skipDefaultChildren: true,
|
|
4891
|
-
elements: children,
|
|
4892
|
-
editor_settings: {
|
|
4893
|
-
title: node.getAttribute("configuration-id") ?? void 0
|
|
4894
|
-
},
|
|
4895
|
-
elType: "widget"
|
|
4896
|
-
};
|
|
4897
|
-
if (isWidget) {
|
|
4898
|
-
return { ...base, elType: "widget", widgetType: elementTag };
|
|
4899
|
-
}
|
|
4900
|
-
return { ...base, elType: elementTag };
|
|
4901
|
-
}
|
|
4902
|
-
async awaitViewRender(element) {
|
|
4903
|
-
const view = element.view;
|
|
4904
|
-
if (view?._currentRenderPromise instanceof Promise) {
|
|
4905
|
-
await view._currentRenderPromise;
|
|
4906
|
-
} else {
|
|
4907
|
-
await Promise.resolve();
|
|
4908
|
-
}
|
|
4909
|
-
}
|
|
4910
|
-
validateChildTypes(node, widgetsCache) {
|
|
4911
|
-
const errors = [];
|
|
4912
|
-
const allowedChildTypes = widgetsCache[node.tagName]?.allowed_child_types;
|
|
4913
|
-
if (allowedChildTypes?.length) {
|
|
4914
|
-
for (const child of Array.from(node.children)) {
|
|
4915
|
-
if (!allowedChildTypes.includes(child.tagName)) {
|
|
4916
|
-
errors.push(
|
|
4917
|
-
`"${child.tagName}" is not allowed as a child of "${node.tagName}". Allowed: ${allowedChildTypes.join(", ")}`
|
|
4918
|
-
);
|
|
4919
|
-
}
|
|
4920
|
-
}
|
|
4921
|
-
}
|
|
4922
|
-
for (const child of Array.from(node.children)) {
|
|
4923
|
-
errors.push(...this.validateChildTypes(child, widgetsCache));
|
|
4924
|
-
}
|
|
4925
|
-
return errors;
|
|
4926
|
-
}
|
|
4927
|
-
matchNodeByConfigId(configId) {
|
|
4928
|
-
const node = this.xml.querySelector(`[configuration-id="${configId}"]`);
|
|
4929
|
-
if (!node) {
|
|
4930
|
-
throw new Error(`Configuration id "${configId}" does not have target node.`);
|
|
4931
|
-
}
|
|
4932
|
-
const id = node.getAttribute("id");
|
|
4933
|
-
if (!id) {
|
|
4934
|
-
throw new Error(`Node with configuration id "${configId}" does not have element id.`);
|
|
4935
|
-
}
|
|
4936
|
-
const element = this.api.getContainer(id);
|
|
4937
|
-
if (!element) {
|
|
4938
|
-
throw new Error(`Element with id "${id}" not found but should exist.`);
|
|
4939
|
-
}
|
|
4940
|
-
return {
|
|
4941
|
-
element,
|
|
4942
|
-
node
|
|
4943
|
-
};
|
|
4944
|
-
}
|
|
4945
|
-
async applyProperties() {
|
|
4946
|
-
const configErrors = [];
|
|
4947
|
-
const styleErrors = [];
|
|
4948
|
-
const allConfigIds = /* @__PURE__ */ new Set([
|
|
4949
|
-
...Object.keys(this.elementConfig),
|
|
4950
|
-
...Object.keys(this.elementStylesConfig),
|
|
4951
|
-
...Object.keys(this.elementCustomCSS)
|
|
4952
|
-
]);
|
|
4953
|
-
for (const configId of allConfigIds) {
|
|
4954
|
-
let element, node;
|
|
4955
|
-
try {
|
|
4956
|
-
({ element, node } = this.matchNodeByConfigId(configId));
|
|
4957
|
-
} catch (matchErr) {
|
|
4958
|
-
const msg = matchErr.message;
|
|
4959
|
-
if (this.elementConfig[configId]) {
|
|
4960
|
-
configErrors.push(msg);
|
|
4961
|
-
}
|
|
4962
|
-
if (this.elementStylesConfig[configId] || this.elementCustomCSS[configId]) {
|
|
4963
|
-
styleErrors.push(msg);
|
|
4964
|
-
}
|
|
4965
|
-
continue;
|
|
4966
|
-
}
|
|
4967
|
-
const config = this.elementConfig[configId];
|
|
4968
|
-
if (config) {
|
|
4969
|
-
for (const [propertyName, propertyValue] of Object.entries(config)) {
|
|
4970
|
-
try {
|
|
4971
|
-
this.api.doUpdateElementProperty({
|
|
4972
|
-
elementId: element.id,
|
|
4973
|
-
propertyName,
|
|
4974
|
-
propertyValue,
|
|
4975
|
-
elementType: node.tagName
|
|
4976
|
-
});
|
|
4977
|
-
} catch (error) {
|
|
4978
|
-
configErrors.push(error.message);
|
|
4979
|
-
}
|
|
4980
|
-
}
|
|
4981
|
-
}
|
|
4982
|
-
const styleConfig = this.elementStylesConfig[configId];
|
|
4983
|
-
const hasInvalidStyles = false;
|
|
4984
|
-
if (styleConfig) {
|
|
4985
|
-
const validStylesPropValues = {};
|
|
4986
|
-
for (const [styleName, stylePropValue] of Object.entries(styleConfig)) {
|
|
4987
|
-
if (styleName === "$intention") {
|
|
4988
|
-
continue;
|
|
4989
|
-
} else {
|
|
4990
|
-
validStylesPropValues[styleName] = stylePropValue;
|
|
4991
|
-
}
|
|
4992
|
-
}
|
|
4993
|
-
if (Object.keys(validStylesPropValues).length > 0) {
|
|
4994
|
-
try {
|
|
4995
|
-
this.api.doUpdateElementProperty({
|
|
4996
|
-
elementId: element.id,
|
|
4997
|
-
propertyName: "_styles",
|
|
4998
|
-
propertyValue: validStylesPropValues,
|
|
4999
|
-
elementType: node.tagName
|
|
5000
|
-
});
|
|
5001
|
-
} catch (error) {
|
|
5002
|
-
styleErrors.push(String(error));
|
|
5003
|
-
}
|
|
5004
|
-
}
|
|
5005
|
-
}
|
|
5006
|
-
const intentionCss = typeof styleConfig?.$intention === "string" ? styleConfig.$intention.trim() : "";
|
|
5007
|
-
const fallbackCss = hasInvalidStyles && intentionCss ? intentionCss : "";
|
|
5008
|
-
const mergedCustomCss = mergeCustomCssText(this.elementCustomCSS[configId], fallbackCss);
|
|
5009
|
-
if (mergedCustomCss) {
|
|
5010
|
-
try {
|
|
5011
|
-
this.api.doUpdateElementProperty({
|
|
5012
|
-
elementId: element.id,
|
|
5013
|
-
propertyName: "_styles",
|
|
5014
|
-
propertyValue: { custom_css: mergedCustomCss },
|
|
5015
|
-
elementType: node.tagName
|
|
5016
|
-
});
|
|
5017
|
-
} catch (cssErr) {
|
|
5018
|
-
styleErrors.push(String(cssErr));
|
|
5019
|
-
}
|
|
5020
|
-
}
|
|
5021
|
-
await this.awaitViewRender(element);
|
|
5022
|
-
}
|
|
5023
|
-
return { configErrors, styleErrors };
|
|
5024
|
-
}
|
|
5025
|
-
async build(rootContainer) {
|
|
5026
|
-
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
5027
|
-
new Set(this.xml.querySelectorAll("*")).forEach((node) => {
|
|
5028
|
-
if (!widgetsCache[node.tagName]) {
|
|
5029
|
-
throw new Error(`Unknown widget type: ${node.tagName}`);
|
|
5030
|
-
}
|
|
5031
|
-
});
|
|
5032
|
-
const typesWithRequiredChildren = Object.keys(widgetsCache).filter(
|
|
5033
|
-
(elementType) => getRequiredDefaultChildTemplates(widgetsCache[elementType]).length > 0
|
|
5034
|
-
);
|
|
5035
|
-
typesWithRequiredChildren.forEach((elementType) => {
|
|
5036
|
-
new RequiredChildrenEnforcer(elementType, widgetsCache).enforce(this.xml);
|
|
5037
|
-
});
|
|
5038
|
-
const childTypeErrors = [];
|
|
5039
|
-
for (const rootChild of Array.from(this.xml.children)) {
|
|
5040
|
-
childTypeErrors.push(...this.validateChildTypes(rootChild, widgetsCache));
|
|
5041
|
-
}
|
|
5042
|
-
if (childTypeErrors.length) {
|
|
5043
|
-
throw new Error(`Invalid element structure:
|
|
5044
|
-
${childTypeErrors.join("\n")}`);
|
|
5045
|
-
}
|
|
5046
|
-
const formErrors = [
|
|
5047
|
-
...collectFormAncestorErrors(this.xml),
|
|
5048
|
-
...collectSubmitButtonErrors(this.xml),
|
|
5049
|
-
...collectEmptyMessageErrors(this.xml)
|
|
5050
|
-
];
|
|
5051
|
-
const children = Array.from(this.xml.children);
|
|
5052
|
-
for (const childNode of children) {
|
|
5053
|
-
const modelTree = this.buildModelTree(childNode, widgetsCache);
|
|
5054
|
-
try {
|
|
5055
|
-
const newElement = this.api.createElement({
|
|
5056
|
-
container: rootContainer,
|
|
5057
|
-
model: modelTree,
|
|
5058
|
-
options: { useHistory: false }
|
|
5059
|
-
});
|
|
5060
|
-
if (!newElement?.model) {
|
|
5061
|
-
throw new Error(CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE);
|
|
5062
|
-
}
|
|
5063
|
-
this.rootContainers.push(newElement);
|
|
5064
|
-
await this.awaitViewRender(newElement);
|
|
5065
|
-
} catch (e) {
|
|
5066
|
-
const attempToRestoreInvalidContainer = this.api.getContainer(modelTree.id);
|
|
5067
|
-
if (attempToRestoreInvalidContainer) {
|
|
5068
|
-
this.api.deleteElement({ container: attempToRestoreInvalidContainer });
|
|
5069
|
-
}
|
|
5070
|
-
throw e;
|
|
5071
|
-
}
|
|
5072
|
-
}
|
|
5073
|
-
const { configErrors, styleErrors } = await this.applyProperties();
|
|
5074
|
-
if (typeof window !== "undefined") {
|
|
5075
|
-
const targetWindow = window.top || window;
|
|
5076
|
-
targetWindow.dispatchEvent(
|
|
5077
|
-
new CustomEvent("elementor/composition/built", {
|
|
5078
|
-
detail: { rootContainers: this.rootContainers.map((c) => c.id) }
|
|
5079
|
-
})
|
|
5080
|
-
);
|
|
5081
|
-
}
|
|
5082
|
-
return {
|
|
5083
|
-
configErrors,
|
|
5084
|
-
styleErrors,
|
|
5085
|
-
formErrors,
|
|
5086
|
-
rootContainers: [...this.rootContainers]
|
|
5087
|
-
};
|
|
5088
|
-
}
|
|
5089
|
-
};
|
|
5090
|
-
|
|
5091
|
-
// src/utils/tracking.ts
|
|
5092
|
-
import { trackEvent } from "@elementor/events";
|
|
5093
|
-
var trackCanvasEvent = (data) => {
|
|
5094
|
-
trackEvent(data);
|
|
5095
|
-
};
|
|
5096
|
-
|
|
5097
|
-
// src/mcp/utils/element-data-util.ts
|
|
5098
|
-
import { getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
|
|
5099
|
-
function hasV3Controls(controls) {
|
|
5100
|
-
return typeof controls === "object" && controls !== null && Object.keys(controls).length > 0;
|
|
5101
|
-
}
|
|
5102
|
-
function isWidgetAvailableForLLM(config) {
|
|
5103
|
-
if (!config) {
|
|
5104
|
-
return false;
|
|
5105
|
-
}
|
|
5106
|
-
if (config.meta?.llm_support === false) {
|
|
5107
|
-
return false;
|
|
5108
|
-
}
|
|
5109
|
-
if (config.title === "Component") {
|
|
5110
|
-
return false;
|
|
5111
|
-
}
|
|
5112
|
-
if (config.atomic_props_schema) {
|
|
5113
|
-
return true;
|
|
5114
|
-
}
|
|
5115
|
-
return hasV3Controls(config.controls);
|
|
5116
|
-
}
|
|
5117
|
-
|
|
5118
|
-
// src/mcp/utils/get-composition-target-container.ts
|
|
5119
|
-
import { COMPONENT_DOCUMENT_TYPE } from "@elementor/editor-documents";
|
|
5120
|
-
function getCompositionTargetContainer(documentContainer, documentType) {
|
|
5121
|
-
const firstChild = documentContainer.children?.[0];
|
|
5122
|
-
if (documentType === COMPONENT_DOCUMENT_TYPE && firstChild) {
|
|
5123
|
-
return firstChild;
|
|
5124
|
-
}
|
|
5125
|
-
return documentContainer;
|
|
5126
|
-
}
|
|
5127
|
-
|
|
5128
|
-
// src/mcp/tools/build-composition/prompt.ts
|
|
5129
|
-
import { toolPrompts } from "@elementor/editor-mcp";
|
|
5130
|
-
var BUILD_COMPOSITIONS_GUIDE_URI = "elementor://canvas/tools/build-compositions-guide";
|
|
5131
|
-
var generatePrompt = () => {
|
|
5132
|
-
const buildCompositionsToolPrompt = toolPrompts("build-compositions");
|
|
5133
|
-
buildCompositionsToolPrompt.description(`
|
|
5134
|
-
# RESOURCES (Read before use)
|
|
5135
|
-
- [elementor://global-classes] - Check FIRST for reusable classes
|
|
5136
|
-
- [elementor://global-variables] - ONLY use variables defined here
|
|
5137
|
-
- [${AVAILABLE_WIDGETS_URI}/v4]
|
|
5138
|
-
|
|
5139
|
-
# TOOL SUPPORT
|
|
5140
|
-
This tool support v4 elements only
|
|
5141
|
-
|
|
5142
|
-
# WORKFLOW
|
|
5143
|
-
1. Check/create global classes via "manage-global-classes" tool
|
|
5144
|
-
2. Build composition (THIS TOOL) - minimal inline styles
|
|
5145
|
-
3. Apply classes via "apply-global-class" tool
|
|
5146
|
-
|
|
5147
|
-
# XML STRUCTURE
|
|
5148
|
-
- Use widget tags: \`<e-button configuration-id="btn1"></e-button>\`
|
|
5149
|
-
- Containers: "e-flexbox", "e-div-block", "e-tabs"
|
|
5150
|
-
- Every element needs unique "configuration-id"
|
|
5151
|
-
- No attributes, classes, IDs, or text nodes in XML
|
|
5152
|
-
|
|
5153
|
-
## NESTED ELEMENTS
|
|
5154
|
-
Some elements have internal tree structures (nesting). When using these elements, you MUST build the FULL tree in XML.
|
|
5155
|
-
- Check \`llm_guidance.nesting\` in widget schemas for structure requirements
|
|
5156
|
-
- \`llm_guidance.required_direct_children\` lists element types that must appear as direct child tags in XML (from widget defaults)
|
|
5157
|
-
- \`allowed_child_types\` lists which element types can be nested inside
|
|
5158
|
-
- \`allowed_parents\` lists which element types this element can be placed inside
|
|
5159
|
-
|
|
5160
|
-
# CONFIGURATION
|
|
5161
|
-
- Map configuration-id \u2192 elementConfig (props) + style (raw CSS declarations)
|
|
5162
|
-
- elementConfig PropValues require \`$$type\` matching schema
|
|
5163
|
-
- 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
|
|
5164
|
-
- NO LINKS in configuration
|
|
5165
|
-
- Retry on errors up to 10x
|
|
5166
|
-
- Check \`llm_guidance.default_settings\` in widget schemas \u2014 omit only keys listed there from elementConfig unless the user explicitly asks to change them
|
|
5167
|
-
|
|
5168
|
-
# DYNAMIC TAGS
|
|
5169
|
-
- 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\`).
|
|
5170
|
-
- 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.
|
|
5171
|
-
- Provide at that node: \`{ "$$type": "dynamic", "value": { "name": "<allowed tag>", "settings": { ... } } }\`
|
|
5172
|
-
- Example (image): \`{ "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }\`
|
|
5173
|
-
- Do NOT send \`group\` (it is resolved automatically). Populate \`settings\` strictly per the tag's schema; use \`{}\` only when it has none.
|
|
5174
|
-
|
|
5175
|
-
Note about configuration ids: These names are visible to the end-user, make sure they make sense, related and relevant.
|
|
5176
|
-
|
|
5177
|
-
# DESIGN PHILOSOPHY: CONTEXT-DRIVEN CREATIVITY
|
|
5178
|
-
|
|
5179
|
-
**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.
|
|
5180
|
-
|
|
5181
|
-
## SIZING: DEFAULT IS NO SIZE (CRITICAL)
|
|
5182
|
-
|
|
5183
|
-
**DO NOT specify height or width unless you have a specific visual reason.**
|
|
5184
|
-
|
|
5185
|
-
Flexbox and CSS already handle sizing automatically:
|
|
5186
|
-
- Containers grow to fit their content
|
|
5187
|
-
- Flex children distribute space via flex properties, not width/height
|
|
5188
|
-
- Text elements size to their content
|
|
5189
|
-
|
|
5190
|
-
WHEN TO SPECIFY SIZE:
|
|
5191
|
-
- min-height on ROOT section for viewport-spanning hero (use min-height, NOT height)
|
|
5192
|
-
- max-width for contained content areas (e.g., max-width: 60rem)
|
|
5193
|
-
- Explicit aspect ratios for media containers
|
|
5194
|
-
|
|
5195
|
-
NEVER SPECIFY:
|
|
5196
|
-
- height on nested containers (causes overflow)
|
|
5197
|
-
- width on flex children (use flex-basis or gap instead)
|
|
5198
|
-
- 100vh on anything except root-level sections
|
|
5199
|
-
- Any size "just to be safe" - if unsure, OMIT IT
|
|
5200
|
-
|
|
5201
|
-
vh units are VIEWPORT-relative. Nested 100vh inside 100vh = 200vh overflow.
|
|
5202
|
-
|
|
5203
|
-
GOOD: \`<e-flexbox>content naturally sizes</e-flexbox>\`
|
|
5204
|
-
BAD: \`<e-flexbox style="height:100vh"><e-div-block style="height:100vh">overflow</e-div-block></e-flexbox>\`
|
|
5205
|
-
|
|
5206
|
-
## Layout Variety (Break the Template)
|
|
5207
|
-
- AVOID: Full-width 100vh hero \u2192 three columns \u2192 testimonials \u2192 CTA (every AI does this)
|
|
5208
|
-
- VARY heights: Use auto-height sections with generous padding (6rem+). Let content breathe
|
|
5209
|
-
- VARY widths: Not everything spans full width. Use contained sections (max-width: 960px) mixed with edge-to-edge
|
|
5210
|
-
- ASYMMETRIC grids: 2:1, 1:3, offset layouts. Avoid equal column widths
|
|
5211
|
-
- Negative space as design element: Large margins create focus and sophistication
|
|
5212
|
-
- Break alignment intentionally: Offset headings, overlapping elements, broken grids
|
|
5213
|
-
|
|
5214
|
-
## Visual Depth & Effects
|
|
5215
|
-
- Layer elements: Overlapping cards, text over images, floating elements
|
|
5216
|
-
- Subtle shadows with color tint (not pure black): \`box-shadow: 0 20px 60px rgba(<brand-color-here>, 0.15)\`
|
|
5217
|
-
- Gradient overlays on images for text readability
|
|
5218
|
-
- Border radius variation: Mix sharp (0) and soft (1rem+) corners purposefully
|
|
5219
|
-
- Backdrop blur for glassmorphism where appropriate
|
|
5220
|
-
- Micro-interactions via CSS: hover transforms, transitions (0.3s ease)
|
|
5221
|
-
|
|
5222
|
-
## Typography with Character
|
|
5223
|
-
- Display fonts for headlines (from user's brand or contextually appropriate)
|
|
5224
|
-
- Size contrast: 4rem+ headlines vs 1rem body. Make hierarchy unmistakable
|
|
5225
|
-
- Letter-spacing: Tight for large headlines (-0.02em), loose for small caps (0.1em)
|
|
5226
|
-
- Line-height: Tight for headlines (1.1), generous for body (1.6-1.8)
|
|
5227
|
-
- Text decoration: Underlines, highlights, gradient text for emphasis
|
|
5228
|
-
|
|
5229
|
-
## Color with Purpose
|
|
5230
|
-
- Extract palette from user context (brand colors, industry norms, mood)
|
|
5231
|
-
- 60-30-10 rule: dominant, secondary, accent
|
|
5232
|
-
- Tinted neutrals over pure grays: warm (#faf8f5, #2d2a26) or cool (#f5f7fa, #1e2430)
|
|
5233
|
-
- Color blocking: Large colored sections create visual rhythm
|
|
5234
|
-
- Gradient directions: Diagonal (135deg, 225deg) feel more dynamic than vertical
|
|
5235
|
-
|
|
5236
|
-
## Spacing Strategy
|
|
5237
|
-
- Section padding: 6rem-10rem vertical, creating breathing room
|
|
5238
|
-
- Rhythm variation: Tight groups (2rem) with generous gaps between (6rem)
|
|
5239
|
-
- Use rem/em exclusively for responsive scaling
|
|
5240
|
-
- Generous padding on CTAs: min 1rem 2.5rem
|
|
5241
|
-
|
|
5242
|
-
# HARD CONSTRAINTS
|
|
5243
|
-
- Variables ONLY from [elementor://global-variables] (others throw errors)
|
|
5244
|
-
- Avoid SVG widgets unless assets are pre-uploaded
|
|
5245
|
-
- Check \`llm_guidance\` in widget schemas (\`default_styles\`, nesting, required children)
|
|
5246
|
-
|
|
5247
|
-
# PARAMETERS
|
|
5248
|
-
- **xmlStructure**: Valid XML with configuration-id attributes
|
|
5249
|
-
- **elementConfig**: configuration-id \u2192 widget PropValues
|
|
5250
|
-
- **style**: configuration-id \u2192 raw CSS declarations (property \u2192 value strings; no selectors)
|
|
5251
|
-
`);
|
|
5252
|
-
buildCompositionsToolPrompt.example(`
|
|
5253
|
-
Section with heading + button (NO explicit heights - content sizes naturally):
|
|
5254
|
-
{
|
|
5255
|
-
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>",
|
|
5256
|
-
elementConfig: {
|
|
5257
|
-
"section1": { "tag": { "$$type": "string", "value": "section" } }
|
|
5258
|
-
},
|
|
5259
|
-
style: {
|
|
5260
|
-
"Section Title": {
|
|
5261
|
-
"padding": "6rem 4rem",
|
|
5262
|
-
"background": "linear-gradient(135deg, #faf8f5 0%, #f0ebe4 100%)",
|
|
5263
|
-
"font-size": "3.5rem",
|
|
5264
|
-
"color": "#2d2a26"
|
|
5265
|
-
}
|
|
5266
|
-
}
|
|
5267
|
-
}
|
|
5268
|
-
Note: No height/width specified on any element - flexbox handles layout automatically.
|
|
5269
|
-
`);
|
|
5270
|
-
buildCompositionsToolPrompt.parameter(
|
|
5271
|
-
"xmlStructure",
|
|
5272
|
-
`Valid XML structure with custom elementor tags and configuration-id attributes.`
|
|
5273
|
-
);
|
|
5274
|
-
buildCompositionsToolPrompt.parameter("elementConfig", `Record mapping configuration IDs to widget PropValues.`);
|
|
5275
|
-
buildCompositionsToolPrompt.parameter(
|
|
5276
|
-
"style",
|
|
5277
|
-
`Record mapping configuration IDs to raw CSS declarations (property \u2192 value strings).`
|
|
5278
|
-
);
|
|
5279
|
-
buildCompositionsToolPrompt.instruction(
|
|
5280
|
-
`Element IDs in the returned XML represent actual widgets. Use these IDs for subsequent styling or configuration changes.`
|
|
5281
|
-
);
|
|
5282
|
-
return buildCompositionsToolPrompt.prompt();
|
|
5283
|
-
};
|
|
5284
|
-
|
|
5285
|
-
// src/mcp/tools/build-composition/schema.ts
|
|
5286
|
-
import { z } from "@elementor/schema";
|
|
5287
|
-
var inputSchema = {
|
|
5288
|
-
xmlStructure: z.string().describe("The XML structure representing the composition to be built"),
|
|
5289
|
-
elementConfig: z.record(
|
|
5290
|
-
z.string().describe("The configuration id"),
|
|
5291
|
-
z.record(
|
|
5292
|
-
z.string().describe("property name"),
|
|
5293
|
-
z.any().describe(`The PropValue for the property, refer to ${WIDGET_SCHEMA_URI}`)
|
|
5294
|
-
)
|
|
5295
|
-
).describe("A record mapping element IDs to their configuration objects. REQUIRED"),
|
|
5296
|
-
style: z.record(
|
|
5297
|
-
z.string().describe("The configuration id"),
|
|
5298
|
-
z.record(
|
|
5299
|
-
z.string().describe('A CSS property name, e.g. "color", "padding".'),
|
|
5300
|
-
z.string().describe('A CSS value, e.g. "6rem 4rem", "#2d2a26".')
|
|
5301
|
-
)
|
|
5302
|
-
).describe(
|
|
5303
|
-
"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."
|
|
5304
|
-
).default({})
|
|
5305
|
-
};
|
|
5306
|
-
var outputSchema = {
|
|
5307
|
-
errors: z.string().describe("Error message if the composition building failed").optional(),
|
|
5308
|
-
xmlStructure: z.string().describe(
|
|
5309
|
-
"The built XML structure as a string. Must use this XML after completion of building the composition, it contains real IDs."
|
|
5310
|
-
).optional(),
|
|
5311
|
-
llm_instructions: z.string().describe("Instructions what to do next, Important to follow these instructions!").optional()
|
|
5312
|
-
};
|
|
5313
|
-
|
|
5314
|
-
// src/mcp/tools/build-composition/xml-leaf-wrapper.ts
|
|
5315
|
-
var DIV_BLOCK_TAG = "e-div-block";
|
|
5316
|
-
var ZERO_SPACING = {
|
|
5317
|
-
$$type: "size",
|
|
5318
|
-
value: {
|
|
5319
|
-
size: {
|
|
5320
|
-
$$type: "number",
|
|
5321
|
-
value: 0
|
|
5322
|
-
},
|
|
5323
|
-
unit: {
|
|
5324
|
-
$$type: "string",
|
|
5325
|
-
value: "px"
|
|
5326
|
-
}
|
|
5327
|
-
}
|
|
5328
|
-
};
|
|
5329
|
-
function adaptLeafRootParams(params) {
|
|
5330
|
-
const doc = new DOMParser().parseFromString(params.xmlStructure, "application/xml");
|
|
5331
|
-
const rootElement = doc.documentElement;
|
|
5332
|
-
if (!isLeafWidget(rootElement.tagName, params.widgetsCache)) {
|
|
5333
|
-
return params;
|
|
5334
|
-
}
|
|
5335
|
-
const wrapperConfigId = getDivBlockWrapperConfigId(params.widgetsCache);
|
|
5336
|
-
return {
|
|
5337
|
-
...params,
|
|
5338
|
-
xmlStructure: serializeWrapped(doc, rootElement, wrapperConfigId),
|
|
5339
|
-
stylesConfig: {
|
|
5340
|
-
...params.stylesConfig,
|
|
5341
|
-
[wrapperConfigId]: {
|
|
5342
|
-
margin: ZERO_SPACING,
|
|
5343
|
-
padding: ZERO_SPACING,
|
|
5344
|
-
...params.stylesConfig[wrapperConfigId]
|
|
5345
|
-
}
|
|
5346
|
-
}
|
|
5347
|
-
};
|
|
5348
|
-
}
|
|
5349
|
-
function getDivBlockWrapperConfigId(widgetsCache) {
|
|
5350
|
-
return widgetsCache[DIV_BLOCK_TAG]?.title ?? DIV_BLOCK_TAG;
|
|
5351
|
-
}
|
|
5352
|
-
function isLeafWidget(tagName, widgetsCache) {
|
|
5353
|
-
return widgetsCache[tagName]?.elType === "widget";
|
|
5354
|
-
}
|
|
5355
|
-
function serializeWrapped(doc, rootElement, wrapperConfigId) {
|
|
5356
|
-
const wrapper = doc.createElement(DIV_BLOCK_TAG);
|
|
5357
|
-
wrapper.setAttribute("configuration-id", wrapperConfigId);
|
|
5358
|
-
wrapper.appendChild(rootElement.cloneNode(true));
|
|
5359
|
-
const wrappedDoc = new DOMParser().parseFromString(`<${DIV_BLOCK_TAG} />`, "application/xml");
|
|
5360
|
-
wrappedDoc.replaceChild(wrapper, wrappedDoc.documentElement);
|
|
5361
|
-
return new XMLSerializer().serializeToString(wrappedDoc);
|
|
5362
|
-
}
|
|
5363
|
-
|
|
5364
|
-
// src/mcp/tools/build-composition/tool.ts
|
|
5365
|
-
var ELEMENT_ADDED_EVENT = "elementor/canvas/element-added";
|
|
5366
|
-
var initBuildCompositionsTool = (reg) => {
|
|
5367
|
-
const { addTool, resource } = reg;
|
|
5368
|
-
resource(
|
|
5369
|
-
"build-compositions-guide",
|
|
5370
|
-
BUILD_COMPOSITIONS_GUIDE_URI,
|
|
5371
|
-
{
|
|
5372
|
-
title: "Build Compositions Guide",
|
|
5373
|
-
description: "Detailed guide for using the build-compositions tool",
|
|
5374
|
-
mimeType: "text/plain"
|
|
5375
|
-
},
|
|
5376
|
-
async (uri) => ({
|
|
5377
|
-
contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt() }]
|
|
5378
|
-
})
|
|
5379
|
-
);
|
|
5380
|
-
addTool({
|
|
5381
|
-
name: "build-compositions",
|
|
5382
|
-
description: "Build V4 element compositions on the Elementor canvas. Read the guide resource before use.",
|
|
5383
|
-
schema: inputSchema,
|
|
5384
|
-
requiredResources: [
|
|
5385
|
-
{ description: "Build compositions guide", uri: BUILD_COMPOSITIONS_GUIDE_URI },
|
|
5386
|
-
{ description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
|
|
5387
|
-
{ description: "Global Classes", uri: "elementor://global-classes" },
|
|
5388
|
-
{ description: "Global Variables", uri: "elementor://global-variables" },
|
|
5389
|
-
{ description: "Styles best practices", uri: BEST_PRACTICES_URI },
|
|
5390
|
-
{ description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 },
|
|
5391
|
-
{ description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
|
|
5392
|
-
],
|
|
5393
|
-
outputSchema,
|
|
5394
|
-
handler: async (rawParams) => {
|
|
5395
|
-
assertCompositionXmlUsesV4WidgetsOnly(rawParams.xmlStructure);
|
|
5396
|
-
const { stylesConfig: convertedStyles, customCSS } = await convertCompositionStyles(rawParams.style);
|
|
5397
|
-
const { xmlStructure, elementConfig, stylesConfig } = adaptLeafRootParams({
|
|
5398
|
-
...rawParams,
|
|
5399
|
-
stylesConfig: convertedStyles,
|
|
5400
|
-
widgetsCache: getWidgetsCache8() ?? {}
|
|
5401
|
-
});
|
|
5402
|
-
let generatedXML = "";
|
|
5403
|
-
const errors = [];
|
|
5404
|
-
const rootContainers = [];
|
|
5405
|
-
const documentContainer = getContainer5("document");
|
|
5406
|
-
const currentDocument = getCurrentDocument();
|
|
5407
|
-
const targetContainer = getCompositionTargetContainer(documentContainer, currentDocument?.type.value);
|
|
5408
|
-
try {
|
|
5409
|
-
const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
|
|
5410
|
-
createElement: createElement13,
|
|
5411
|
-
deleteElement: deleteElement2,
|
|
5412
|
-
getWidgetsCache: getWidgetsCache8
|
|
5413
|
-
});
|
|
5414
|
-
compositionBuilder.setElementConfig(elementConfig);
|
|
5415
|
-
compositionBuilder.setStylesConfig(stylesConfig);
|
|
5416
|
-
compositionBuilder.setCustomCSS(customCSS);
|
|
5417
|
-
const {
|
|
5418
|
-
configErrors,
|
|
5419
|
-
formErrors,
|
|
5420
|
-
rootContainers: generatedRootContainers
|
|
5421
|
-
} = await compositionBuilder.build(targetContainer);
|
|
5422
|
-
rootContainers.push(...generatedRootContainers);
|
|
5423
|
-
generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
|
|
5424
|
-
rootContainers.forEach((container) => {
|
|
5425
|
-
const elementData = container.model?.toJSON();
|
|
5426
|
-
if (elementData) {
|
|
5427
|
-
onElementAdded(elementData);
|
|
5428
|
-
}
|
|
5429
|
-
});
|
|
5430
|
-
Object.values(stylesConfig).forEach((styleValue) => {
|
|
5431
|
-
dispatchMcpStylesAppliedEvent({ styleValue });
|
|
5432
|
-
});
|
|
5433
|
-
if (configErrors.length) {
|
|
5434
|
-
errors.push(...configErrors.map((msg) => new Error(msg)));
|
|
5435
|
-
}
|
|
5436
|
-
if (formErrors.length) {
|
|
5437
|
-
errors.push(...formErrors.map((msg) => new Error(msg)));
|
|
5438
|
-
}
|
|
5439
|
-
} catch (error) {
|
|
5440
|
-
errors.push(error);
|
|
5441
|
-
}
|
|
5442
|
-
if (errors.length) {
|
|
5443
|
-
rootContainers.forEach((rootContainer) => {
|
|
5444
|
-
deleteElement2({
|
|
5445
|
-
container: rootContainer,
|
|
5446
|
-
options: { useHistory: false }
|
|
5447
|
-
});
|
|
5448
|
-
});
|
|
5449
|
-
const errorMessages = errors.map((e) => {
|
|
5450
|
-
if (typeof e === "string") {
|
|
5451
|
-
return e;
|
|
5452
|
-
}
|
|
5453
|
-
if (e instanceof Error) {
|
|
5454
|
-
return e.message || String(e);
|
|
5455
|
-
}
|
|
5456
|
-
if (typeof e === "object" && e !== null) {
|
|
5457
|
-
return JSON.stringify(e);
|
|
5458
|
-
}
|
|
5459
|
-
return String(e);
|
|
5460
|
-
}).filter(
|
|
5461
|
-
(msg) => msg && msg.trim() !== "" && msg !== "{}" && msg !== "null" && msg !== "undefined"
|
|
5462
|
-
);
|
|
5463
|
-
if (errorMessages.length === 0) {
|
|
5464
|
-
throw new Error(
|
|
5465
|
-
"Failed to build composition: Unknown error occurred. No error details available."
|
|
5466
|
-
);
|
|
5467
|
-
}
|
|
5468
|
-
const errorText = `Failed to build composition with the following errors:
|
|
5469
|
-
|
|
5470
|
-
${errorMessages.join(
|
|
5471
|
-
"\n\n"
|
|
5472
|
-
)}`;
|
|
5473
|
-
throw new Error(errorText);
|
|
5474
|
-
}
|
|
5475
|
-
return {
|
|
5476
|
-
xmlStructure: generatedXML,
|
|
5477
|
-
errors: errors?.length ? errors.map((e) => typeof e === "string" ? e : e.message).join("\n\n") : void 0,
|
|
5478
|
-
llm_instructions: `The composition was built successfully with element IDs embedded in the XML.
|
|
5479
|
-
|
|
5480
|
-
**CRITICAL NEXT STEPS** (Follow in order):
|
|
5481
|
-
1. **Apply Global Classes**: Use "apply-global-class" tool to apply the global classes you created BEFORE building this composition
|
|
5482
|
-
- Check the created element IDs in the returned XML
|
|
5483
|
-
- Apply semantic classes (heading-primary, button-cta, etc.) to appropriate elements
|
|
5484
|
-
|
|
5485
|
-
2. **Fine-tune if needed**: Use "configure-element" tool only for element-specific adjustments that don't warrant global classes
|
|
5486
|
-
|
|
5487
|
-
Remember: Global classes ensure design consistency and reusability. Don't skip applying them!
|
|
5488
|
-
`
|
|
5489
|
-
};
|
|
5490
|
-
}
|
|
5491
|
-
});
|
|
5492
|
-
};
|
|
5493
|
-
async function convertCompositionStyles(style) {
|
|
5494
|
-
const stylesConfig = {};
|
|
5495
|
-
const customCSS = {};
|
|
5496
|
-
if (!style || Object.keys(style).length === 0) {
|
|
5497
|
-
return { stylesConfig, customCSS };
|
|
5498
|
-
}
|
|
5499
|
-
const results = await convertStyleBlocksToAtomic(style);
|
|
5500
|
-
for (const [configId, { props, customCss }] of Object.entries(results)) {
|
|
5501
|
-
stylesConfig[configId] = props;
|
|
5502
|
-
if (customCss) {
|
|
5503
|
-
customCSS[configId] = customCss;
|
|
5504
|
-
}
|
|
5505
|
-
}
|
|
5506
|
-
return { stylesConfig, customCSS };
|
|
5507
|
-
}
|
|
5508
|
-
function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
|
|
5509
|
-
const doc = new DOMParser().parseFromString(xmlStructure, "application/xml");
|
|
5510
|
-
if (doc.querySelector("parsererror")) {
|
|
5511
|
-
throw new Error("Failed to parse XML string: " + doc);
|
|
5512
|
-
}
|
|
5513
|
-
const widgetsCache = getWidgetsCache8() ?? {};
|
|
5514
|
-
for (const node of doc.querySelectorAll("*")) {
|
|
5515
|
-
const type = node.tagName;
|
|
5516
|
-
const widgetData = widgetsCache[type];
|
|
5517
|
-
if (!widgetData) {
|
|
5518
|
-
continue;
|
|
5519
|
-
}
|
|
5520
|
-
if (widgetData.elType !== "widget") {
|
|
5521
|
-
continue;
|
|
5522
|
-
}
|
|
5523
|
-
if (!isWidgetAvailableForLLM(widgetData) || !widgetData.atomic_props_schema) {
|
|
5524
|
-
throw new Error(`This tool does not support element type: ${type}`);
|
|
5525
|
-
}
|
|
5526
|
-
}
|
|
5527
|
-
}
|
|
5528
|
-
function onElementAdded(element) {
|
|
5529
|
-
const elType = element.elType ?? "";
|
|
5530
|
-
const widgetType = element.widgetType ?? "";
|
|
5531
|
-
const elementName = elType === "widget" ? widgetType : elType;
|
|
5532
|
-
trackCanvasEvent({
|
|
5533
|
-
eventName: "add_element",
|
|
5534
|
-
executed_by: "mcp_tool",
|
|
5535
|
-
element_name: elementName,
|
|
5536
|
-
element_type: elType,
|
|
5537
|
-
widget_type: widgetType
|
|
5538
|
-
});
|
|
5539
|
-
const event = {
|
|
5540
|
-
element,
|
|
5541
|
-
executedBy: "mcp_tool"
|
|
5542
|
-
};
|
|
5543
|
-
window.dispatchEvent(new CustomEvent(ELEMENT_ADDED_EVENT, { detail: event }));
|
|
5544
|
-
if (element.elements?.length) {
|
|
5545
|
-
element.elements?.forEach((childElement) => {
|
|
5546
|
-
onElementAdded(childElement);
|
|
5547
|
-
});
|
|
5548
|
-
}
|
|
5549
|
-
}
|
|
5550
|
-
|
|
5551
|
-
// src/mcp/tools/configure-element/tool.ts
|
|
5552
|
-
import { getContainer as getContainer6, getWidgetsCache as getWidgetsCache9 } from "@elementor/editor-elements";
|
|
5553
|
-
import { dispatchMcpStylesAppliedEvent as dispatchMcpStylesAppliedEvent2 } from "@elementor/editor-mcp";
|
|
5554
|
-
import { Schema as Schema2 } from "@elementor/editor-props";
|
|
5555
|
-
|
|
5556
4822
|
// src/mcp/tools/configure-element/prompt.ts
|
|
5557
|
-
import { toolPrompts
|
|
4823
|
+
import { toolPrompts } from "@elementor/editor-mcp";
|
|
5558
4824
|
var CONFIGURE_ELEMENT_GUIDE_URI = "elementor://canvas/tools/configure-element-guide";
|
|
5559
|
-
var
|
|
5560
|
-
const configureElementToolPrompt =
|
|
4825
|
+
var generatePrompt = () => {
|
|
4826
|
+
const configureElementToolPrompt = toolPrompts("configure-element");
|
|
5561
4827
|
configureElementToolPrompt.description(`
|
|
5562
4828
|
Configure an existing element on the page.
|
|
5563
4829
|
|
|
@@ -5665,11 +4931,11 @@ NO Advanced tab. Never mention Advanced tab.
|
|
|
5665
4931
|
`);
|
|
5666
4932
|
return configureElementToolPrompt.prompt();
|
|
5667
4933
|
};
|
|
5668
|
-
var CONFIGURE_ELEMENT_GUIDE_TEXT =
|
|
4934
|
+
var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt();
|
|
5669
4935
|
|
|
5670
4936
|
// src/mcp/tools/configure-element/schema.ts
|
|
5671
4937
|
import { z as z2 } from "@elementor/schema";
|
|
5672
|
-
var
|
|
4938
|
+
var inputSchema = {
|
|
5673
4939
|
propertiesToChange: z2.record(
|
|
5674
4940
|
z2.string().describe("The property name."),
|
|
5675
4941
|
z2.any().describe(`PropValue, refer to [${WIDGET_SCHEMA_URI}] by correct type, as appears in elementType`),
|
|
@@ -5686,7 +4952,7 @@ var inputSchema2 = {
|
|
|
5686
4952
|
elementType: z2.string().describe("The type of the element to retrieve the schema"),
|
|
5687
4953
|
elementId: z2.string().describe("The unique id of the element to configure")
|
|
5688
4954
|
};
|
|
5689
|
-
var
|
|
4955
|
+
var outputSchema = {
|
|
5690
4956
|
success: z2.boolean().describe(
|
|
5691
4957
|
"Whether the configuration change was successful, only if propertyName and propertyValue are provided"
|
|
5692
4958
|
)
|
|
@@ -5704,27 +4970,27 @@ var initConfigureElementTool = (reg) => {
|
|
|
5704
4970
|
mimeType: "text/plain"
|
|
5705
4971
|
},
|
|
5706
4972
|
async (uri) => ({
|
|
5707
|
-
contents: [{ uri: uri.href, mimeType: "text/plain", text:
|
|
4973
|
+
contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt() }]
|
|
5708
4974
|
})
|
|
5709
4975
|
);
|
|
5710
4976
|
addTool({
|
|
5711
4977
|
name: "configure-element",
|
|
5712
4978
|
description: "Configure an existing V4 element's properties and styles. Read the guide resource before use.",
|
|
5713
|
-
schema:
|
|
5714
|
-
outputSchema
|
|
4979
|
+
schema: inputSchema,
|
|
4980
|
+
outputSchema,
|
|
5715
4981
|
requiredResources: [
|
|
5716
4982
|
{ description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
|
|
5717
4983
|
{ description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI },
|
|
5718
4984
|
{ description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
|
|
5719
4985
|
],
|
|
5720
4986
|
handler: async ({ elementId, propertiesToChange, elementType, style }) => {
|
|
5721
|
-
const widgetData =
|
|
4987
|
+
const widgetData = getWidgetsCache6()?.[elementType];
|
|
5722
4988
|
if (!widgetData) {
|
|
5723
4989
|
throw new Error(
|
|
5724
4990
|
`Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
|
|
5725
4991
|
);
|
|
5726
4992
|
}
|
|
5727
|
-
const container =
|
|
4993
|
+
const container = getContainer5(elementId);
|
|
5728
4994
|
if (!container) {
|
|
5729
4995
|
throw new Error(`Element with id ${elementId} not found`);
|
|
5730
4996
|
}
|
|
@@ -5789,7 +5055,7 @@ async function applyStyleFromCss(opts) {
|
|
|
5789
5055
|
propertyValue: styleValue,
|
|
5790
5056
|
customCssWriteMode: "merge-with-stored"
|
|
5791
5057
|
});
|
|
5792
|
-
|
|
5058
|
+
dispatchMcpStylesAppliedEvent({ styleValue });
|
|
5793
5059
|
} catch (error) {
|
|
5794
5060
|
throw new Error(
|
|
5795
5061
|
createUpdateErrorMessage({
|
|
@@ -5817,9 +5083,9 @@ Provide styling as raw CSS via the "style" parameter (a flat map of CSS property
|
|
|
5817
5083
|
|
|
5818
5084
|
// src/mcp/tools/create-element/tool.ts
|
|
5819
5085
|
import { getCurrentDocument as getCurrentDocument2 } from "@elementor/editor-documents";
|
|
5820
|
-
import { httpService as
|
|
5086
|
+
import { httpService as httpService7 } from "@elementor/http-client";
|
|
5821
5087
|
import { z as z3 } from "@elementor/schema";
|
|
5822
|
-
var
|
|
5088
|
+
var MCP_PROXY_URL6 = "elementor/v1/mcp-proxy";
|
|
5823
5089
|
var initCreateElementTool = (reg) => {
|
|
5824
5090
|
const { addTool } = reg;
|
|
5825
5091
|
addTool({
|
|
@@ -5839,7 +5105,7 @@ var initCreateElementTool = (reg) => {
|
|
|
5839
5105
|
if (!document2?.id) {
|
|
5840
5106
|
throw new Error("No active document found.");
|
|
5841
5107
|
}
|
|
5842
|
-
const { data } = await
|
|
5108
|
+
const { data } = await httpService7().post(MCP_PROXY_URL6, {
|
|
5843
5109
|
tool: "create-element",
|
|
5844
5110
|
input: {
|
|
5845
5111
|
parent_id: parentId ?? "document",
|
|
@@ -5857,13 +5123,13 @@ var initCreateElementTool = (reg) => {
|
|
|
5857
5123
|
};
|
|
5858
5124
|
|
|
5859
5125
|
// src/mcp/tools/get-element-config/tool.ts
|
|
5860
|
-
import { getContainer as
|
|
5126
|
+
import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
|
|
5861
5127
|
import { Schema as Schema3 } from "@elementor/editor-props";
|
|
5862
5128
|
import { z as z4 } from "@elementor/schema";
|
|
5863
5129
|
var schema = {
|
|
5864
5130
|
elementId: z4.string()
|
|
5865
5131
|
};
|
|
5866
|
-
var
|
|
5132
|
+
var outputSchema2 = {
|
|
5867
5133
|
properties: z4.record(z4.string(), z4.any()).describe("A record mapping PropTypes to their corresponding PropValues"),
|
|
5868
5134
|
style: z4.record(z4.string(), z4.any()).describe("A record mapping StyleSchema properties to their corresponding PropValues"),
|
|
5869
5135
|
childElements: z4.array(
|
|
@@ -5890,14 +5156,14 @@ var initGetElementConfigTool = (reg) => {
|
|
|
5890
5156
|
name: "get-element-configuration-values",
|
|
5891
5157
|
description: "Retrieve the element's configuration PropValues for a specific element by unique ID.",
|
|
5892
5158
|
schema,
|
|
5893
|
-
outputSchema:
|
|
5159
|
+
outputSchema: outputSchema2,
|
|
5894
5160
|
handler: async ({ elementId }) => {
|
|
5895
|
-
const element =
|
|
5161
|
+
const element = getContainer6(elementId);
|
|
5896
5162
|
if (!element) {
|
|
5897
5163
|
throw new Error(`Element with ID ${elementId} not found.`);
|
|
5898
5164
|
}
|
|
5899
5165
|
const elementType = element.model.get("widgetType") || element.model.get("elType") || "";
|
|
5900
|
-
const widgetData =
|
|
5166
|
+
const widgetData = getWidgetsCache7()?.[elementType];
|
|
5901
5167
|
if (!widgetData) {
|
|
5902
5168
|
throw new Error(
|
|
5903
5169
|
`Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
|
|
@@ -5909,7 +5175,7 @@ var initGetElementConfigTool = (reg) => {
|
|
|
5909
5175
|
);
|
|
5910
5176
|
}
|
|
5911
5177
|
const elementRawSettings = element.settings;
|
|
5912
|
-
const propSchema =
|
|
5178
|
+
const propSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
|
|
5913
5179
|
if (!elementRawSettings || !propSchema) {
|
|
5914
5180
|
throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
|
|
5915
5181
|
}
|
|
@@ -5960,10 +5226,10 @@ var initCanvasMcp = (reg) => {
|
|
|
5960
5226
|
initEditorStateResource(reg);
|
|
5961
5227
|
initGeneralContextResource(reg);
|
|
5962
5228
|
initBestPracticesResource(reg);
|
|
5963
|
-
initBuildCompositionsTool(reg);
|
|
5964
5229
|
initGetElementConfigTool(reg);
|
|
5965
5230
|
initConfigureElementTool(reg);
|
|
5966
5231
|
initCreateElementTool(reg);
|
|
5232
|
+
initBuildCompositionTool(reg);
|
|
5967
5233
|
initBreakpointsResource(reg);
|
|
5968
5234
|
};
|
|
5969
5235
|
|
|
@@ -6163,7 +5429,7 @@ function shouldBlock(sourceElements, targetElements) {
|
|
|
6163
5429
|
}
|
|
6164
5430
|
|
|
6165
5431
|
// src/style-commands/paste-style.ts
|
|
6166
|
-
import { getContainer as
|
|
5432
|
+
import { getContainer as getContainer7, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
|
|
6167
5433
|
import { classesPropTypeUtil } from "@elementor/editor-props";
|
|
6168
5434
|
import {
|
|
6169
5435
|
__privateListenTo as listenTo5,
|
|
@@ -6172,7 +5438,7 @@ import {
|
|
|
6172
5438
|
} from "@elementor/editor-v1-adapters";
|
|
6173
5439
|
|
|
6174
5440
|
// src/utils/command-utils.ts
|
|
6175
|
-
import { getElementLabel as getElementLabel2, getWidgetsCache as
|
|
5441
|
+
import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
|
|
6176
5442
|
import { CLASSES_PROP_KEY } from "@elementor/editor-props";
|
|
6177
5443
|
import { __ as __5 } from "@wordpress/i18n";
|
|
6178
5444
|
function hasAtomicWidgets(args) {
|
|
@@ -6197,7 +5463,7 @@ function getClassesProp(container) {
|
|
|
6197
5463
|
}
|
|
6198
5464
|
function getContainerSchema(container) {
|
|
6199
5465
|
const type = container?.model.get("widgetType") || container?.model.get("elType");
|
|
6200
|
-
const widgetsCache =
|
|
5466
|
+
const widgetsCache = getWidgetsCache8();
|
|
6201
5467
|
const elementType = widgetsCache?.[type];
|
|
6202
5468
|
return elementType?.atomic_props_schema ?? null;
|
|
6203
5469
|
}
|
|
@@ -6316,7 +5582,7 @@ function pasteStyles(args, pasteLocalStyle) {
|
|
|
6316
5582
|
}
|
|
6317
5583
|
const clipboardElements = getClipboardElements(storageKey);
|
|
6318
5584
|
const [clipboardElement] = clipboardElements ?? [];
|
|
6319
|
-
const clipboardContainer =
|
|
5585
|
+
const clipboardContainer = getContainer7(clipboardElement.id);
|
|
6320
5586
|
if (!clipboardElement || !clipboardContainer || !isAtomicWidget(clipboardContainer)) {
|
|
6321
5587
|
return;
|
|
6322
5588
|
}
|
|
@@ -6593,10 +5859,10 @@ function useEscapeOnCanvas(canvasDocument, onEscape) {
|
|
|
6593
5859
|
}
|
|
6594
5860
|
|
|
6595
5861
|
// src/utils/after-render.ts
|
|
6596
|
-
import { getContainer as
|
|
5862
|
+
import { getContainer as getContainer8 } from "@elementor/editor-elements";
|
|
6597
5863
|
function doAfterRender(elementIds, callback) {
|
|
6598
5864
|
const pending = elementIds.map((elementId) => {
|
|
6599
|
-
const view =
|
|
5865
|
+
const view = getContainer8(elementId)?.view;
|
|
6600
5866
|
if (!view || !hasDoAfterRender(view)) {
|
|
6601
5867
|
return void 0;
|
|
6602
5868
|
}
|
|
@@ -6611,6 +5877,9 @@ function doAfterRender(elementIds, callback) {
|
|
|
6611
5877
|
function hasDoAfterRender(view) {
|
|
6612
5878
|
return typeof view?._doAfterRender === "function";
|
|
6613
5879
|
}
|
|
5880
|
+
|
|
5881
|
+
// src/sync/element-added-event.ts
|
|
5882
|
+
var ELEMENT_ADDED_EVENT = "elementor/canvas/element-added";
|
|
6614
5883
|
export {
|
|
6615
5884
|
BREAKPOINTS_SCHEMA_FULL_URI,
|
|
6616
5885
|
BREAKPOINTS_SCHEMA_URI,
|