@elementor/editor-canvas 4.3.0-998 → 4.3.0-beta1

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.
Files changed (50) hide show
  1. package/dist/index.d.mts +3 -1
  2. package/dist/index.d.ts +3 -1
  3. package/dist/index.js +452 -408
  4. package/dist/index.mjs +434 -386
  5. package/package.json +20 -20
  6. package/src/init-settings-transformers.ts +4 -0
  7. package/src/init-style-transformers.ts +2 -2
  8. package/src/init.tsx +5 -4
  9. package/src/legacy/__tests__/create-templated-element-type.test.ts +50 -0
  10. package/src/legacy/__tests__/list-type.test.ts +89 -0
  11. package/src/legacy/create-nested-templated-element-type.ts +3 -1
  12. package/src/legacy/create-templated-element-type.ts +5 -2
  13. package/src/legacy/list-type.ts +63 -0
  14. package/src/legacy/replacements/inline-editing/__tests__/inline-editing-eligibility.test.ts +7 -7
  15. package/src/legacy/replacements/inline-editing/inline-editing-elements.tsx +22 -9
  16. package/src/legacy/replacements/inline-editing/inline-editing-eligibility.ts +12 -3
  17. package/src/legacy/tabs-model-extensions.ts +2 -5
  18. package/src/legacy/twig-rendering-utils.ts +11 -2
  19. package/src/legacy/types.ts +4 -0
  20. package/src/mcp/canvas-mcp.ts +2 -6
  21. package/src/mcp/mcp-description.ts +18 -30
  22. package/src/mcp/resources/__tests__/available-widgets-resource.test.ts +22 -13
  23. package/src/mcp/resources/__tests__/dynamic-tags-resource.test.ts +22 -30
  24. package/src/mcp/resources/__tests__/widgets-schema-resource.test.ts +11 -6
  25. package/src/mcp/resources/available-widgets-resource.ts +13 -10
  26. package/src/mcp/resources/dynamic-tags-resource.ts +6 -15
  27. package/src/mcp/resources/widgets-schema-resource.ts +8 -5
  28. package/src/mcp/tools/configure-element/__tests__/tool.test.ts +130 -0
  29. package/src/mcp/tools/configure-element/prompt.ts +3 -6
  30. package/src/mcp/tools/configure-element/schema.ts +6 -0
  31. package/src/mcp/tools/configure-element/tool.ts +11 -1
  32. package/src/mcp/tools/get-page-structure/tool.ts +73 -0
  33. package/src/mcp/utils/__tests__/do-update-element-property.test.ts +27 -1
  34. package/src/mcp/utils/do-update-element-property.ts +18 -6
  35. package/src/mcp/utils/get-mcp-error-message.ts +16 -0
  36. package/src/renderers/__tests__/compute-html-tag.test.ts +55 -0
  37. package/src/renderers/__tests__/create-dom-renderer.test.ts +60 -0
  38. package/src/renderers/__tests__/fixtures/html-tag-computer-cases.json +87 -0
  39. package/src/renderers/compute-html-tag.ts +73 -0
  40. package/src/renderers/create-dom-renderer.ts +13 -22
  41. package/src/transformers/settings/escaped-html-transformer.ts +6 -0
  42. package/src/transformers/shared/__tests__/icon-transformer.test.ts +74 -0
  43. package/src/transformers/shared/icon-transformer.ts +122 -0
  44. package/src/transformers/shared/process-svg-content.ts +26 -0
  45. package/src/transformers/shared/svg-src-transformer.ts +1 -26
  46. package/src/utils/__tests__/sanitize-escaped-html.test.ts +105 -0
  47. package/src/utils/sanitize-escaped-html.ts +32 -0
  48. package/src/mcp/tools/build-composition/tool.ts +0 -133
  49. package/src/mcp/tools/create-element/tool.ts +0 -61
  50. package/src/mcp/tools/get-element-config/tool.ts +0 -114
package/dist/index.mjs CHANGED
@@ -12,10 +12,10 @@ var BEST_PRACTICES_FULL_URI = `${CANVAS_SERVER_NAME}_${BEST_PRACTICES_URI}`;
12
12
  var MCP_PROXY_URL = "elementor/v1/mcp-proxy";
13
13
  var listWidgetTypes = async () => {
14
14
  const { data } = await httpService().post(MCP_PROXY_URL, {
15
- tool: "list-widgets",
16
- input: {}
15
+ tool: "list-widget-schemas",
16
+ input: { summary: true }
17
17
  });
18
- return (data.data ?? []).map((widget) => widget.type);
18
+ return (data.data?.widgets ?? []).map((widget) => widget.type);
19
19
  };
20
20
  var fetchWidgetSchema = async (widgetType) => {
21
21
  const { data } = await httpService().post(MCP_PROXY_URL, {
@@ -47,13 +47,13 @@ var initWidgetsSchemaResource = (reg) => {
47
47
  if (!widgetType) {
48
48
  throw new Error("No widget type provided.");
49
49
  }
50
- const schema2 = await fetchWidgetSchema(widgetType);
50
+ const schema = await fetchWidgetSchema(widgetType);
51
51
  return {
52
52
  contents: [
53
53
  {
54
54
  uri: uri.toString(),
55
55
  mimeType: "application/json",
56
- text: JSON.stringify(schema2)
56
+ text: JSON.stringify(schema)
57
57
  }
58
58
  ]
59
59
  };
@@ -1260,10 +1260,10 @@ var getMultiPropsValue = (multiProps) => {
1260
1260
  // src/renderers/create-props-resolver.ts
1261
1261
  var TRANSFORM_DEPTH_LIMIT = 3;
1262
1262
  function createPropsResolver({ transformers, schema: initialSchema, onPropResolve }) {
1263
- async function resolve({ props, schema: schema2, signal, renderContext }) {
1264
- schema2 = schema2 ?? initialSchema;
1263
+ async function resolve({ props, schema, signal, renderContext }) {
1264
+ schema = schema ?? initialSchema;
1265
1265
  const promises = Promise.all(
1266
- Object.entries(schema2).map(async ([key, type]) => {
1266
+ Object.entries(schema).map(async ([key, type]) => {
1267
1267
  const value = props[key] ?? type.default;
1268
1268
  const transformed = await transform({ value, key, type, signal, renderContext });
1269
1269
  onPropResolve?.({ key, value: transformed, propValue: value, propType: type });
@@ -1989,6 +1989,40 @@ var dateTimeTransformer = createTransformer((values) => {
1989
1989
  }).join(" ");
1990
1990
  });
1991
1991
 
1992
+ // src/utils/sanitize-escaped-html.ts
1993
+ import DOMPurify from "dompurify";
1994
+ var ALLOWED_NON_OPERATIONAL_ATTRS = [
1995
+ "href",
1996
+ "target",
1997
+ "class",
1998
+ "id",
1999
+ "style",
2000
+ "title",
2001
+ "lang",
2002
+ "dir",
2003
+ "role"
2004
+ ];
2005
+ function getAllowedHtmlWrapperTags() {
2006
+ return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
2007
+ }
2008
+ function sanitizeEscapedHtml(value) {
2009
+ if (!value) {
2010
+ return "";
2011
+ }
2012
+ const allowedTags = [...getAllowedHtmlWrapperTags()];
2013
+ return DOMPurify.sanitize(value, {
2014
+ ALLOWED_TAGS: allowedTags,
2015
+ ALLOWED_ATTR: [...ALLOWED_NON_OPERATIONAL_ATTRS],
2016
+ ALLOW_DATA_ATTR: true,
2017
+ ALLOW_ARIA_ATTR: true
2018
+ });
2019
+ }
2020
+
2021
+ // src/transformers/settings/escaped-html-transformer.ts
2022
+ var escapedHtmlTransformer = createTransformer((value) => {
2023
+ return sanitizeEscapedHtml(value);
2024
+ });
2025
+
1992
2026
  // src/transformers/settings/html-v2-transformer.ts
1993
2027
  var htmlV2Transformer = createTransformer((value) => {
1994
2028
  return value?.content ?? "";
@@ -2025,6 +2059,102 @@ var timeRangeTransformer = createTransformer((value) => {
2025
2059
  };
2026
2060
  });
2027
2061
 
2062
+ // src/transformers/shared/process-svg-content.ts
2063
+ import DOMPurify2 from "dompurify";
2064
+ var SVG_INLINE_STYLES = "width: 100%; height: 100%; overflow: unset;";
2065
+ function processSvgContent(svgText) {
2066
+ const sanitized = DOMPurify2.sanitize(svgText, {
2067
+ USE_PROFILES: { svg: true, svgFilters: true }
2068
+ });
2069
+ const parser = new DOMParser();
2070
+ const doc = parser.parseFromString(sanitized, "image/svg+xml");
2071
+ const svgElement = doc.querySelector("svg");
2072
+ if (!svgElement) {
2073
+ return null;
2074
+ }
2075
+ svgElement.setAttribute("fill", "currentColor");
2076
+ const existingStyle = svgElement.getAttribute("style") ?? "";
2077
+ const trimmed = existingStyle.trim();
2078
+ const merged = trimmed ? `${trimmed.replace(/;$/, "")}; ${SVG_INLINE_STYLES}` : SVG_INLINE_STYLES;
2079
+ svgElement.setAttribute("style", merged);
2080
+ return svgElement.outerHTML;
2081
+ }
2082
+
2083
+ // src/transformers/shared/icon-transformer.ts
2084
+ var FONT_AWESOME_JSON = {
2085
+ width: 0,
2086
+ height: 1,
2087
+ path: 4
2088
+ };
2089
+ var fontAwesomeJsonCache = /* @__PURE__ */ new Map();
2090
+ var iconTransformer = createTransformer(async (value, { signal }) => {
2091
+ const iconValue = typeof value.value === "string" ? value.value : null;
2092
+ const library = typeof value.library === "string" ? value.library : null;
2093
+ if (!iconValue || !library) {
2094
+ return { html: null, url: null };
2095
+ }
2096
+ const iconName = getFontAwesomeIconName(iconValue);
2097
+ const jsonFileName = getFontAwesomeJsonFileName(library);
2098
+ if (!iconName || !jsonFileName) {
2099
+ return { html: null, url: null };
2100
+ }
2101
+ const icons = await fetchFontAwesomeIcons(jsonFileName, signal);
2102
+ const iconData = icons?.[iconName];
2103
+ if (!iconData) {
2104
+ return { html: null, url: null };
2105
+ }
2106
+ const svgText = buildFontAwesomeSvg(iconData);
2107
+ const html = processSvgContent(svgText);
2108
+ return { html, url: null };
2109
+ });
2110
+ function getFontAwesomeIconName(iconValue) {
2111
+ const match = iconValue.match(/^fa\S*\s+fa-(.+)$/);
2112
+ return match?.[1] ?? null;
2113
+ }
2114
+ function getFontAwesomeJsonFileName(library) {
2115
+ if (!library.startsWith("fa-")) {
2116
+ return null;
2117
+ }
2118
+ return library.replace(/^fa-/, "");
2119
+ }
2120
+ function getAssetsBaseUrl() {
2121
+ const assetsUrl = window.elementorCommon?.config?.urls?.assets;
2122
+ return typeof assetsUrl === "string" && assetsUrl !== "" ? assetsUrl : null;
2123
+ }
2124
+ async function fetchFontAwesomeIcons(jsonFileName, signal) {
2125
+ const cached = fontAwesomeJsonCache.get(jsonFileName);
2126
+ if (cached) {
2127
+ return cached;
2128
+ }
2129
+ const icons = await loadFontAwesomeIcons(jsonFileName, signal);
2130
+ if (icons) {
2131
+ fontAwesomeJsonCache.set(jsonFileName, icons);
2132
+ }
2133
+ return icons;
2134
+ }
2135
+ async function loadFontAwesomeIcons(jsonFileName, signal) {
2136
+ const assetsUrl = getAssetsBaseUrl();
2137
+ if (!assetsUrl) {
2138
+ return null;
2139
+ }
2140
+ try {
2141
+ const response = await fetch(`${assetsUrl}lib/font-awesome/json/${jsonFileName}.json`, { signal });
2142
+ if (!response.ok) {
2143
+ return null;
2144
+ }
2145
+ const data = await response.json();
2146
+ return data.icons ?? null;
2147
+ } catch {
2148
+ return null;
2149
+ }
2150
+ }
2151
+ function buildFontAwesomeSvg(iconData) {
2152
+ const width = iconData[FONT_AWESOME_JSON.width];
2153
+ const height = iconData[FONT_AWESOME_JSON.height];
2154
+ const path = iconData[FONT_AWESOME_JSON.path];
2155
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"><path d="${path}"></path></svg>`;
2156
+ }
2157
+
2028
2158
  // src/transformers/shared/image-src-transformer.ts
2029
2159
  var imageSrcTransformer = createTransformer((value) => ({
2030
2160
  id: value.id ?? null,
@@ -2066,26 +2196,7 @@ var plainTransformer = createTransformer((value) => {
2066
2196
  });
2067
2197
 
2068
2198
  // src/transformers/shared/svg-src-transformer.ts
2069
- import DOMPurify from "dompurify";
2070
2199
  import { getMediaAttachment as getMediaAttachment2 } from "@elementor/wp-media";
2071
- var SVG_INLINE_STYLES = "width: 100%; height: 100%; overflow: unset;";
2072
- function processSvgContent(svgText) {
2073
- const sanitized = DOMPurify.sanitize(svgText, {
2074
- USE_PROFILES: { svg: true, svgFilters: true }
2075
- });
2076
- const parser = new DOMParser();
2077
- const doc = parser.parseFromString(sanitized, "image/svg+xml");
2078
- const svgElement = doc.querySelector("svg");
2079
- if (!svgElement) {
2080
- return null;
2081
- }
2082
- svgElement.setAttribute("fill", "currentColor");
2083
- const existingStyle = svgElement.getAttribute("style") ?? "";
2084
- const trimmed = existingStyle.trim();
2085
- const merged = trimmed ? `${trimmed.replace(/;$/, "")}; ${SVG_INLINE_STYLES}` : SVG_INLINE_STYLES;
2086
- svgElement.setAttribute("style", merged);
2087
- return svgElement.outerHTML;
2088
- }
2089
2200
  async function fetchSvgContent(url, signal) {
2090
2201
  try {
2091
2202
  const response = await fetch(url, { signal });
@@ -2141,7 +2252,7 @@ var videoSrcTransformer = createTransformer(async (value) => {
2141
2252
 
2142
2253
  // src/init-settings-transformers.ts
2143
2254
  function initSettingsTransformers() {
2144
- settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("svg-src", svgSrcTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).register("date-range", dateRangeTransformer).register("time-range", timeRangeTransformer).registerFallback(plainTransformer);
2255
+ settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("svg-src", svgSrcTransformer).register("icon", iconTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).register("escaped-html", escapedHtmlTransformer).register("date-range", dateRangeTransformer).register("time-range", timeRangeTransformer).registerFallback(plainTransformer);
2145
2256
  }
2146
2257
 
2147
2258
  // src/transformers/styles/background-color-overlay-transformer.ts
@@ -2418,11 +2529,11 @@ function getVal2(val) {
2418
2529
  var transformOriginTransformer = createTransformer((value) => {
2419
2530
  const x = getVal2(value.x);
2420
2531
  const y = getVal2(value.y);
2421
- const z5 = getVal2(value.z);
2422
- if (x === DEFAULT_XY && y === DEFAULT_XY && z5 === DEFAULT_Z) {
2532
+ const z3 = getVal2(value.z);
2533
+ if (x === DEFAULT_XY && y === DEFAULT_XY && z3 === DEFAULT_Z) {
2423
2534
  return null;
2424
2535
  }
2425
- return `${x} ${y} ${z5}`;
2536
+ return `${x} ${y} ${z3}`;
2426
2537
  });
2427
2538
 
2428
2539
  // src/transformers/styles/transform-rotate-transformer.ts
@@ -2500,13 +2611,13 @@ function initStyleTransformers() {
2500
2611
  "layout-direction",
2501
2612
  createMultiPropsTransformer(["row", "column"], ({ propKey, key }) => `${key}-${propKey}`)
2502
2613
  ).register("flex", flexTransformer).register(
2503
- "border-width",
2614
+ "border-width-v2",
2504
2615
  createMultiPropsTransformer(
2505
2616
  ["block-start", "block-end", "inline-start", "inline-end"],
2506
2617
  ({ key }) => `border-${key}-width`
2507
2618
  )
2508
2619
  ).register(
2509
- "border-radius",
2620
+ "border-radius-v2",
2510
2621
  createMultiPropsTransformer(
2511
2622
  ["start-start", "start-end", "end-start", "end-end"],
2512
2623
  ({ key }) => `border-${key}-radius`
@@ -2530,28 +2641,13 @@ function createDomRenderer() {
2530
2641
  render: environment.render
2531
2642
  };
2532
2643
  }
2644
+ function getAllowedHtmlWrapperTags2() {
2645
+ return window.elementorCommon?.config?.allowedHTMLWrapperTags ?? [];
2646
+ }
2533
2647
  function escapeHtmlTag(value) {
2534
- const allowedTags = [
2535
- "a",
2536
- "article",
2537
- "aside",
2538
- "button",
2539
- "div",
2540
- "footer",
2541
- "h1",
2542
- "h2",
2543
- "h3",
2544
- "h4",
2545
- "h5",
2546
- "h6",
2547
- "header",
2548
- "main",
2549
- "nav",
2550
- "p",
2551
- "section",
2552
- "span"
2553
- ];
2554
- return allowedTags.includes(value) ? value : "div";
2648
+ const allowedTags = getAllowedHtmlWrapperTags2();
2649
+ const normalizedTag = value?.toLowerCase?.() ?? "";
2650
+ return allowedTags.includes(normalizedTag) ? value : "div";
2555
2651
  }
2556
2652
  function escapeURL(value) {
2557
2653
  const allowedProtocols = ["http:", "https:", "mailto:", "tel:"];
@@ -2646,6 +2742,52 @@ function createElementViewClassDeclaration() {
2646
2742
  // src/legacy/create-nested-templated-element-type.ts
2647
2743
  import { ELEMENT_STYLE_CHANGE_EVENT as ELEMENT_STYLE_CHANGE_EVENT2 } from "@elementor/editor-elements";
2648
2744
 
2745
+ // src/renderers/compute-html-tag.ts
2746
+ var DEFAULT_LINK_TAG = "a";
2747
+ function computeHtmlTag(settings, defaultTag, options = {}) {
2748
+ const followLink = options.followLink ?? true;
2749
+ if (followLink && settingsHaveActiveLink(settings)) {
2750
+ const link = settings.link;
2751
+ return extractLinkHtmlTag(isRecord(link) ? link : {});
2752
+ }
2753
+ const settingsTag = extractHtmlTagValue(settings.tag);
2754
+ if (null !== settingsTag && "" !== settingsTag) {
2755
+ return settingsTag;
2756
+ }
2757
+ return defaultTag;
2758
+ }
2759
+ function settingsHaveActiveLink(settings) {
2760
+ const link = settings.link;
2761
+ if (!isRecord(link)) {
2762
+ return false;
2763
+ }
2764
+ const href = extractHtmlTagValue(link.href);
2765
+ if (null !== href && "" !== href) {
2766
+ return true;
2767
+ }
2768
+ const attributes = link.attributes;
2769
+ return typeof attributes === "string" && "" !== attributes;
2770
+ }
2771
+ function extractLinkHtmlTag(link) {
2772
+ const tag = extractHtmlTagValue(link.tag);
2773
+ if (null !== tag && "" !== tag) {
2774
+ return tag;
2775
+ }
2776
+ return DEFAULT_LINK_TAG;
2777
+ }
2778
+ function extractHtmlTagValue(value) {
2779
+ if (isRecord(value) && typeof value.value === "string") {
2780
+ return value.value;
2781
+ }
2782
+ if (typeof value === "string") {
2783
+ return value;
2784
+ }
2785
+ return null;
2786
+ }
2787
+ function isRecord(value) {
2788
+ return typeof value === "object" && null !== value && !Array.isArray(value);
2789
+ }
2790
+
2649
2791
  // src/legacy/create-pending-element.ts
2650
2792
  import {
2651
2793
  addModelToParent,
@@ -2711,7 +2853,13 @@ function setupTwigRenderer({ renderer, element }) {
2711
2853
  transformers: settingsTransformersRegistry,
2712
2854
  schema: element.atomic_props_schema
2713
2855
  });
2714
- return { templateKey, baseStylesDictionary, resolveProps };
2856
+ return {
2857
+ templateKey,
2858
+ baseStylesDictionary,
2859
+ resolveProps,
2860
+ defaultHtmlTag: element.default_html_tag ?? "div",
2861
+ htmlTagFollowsLink: element.html_tag_follows_link ?? true
2862
+ };
2715
2863
  }
2716
2864
  function createBeforeRender(view) {
2717
2865
  view._ensureViewIsIntact();
@@ -2751,7 +2899,7 @@ function createTemplatedElementView({
2751
2899
  element
2752
2900
  }) {
2753
2901
  const BaseView = createElementViewClassDeclaration();
2754
- const { templateKey, baseStylesDictionary, resolveProps } = setupTwigRenderer({
2902
+ const { templateKey, baseStylesDictionary, resolveProps, defaultHtmlTag, htmlTagFollowsLink } = setupTwigRenderer({
2755
2903
  type,
2756
2904
  renderer,
2757
2905
  element
@@ -2821,6 +2969,7 @@ function createTemplatedElementView({
2821
2969
  interaction_id: this.getInteractionId(),
2822
2970
  type,
2823
2971
  settings,
2972
+ tag: computeHtmlTag(settings, defaultHtmlTag, { followLink: htmlTagFollowsLink }),
2824
2973
  base_styles: baseStylesDictionary,
2825
2974
  ...this.getResolverRenderContext?.() ?? {}
2826
2975
  };
@@ -2910,7 +3059,7 @@ function createNestedTemplatedElementView({
2910
3059
  element
2911
3060
  }) {
2912
3061
  const legacyWindow = window;
2913
- const { templateKey, baseStylesDictionary, resolveProps } = setupTwigRenderer({
3062
+ const { templateKey, baseStylesDictionary, resolveProps, defaultHtmlTag, htmlTagFollowsLink } = setupTwigRenderer({
2914
3063
  type,
2915
3064
  renderer,
2916
3065
  element
@@ -2985,6 +3134,7 @@ function createNestedTemplatedElementView({
2985
3134
  interaction_id: this.getInteractionId(),
2986
3135
  type,
2987
3136
  settings: resolvedSettings,
3137
+ tag: computeHtmlTag(resolvedSettings, defaultHtmlTag, { followLink: htmlTagFollowsLink }),
2988
3138
  base_styles: baseStylesDictionary,
2989
3139
  editor_attributes: buildEditorAttributes(model),
2990
3140
  editor_classes: buildEditorClasses(model),
@@ -3208,8 +3358,8 @@ import { createRoot } from "react-dom/client";
3208
3358
  import * as React11 from "react";
3209
3359
  import { getContainer as getContainer2, getElementLabel, getElementType as getElementType2 } from "@elementor/editor-elements";
3210
3360
  import {
3361
+ escapedHtmlPropTypeUtil as escapedHtmlPropTypeUtil2,
3211
3362
  htmlV3PropTypeUtil as htmlV3PropTypeUtil2,
3212
- parseHtmlChildren,
3213
3363
  stringPropTypeUtil as stringPropTypeUtil2
3214
3364
  } from "@elementor/editor-props";
3215
3365
  import { __privateRunCommandSync as runCommandSync, getCurrentEditMode, undoable } from "@elementor/editor-v1-adapters";
@@ -3488,11 +3638,15 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
3488
3638
  };
3489
3639
 
3490
3640
  // src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
3491
- import { htmlV3PropTypeUtil, stringPropTypeUtil } from "@elementor/editor-props";
3641
+ import {
3642
+ escapedHtmlPropTypeUtil,
3643
+ htmlV3PropTypeUtil,
3644
+ stringPropTypeUtil
3645
+ } from "@elementor/editor-props";
3492
3646
  var hasKey = (propType) => {
3493
3647
  return "key" in propType;
3494
3648
  };
3495
- var TEXT_PROP_TYPE_KEYS = /* @__PURE__ */ new Set([htmlV3PropTypeUtil.key, stringPropTypeUtil.key]);
3649
+ var TEXT_PROP_TYPE_KEYS = /* @__PURE__ */ new Set([escapedHtmlPropTypeUtil.key, htmlV3PropTypeUtil.key, stringPropTypeUtil.key]);
3496
3650
  var isCoreTextPropTypeKey = (key) => {
3497
3651
  return TEXT_PROP_TYPE_KEYS.has(key);
3498
3652
  };
@@ -3512,7 +3666,7 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
3512
3666
  if (rawValue === null || rawValue === void 0) {
3513
3667
  return isAllowedBySchema(propTypeFromSchema);
3514
3668
  }
3515
- return htmlV3PropTypeUtil.isValid(rawValue) || stringPropTypeUtil.isValid(rawValue);
3669
+ return escapedHtmlPropTypeUtil.isValid(rawValue) || htmlV3PropTypeUtil.isValid(rawValue) || stringPropTypeUtil.isValid(rawValue);
3516
3670
  };
3517
3671
 
3518
3672
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
@@ -3597,17 +3751,26 @@ var InlineEditingReplacement = class extends ReplacementBase {
3597
3751
  }
3598
3752
  getExtractedContentValue() {
3599
3753
  const propValue = this.getInlineEditablePropValue();
3754
+ if (escapedHtmlPropTypeUtil2.isValid(propValue)) {
3755
+ return escapedHtmlPropTypeUtil2.extract(propValue) ?? "";
3756
+ }
3600
3757
  const extracted = htmlV3PropTypeUtil2.extract(propValue);
3601
3758
  return stringPropTypeUtil2.extract(extracted?.content ?? null) ?? "";
3602
3759
  }
3760
+ createContentPropValue(value) {
3761
+ const content = value || "";
3762
+ const propTypeKey = this.getInlineEditablePropTypeKey();
3763
+ if (propTypeKey === htmlV3PropTypeUtil2.key) {
3764
+ return htmlV3PropTypeUtil2.create({
3765
+ content: stringPropTypeUtil2.create(content),
3766
+ children: []
3767
+ });
3768
+ }
3769
+ return escapedHtmlPropTypeUtil2.create(content);
3770
+ }
3603
3771
  setContentValue(value) {
3604
3772
  const settingKey = this.getInlineEditablePropertyName();
3605
- const html = value || "";
3606
- const parsed = parseHtmlChildren(html);
3607
- const valueToSave = htmlV3PropTypeUtil2.create({
3608
- content: parsed.content ? stringPropTypeUtil2.create(parsed.content) : null,
3609
- children: parsed.children
3610
- });
3773
+ const valueToSave = this.createContentPropValue(value);
3611
3774
  undoable(
3612
3775
  {
3613
3776
  do: () => {
@@ -3636,7 +3799,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
3636
3799
  return null;
3637
3800
  }
3638
3801
  if (propType.kind === "union") {
3639
- const textKeys = [htmlV3PropTypeUtil2.key, stringPropTypeUtil2.key];
3802
+ const textKeys = [escapedHtmlPropTypeUtil2.key, htmlV3PropTypeUtil2.key, stringPropTypeUtil2.key];
3640
3803
  for (const key of textKeys) {
3641
3804
  if (propType.prop_types[key]) {
3642
3805
  return key;
@@ -3891,8 +4054,54 @@ function createNestedTemplatedType(type, renderer, element) {
3891
4054
  });
3892
4055
  }
3893
4056
 
4057
+ // src/legacy/list-type.ts
4058
+ var LIST_TYPE = "e-list";
4059
+ function initListType() {
4060
+ registerElementType(
4061
+ LIST_TYPE,
4062
+ (options) => createListType(options)
4063
+ );
4064
+ }
4065
+ function createListType(options) {
4066
+ const BaseType = createNestedTemplatedElementType(options);
4067
+ let ListView = null;
4068
+ return class extends BaseType {
4069
+ getView() {
4070
+ if (!ListView) {
4071
+ ListView = createListView(options);
4072
+ }
4073
+ return ListView;
4074
+ }
4075
+ };
4076
+ }
4077
+ function createListView(options) {
4078
+ const BaseView = createNestedTemplatedElementView(options);
4079
+ return BaseView.extend({
4080
+ getRenderContext() {
4081
+ const parentContext = this._parent?.getRenderContext?.();
4082
+ const settings = this.model.get("settings");
4083
+ const showMarkersProp = settings?.get?.("show_markers");
4084
+ const showMarkers = showMarkersProp?.value ?? showMarkersProp ?? true;
4085
+ return {
4086
+ ...parentContext,
4087
+ show_markers: showMarkers
4088
+ };
4089
+ },
4090
+ getResolverRenderContext() {
4091
+ const parentContext = this._parent?.getResolverRenderContext?.();
4092
+ const settings = this.model.get("settings");
4093
+ const showMarkersProp = settings?.get?.("show_markers");
4094
+ const showMarkers = showMarkersProp?.value ?? showMarkersProp ?? true;
4095
+ return {
4096
+ ...parentContext,
4097
+ show_markers: showMarkers
4098
+ };
4099
+ }
4100
+ });
4101
+ }
4102
+
3894
4103
  // src/legacy/tabs-model-extensions.ts
3895
- import { htmlV3PropTypeUtil as htmlV3PropTypeUtil3, stringPropTypeUtil as stringPropTypeUtil3 } from "@elementor/editor-props";
4104
+ import { escapedHtmlPropTypeUtil as escapedHtmlPropTypeUtil3 } from "@elementor/editor-props";
3896
4105
  var tabModelExtensions = {
3897
4106
  modifyDefaultChildren(elements) {
3898
4107
  if (!Array.isArray(elements) || elements.length === 0) {
@@ -3908,10 +4117,7 @@ var tabModelExtensions = {
3908
4117
  ...paragraphElement,
3909
4118
  settings: {
3910
4119
  ...paragraphElement.settings,
3911
- paragraph: htmlV3PropTypeUtil3.create({
3912
- content: stringPropTypeUtil3.create(`Tab ${position}`),
3913
- children: []
3914
- })
4120
+ paragraph: escapedHtmlPropTypeUtil3.create(`Tab ${position}`)
3915
4121
  }
3916
4122
  };
3917
4123
  return [updatedParagraph, ...elements.slice(1)];
@@ -3922,22 +4128,22 @@ function initTabsModelExtensions() {
3922
4128
  }
3923
4129
 
3924
4130
  // src/mcp/canvas-mcp.ts
3925
- import { Schema as Schema4 } from "@elementor/editor-props";
4131
+ import { Schema as Schema3 } from "@elementor/editor-props";
3926
4132
 
3927
4133
  // src/mcp/resources/available-widgets-resource.ts
3928
4134
  import { httpService as httpService3 } from "@elementor/http-client";
3929
4135
  var MCP_PROXY_URL2 = "elementor/v1/mcp-proxy";
3930
4136
  var AVAILABLE_WIDGETS_URI = "elementor://context/available-widgets";
3931
4137
  var AVAILABLE_WIDGETS_URI_V4 = "elementor://context/available-widgets/v4";
3932
- var fetchWidgets = async (version) => {
4138
+ var fetchWidgets = async () => {
3933
4139
  const { data } = await httpService3().post(MCP_PROXY_URL2, {
3934
- tool: "list-widgets",
3935
- input: version ? { version } : {}
4140
+ tool: "list-widget-schemas",
4141
+ input: { summary: true }
3936
4142
  });
3937
- return data.data ?? [];
4143
+ return data.data?.widgets ?? [];
3938
4144
  };
3939
- var buildContents = async (uri, version) => {
3940
- const widgets = await fetchWidgets(version);
4145
+ var buildContents = async (uri) => {
4146
+ const widgets = await fetchWidgets();
3941
4147
  return {
3942
4148
  contents: [
3943
4149
  {
@@ -3956,13 +4162,13 @@ var initAvailableWidgetsResource = (reg) => {
3956
4162
  {
3957
4163
  description: "All registered v4 version widgets"
3958
4164
  },
3959
- async () => buildContents(AVAILABLE_WIDGETS_URI_V4, "v4")
4165
+ async () => buildContents(AVAILABLE_WIDGETS_URI_V4)
3960
4166
  );
3961
4167
  resource(
3962
4168
  "available-widgets",
3963
4169
  AVAILABLE_WIDGETS_URI,
3964
4170
  {
3965
- description: "All registered widget types with v3/v4 version metadata and description."
4171
+ description: "All registered v4 widget types with description."
3966
4172
  },
3967
4173
  async () => buildContents(AVAILABLE_WIDGETS_URI)
3968
4174
  );
@@ -4097,11 +4303,10 @@ import { httpService as httpService5 } from "@elementor/http-client";
4097
4303
  var DYNAMIC_TAGS_URI = "elementor://dynamic-tags";
4098
4304
  var MCP_PROXY_URL4 = "elementor/v1/mcp-proxy";
4099
4305
  var fetchDynamicTags = async () => {
4100
- const { data } = await httpService5().post(MCP_PROXY_URL4, {
4101
- tool: "list-dynamic-tags",
4102
- input: {}
4306
+ const { data } = await httpService5().get(MCP_PROXY_URL4, {
4307
+ params: { uri: DYNAMIC_TAGS_URI }
4103
4308
  });
4104
- return data.data ?? [];
4309
+ return data.data ?? "[]";
4105
4310
  };
4106
4311
  var initDynamicTagsResource = (reg) => {
4107
4312
  const { resource } = reg;
@@ -4113,13 +4318,12 @@ var initDynamicTagsResource = (reg) => {
4113
4318
  mimeType: "application/json"
4114
4319
  },
4115
4320
  async (uri) => {
4116
- const tags = await fetchDynamicTags();
4117
4321
  return {
4118
4322
  contents: [
4119
4323
  {
4120
4324
  uri: uri.href,
4121
4325
  mimeType: "application/json",
4122
- text: JSON.stringify(tags)
4326
+ text: await fetchDynamicTags()
4123
4327
  }
4124
4328
  ]
4125
4329
  };
@@ -4445,99 +4649,8 @@ function getElementDisplayName(container) {
4445
4649
  }
4446
4650
  }
4447
4651
 
4448
- // src/mcp/tools/build-composition/tool.ts
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
- }
4538
-
4539
4652
  // src/mcp/tools/configure-element/tool.ts
4540
- import { getContainer as getContainer5, getWidgetsCache as getWidgetsCache6 } from "@elementor/editor-elements";
4653
+ import { getContainer as getContainer4, getWidgetsCache as getWidgetsCache6 } from "@elementor/editor-elements";
4541
4654
  import { dispatchMcpStylesAppliedEvent } from "@elementor/editor-mcp";
4542
4655
  import { Schema as Schema2 } from "@elementor/editor-props";
4543
4656
 
@@ -4569,9 +4682,9 @@ var readStoredCustomCssText = (raw) => {
4569
4682
 
4570
4683
  // src/mcp/utils/resolve-canonical-prop-name.ts
4571
4684
  import { getWidgetsCache as getWidgetsCache4 } from "@elementor/editor-elements";
4572
- function buildAliasToCanonicalMap(schema2) {
4685
+ function buildAliasToCanonicalMap(schema) {
4573
4686
  const aliasToCanonical = {};
4574
- for (const [canonical, propType] of Object.entries(schema2)) {
4687
+ for (const [canonical, propType] of Object.entries(schema)) {
4575
4688
  const aliases = propType.meta?.aliases;
4576
4689
  if (!Array.isArray(aliases)) {
4577
4690
  continue;
@@ -4585,26 +4698,26 @@ function buildAliasToCanonicalMap(schema2) {
4585
4698
  return aliasToCanonical;
4586
4699
  }
4587
4700
  function resolveCanonicalPropName(elementType, propertyName) {
4588
- const schema2 = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
4589
- if (!schema2 || schema2[propertyName]) {
4701
+ const schema = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
4702
+ if (!schema || schema[propertyName]) {
4590
4703
  return propertyName;
4591
4704
  }
4592
- return buildAliasToCanonicalMap(schema2)[propertyName] ?? propertyName;
4705
+ return buildAliasToCanonicalMap(schema)[propertyName] ?? propertyName;
4593
4706
  }
4594
4707
  function resolveCanonicalPropKeys(elementType, props) {
4595
- const schema2 = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
4596
- if (!schema2) {
4708
+ const schema = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
4709
+ if (!schema) {
4597
4710
  return { ...props };
4598
4711
  }
4599
- const aliasToCanonical = buildAliasToCanonicalMap(schema2);
4712
+ const aliasToCanonical = buildAliasToCanonicalMap(schema);
4600
4713
  const resolved = {};
4601
4714
  for (const [key, value] of Object.entries(props)) {
4602
- if (schema2[key]) {
4715
+ if (schema[key]) {
4603
4716
  resolved[key] = value;
4604
4717
  }
4605
4718
  }
4606
4719
  for (const [key, value] of Object.entries(props)) {
4607
- if (schema2[key]) {
4720
+ if (schema[key]) {
4608
4721
  continue;
4609
4722
  }
4610
4723
  const canonical = aliasToCanonical[key];
@@ -4652,9 +4765,9 @@ var dynamicTagLLMResolver = (value) => {
4652
4765
  }
4653
4766
  };
4654
4767
  };
4655
- var buildStrictSettings = (schema2, provided) => {
4768
+ var buildStrictSettings = (schema, provided) => {
4656
4769
  const settings = {};
4657
- for (const [key, propType] of Object.entries(schema2)) {
4770
+ for (const [key, propType] of Object.entries(schema)) {
4658
4771
  if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
4659
4772
  continue;
4660
4773
  }
@@ -4686,6 +4799,20 @@ var LOCAL_STYLE_META = {
4686
4799
  breakpoint: "desktop",
4687
4800
  state: null
4688
4801
  };
4802
+ var UnsupportedPropertyError = class extends Error {
4803
+ elementType;
4804
+ propertyName;
4805
+ constructor(elementType, propertyName, availableProperties) {
4806
+ super(
4807
+ `Property "${propertyName}" does not exist on element type "${elementType}". Available properties are: ${availableProperties.join(
4808
+ ", "
4809
+ )}`
4810
+ );
4811
+ this.name = "UnsupportedPropertyError";
4812
+ this.elementType = elementType;
4813
+ this.propertyName = propertyName;
4814
+ }
4815
+ };
4689
4816
  function resolvePropValue(value, forceKey) {
4690
4817
  const Utils = window.elementorV2.editorVariables.Utils;
4691
4818
  return Schema.adjustLlmPropValueSchema(value, {
@@ -4791,12 +4918,7 @@ var doUpdateElementProperty = (params) => {
4791
4918
  throw new Error(`No prop schema found for element type: ${elementType}`);
4792
4919
  }
4793
4920
  if (!elementPropSchema[propertyName]) {
4794
- const propertyNames = Object.keys(elementPropSchema);
4795
- throw new Error(
4796
- `Property "${propertyName}" does not exist on element type "${elementType}". Available properties are: ${propertyNames.join(
4797
- ", "
4798
- )}`
4799
- );
4921
+ throw new UnsupportedPropertyError(elementType, propertyName, Object.keys(elementPropSchema));
4800
4922
  }
4801
4923
  const propKey = elementPropSchema[propertyName].key;
4802
4924
  const value = resolvePropValue(propertyValue, propKey);
@@ -4866,8 +4988,6 @@ For all non-primitive entries in \`propertiesToChange\`, provide the schema \`ke
4866
4988
 
4867
4989
  Use the EXACT PropType schema given, and ALWAYS include the \`key\` from the schema for every property you are changing in \`propertiesToChange\`.
4868
4990
 
4869
- Check \`llm_guidance.default_settings\` in the widget schema \u2014 include a key in \`propertiesToChange\` only when the user explicitly asks to change it.
4870
-
4871
4991
  # Dynamic tags
4872
4992
  A value can be made dynamic wherever its schema exposes a variant with "$$type": "dynamic". This may be the property root OR a NESTED field: for example an image is made dynamic on its "src" (the root stays "image"), NOT on the whole "image" value.
4873
4993
  Put the dynamic object EXACTLY at the node whose schema offers the "dynamic" variant, in place of the static variant. The variant's "name" enumerates the tags allowed at that node.
@@ -4894,7 +5014,7 @@ Do NOT send "group" (it is resolved automatically). Use { "settings": {} } only
4894
5014
  );
4895
5015
  configureElementToolPrompt.parameter(
4896
5016
  "style",
4897
- 'A flat map of raw CSS declarations (property \u2192 value), e.g. { "line-height": "1.25rem", "color": "var(--primary-text, #000)" }. Set a value to null to reset that property to its default. OPTIONAL.'
5017
+ 'A flat map of raw CSS declarations (property \u2192 value), e.g. { "line-height": "1.25rem", "color": "var(--primary-text, #000)" }. font-family must be a single Google Font name or a var(--label) \u2014 no fallback stacks. Set a value to null to reset that property to its default. OPTIONAL.'
4898
5018
  );
4899
5019
  configureElementToolPrompt.example(`
4900
5020
  \`\`\`json
@@ -4926,36 +5046,39 @@ Do NOT send "group" (it is resolved automatically). Use { "settings": {} } only
4926
5046
  V4 only: If MCP fails, give manual steps using V4 UI.
4927
5047
 
4928
5048
  V4 Editor structure:
4929
- Panel tabs: General (\u2192 Settings section: ID, Tag, Link), Style, Interactions.
5049
+ Panel tabs: General (\u2192 Settings section: ID, Tag, and Link where the widget supports it), Style, Interactions.
4930
5050
  NO Advanced tab. Never mention Advanced tab.
5051
+ Note: \`link\` is valid only when the element's PropType schema (which you must already have) includes a \`link\` property. Sending \`link\` to a widget whose schema lacks it is skipped and reported in the response \`warnings\` (other changes still apply) and the link is lost.
4931
5052
  `);
4932
5053
  return configureElementToolPrompt.prompt();
4933
5054
  };
4934
- var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt();
4935
5055
 
4936
5056
  // src/mcp/tools/configure-element/schema.ts
4937
- import { z as z2 } from "@elementor/schema";
5057
+ import { z } from "@elementor/schema";
4938
5058
  var inputSchema = {
4939
- propertiesToChange: z2.record(
4940
- z2.string().describe("The property name."),
4941
- z2.any().describe(`PropValue, refer to [${WIDGET_SCHEMA_URI}] by correct type, as appears in elementType`),
4942
- z2.any()
5059
+ propertiesToChange: z.record(
5060
+ z.string().describe("The property name."),
5061
+ z.any().describe(`PropValue, refer to [${WIDGET_SCHEMA_URI}] by correct type, as appears in elementType`),
5062
+ z.any()
4943
5063
  ).describe("An object record containing property names and their new values to be set on the element"),
4944
- style: z2.record(
4945
- z2.string().describe('A CSS property name, e.g. "color", "margin-top".'),
4946
- z2.string().nullable().describe(
5064
+ style: z.record(
5065
+ z.string().describe('A CSS property name, e.g. "color", "margin-top".'),
5066
+ z.string().nullable().describe(
4947
5067
  'A CSS value, e.g. "red", "10px", "1px solid #000". Use null to reset the property to its default.'
4948
5068
  )
4949
5069
  ).describe(
4950
5070
  "Raw CSS declarations as a flat property\u2192value map. Converted to native styles server-side; any declaration that cannot be converted is stored as the element custom CSS. A null value resets that property to its default."
4951
5071
  ).default({}),
4952
- elementType: z2.string().describe("The type of the element to retrieve the schema"),
4953
- elementId: z2.string().describe("The unique id of the element to configure")
5072
+ elementType: z.string().describe("The type of the element to retrieve the schema"),
5073
+ elementId: z.string().describe("The unique id of the element to configure")
4954
5074
  };
4955
5075
  var outputSchema = {
4956
- success: z2.boolean().describe(
5076
+ success: z.boolean().describe(
4957
5077
  "Whether the configuration change was successful, only if propertyName and propertyValue are provided"
4958
- )
5078
+ ),
5079
+ warnings: z.string().describe(
5080
+ 'Non-fatal notices. Present when some props were skipped because they are not in the element schema (e.g. a "link" on a widget with no link prop). Other changes were still applied.'
5081
+ ).optional()
4959
5082
  };
4960
5083
 
4961
5084
  // src/mcp/tools/configure-element/tool.ts
@@ -4990,7 +5113,7 @@ var initConfigureElementTool = (reg) => {
4990
5113
  `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
4991
5114
  );
4992
5115
  }
4993
- const container = getContainer5(elementId);
5116
+ const container = getContainer4(elementId);
4994
5117
  if (!container) {
4995
5118
  throw new Error(`Element with id ${elementId} not found`);
4996
5119
  }
@@ -5005,6 +5128,7 @@ var initConfigureElementTool = (reg) => {
5005
5128
  }
5006
5129
  const propertiesToUpdate = resolveCanonicalPropKeys(elementType, propertiesToChange);
5007
5130
  const toUpdate = Object.entries(propertiesToUpdate);
5131
+ const skippedProps = [];
5008
5132
  for (const [propertyName, propertyValue] of toUpdate) {
5009
5133
  if (!Schema2.isPropKeyConfigurable(propertyName)) {
5010
5134
  throw new Error(`Not allowed to update ${propertyName}`);
@@ -5017,6 +5141,10 @@ var initConfigureElementTool = (reg) => {
5017
5141
  propertyValue
5018
5142
  });
5019
5143
  } catch (error) {
5144
+ if (error instanceof UnsupportedPropertyError) {
5145
+ skippedProps.push(error.propertyName);
5146
+ continue;
5147
+ }
5020
5148
  const errorMessage = createUpdateErrorMessage({
5021
5149
  propertyName,
5022
5150
  elementId,
@@ -5029,7 +5157,10 @@ var initConfigureElementTool = (reg) => {
5029
5157
  }
5030
5158
  await applyStyleFromCss({ elementId, elementType, style });
5031
5159
  return {
5032
- success: true
5160
+ success: true,
5161
+ warnings: skippedProps.length ? `Skipped unsupported props (not in the "${elementType}" schema; other changes were applied): ${skippedProps.join(
5162
+ ", "
5163
+ )}.` : void 0
5033
5164
  };
5034
5165
  }
5035
5166
  });
@@ -5081,143 +5212,74 @@ Provide styling as raw CSS via the "style" parameter (a flat map of CSS property
5081
5212
  }`;
5082
5213
  }
5083
5214
 
5084
- // src/mcp/tools/create-element/tool.ts
5085
- import { getCurrentDocument as getCurrentDocument2 } from "@elementor/editor-documents";
5086
- import { httpService as httpService7 } from "@elementor/http-client";
5087
- import { z as z3 } from "@elementor/schema";
5088
- var MCP_PROXY_URL6 = "elementor/v1/mcp-proxy";
5089
- var initCreateElementTool = (reg) => {
5215
+ // src/mcp/tools/get-page-structure/tool.ts
5216
+ import { getCurrentDocument } from "@elementor/editor-documents";
5217
+ import { httpService as httpService6 } from "@elementor/http-client";
5218
+ import { z as z2 } from "@elementor/schema";
5219
+
5220
+ // src/mcp/utils/get-mcp-error-message.ts
5221
+ import { AxiosError } from "@elementor/http-client";
5222
+ function getMcpErrorMessage(error, toolName) {
5223
+ if (error instanceof AxiosError) {
5224
+ const data = error.response?.data;
5225
+ if (data?.message) {
5226
+ return data.code ? `${data.code}: ${data.message}` : data.message;
5227
+ }
5228
+ }
5229
+ if (error instanceof Error) {
5230
+ return error.message;
5231
+ }
5232
+ return `${toolName} failed with an unknown error.`;
5233
+ }
5234
+
5235
+ // src/mcp/tools/get-page-structure/tool.ts
5236
+ var MCP_PROXY_URL5 = "elementor/v1/mcp-proxy";
5237
+ var initGetPageStructureTool = (reg) => {
5090
5238
  const { addTool } = reg;
5091
5239
  addTool({
5092
- name: "create-element",
5093
- description: "Insert a new element into the current Elementor document via the server-side MCP ability. The document is saved as a draft. Reload the editor after calling this tool to see the result.",
5240
+ name: "get-page-structure",
5241
+ description: "Returns a lean Elementor element tree skeleton (id, elType, widgetType, title, nested elements) for a post or page. If no postId is provided, uses the currently open document. Optionally scope to a subtree with elementId. Set includeContent=true (requires elementId) to also return each node's settings and styles.",
5094
5242
  schema: {
5095
- elementType: z3.string().describe("Registry identifier of the element to create, e.g. 'e-heading', 'e-flexbox'."),
5096
- parentId: z3.string().optional().describe("ID of the parent container. Omit or pass 'document' to insert at the document root.")
5243
+ postId: z2.number().optional().describe(
5244
+ "WordPress post ID of the Elementor document. If omitted, uses the currently open document."
5245
+ ),
5246
+ elementId: z2.string().optional().describe("If provided, returns only the subtree rooted at that element id."),
5247
+ includeContent: z2.boolean().optional().describe(
5248
+ "If true, includes each node's settings and styles (same shape build-composition accepts as input). Requires elementId."
5249
+ )
5097
5250
  },
5098
5251
  outputSchema: {
5099
- elementId: z3.string(),
5100
- previewUrl: z3.string(),
5101
- version: z3.string()
5252
+ elements: z2.array(z2.any()).describe(
5253
+ "Skeleton of Elementor elements (id, elType, widgetType, title, nested elements). When includeContent is true, each node also includes settings and styles."
5254
+ )
5102
5255
  },
5103
- handler: async ({ elementType, parentId }) => {
5104
- const document2 = getCurrentDocument2();
5105
- if (!document2?.id) {
5106
- throw new Error("No active document found.");
5107
- }
5108
- const { data } = await httpService7().post(MCP_PROXY_URL6, {
5109
- tool: "create-element",
5110
- input: {
5111
- parent_id: parentId ?? "document",
5112
- element: { type: elementType },
5113
- post_id: document2.id
5114
- }
5115
- });
5116
- return {
5117
- elementId: data.data.element_id,
5118
- previewUrl: data.data.preview_url,
5119
- version: data.data.version
5120
- };
5121
- }
5122
- });
5123
- };
5124
-
5125
- // src/mcp/tools/get-element-config/tool.ts
5126
- import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
5127
- import { Schema as Schema3 } from "@elementor/editor-props";
5128
- import { z as z4 } from "@elementor/schema";
5129
- var schema = {
5130
- elementId: z4.string()
5131
- };
5132
- var outputSchema2 = {
5133
- properties: z4.record(z4.string(), z4.any()).describe("A record mapping PropTypes to their corresponding PropValues"),
5134
- style: z4.record(z4.string(), z4.any()).describe("A record mapping StyleSchema properties to their corresponding PropValues"),
5135
- childElements: z4.array(
5136
- z4.object({
5137
- id: z4.string(),
5138
- elementType: z4.string(),
5139
- childElements: z4.array(z4.any()).describe("An array of child element IDs, when applicable, same structure recursively")
5140
- })
5141
- ).describe("An array of child element IDs, when applicable, with recursive structure")
5142
- };
5143
- var structuredElements = (element) => {
5144
- const children = element.children || [];
5145
- return children.map((child) => {
5146
- return {
5147
- id: child.id,
5148
- elementType: child.model.get("elType") || child.model.get("widgetType") || "unknown",
5149
- childElements: structuredElements(child)
5150
- };
5151
- });
5152
- };
5153
- var initGetElementConfigTool = (reg) => {
5154
- const { addTool } = reg;
5155
- addTool({
5156
- name: "get-element-configuration-values",
5157
- description: "Retrieve the element's configuration PropValues for a specific element by unique ID.",
5158
- schema,
5159
- outputSchema: outputSchema2,
5160
- handler: async ({ elementId }) => {
5161
- const element = getContainer6(elementId);
5162
- if (!element) {
5163
- throw new Error(`Element with ID ${elementId} not found.`);
5164
- }
5165
- const elementType = element.model.get("widgetType") || element.model.get("elType") || "";
5166
- const widgetData = getWidgetsCache7()?.[elementType];
5167
- if (!widgetData) {
5168
- throw new Error(
5169
- `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
5170
- );
5256
+ handler: async ({ postId, elementId, includeContent }) => {
5257
+ const resolvedPostId = postId ?? getCurrentDocument()?.id;
5258
+ if (!resolvedPostId) {
5259
+ throw new Error("No post ID provided and no active document found.");
5171
5260
  }
5172
- if (!widgetData.atomic_props_schema) {
5173
- throw new Error(
5174
- `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${elementType}`
5175
- );
5176
- }
5177
- const elementRawSettings = element.settings;
5178
- const propSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
5179
- if (!elementRawSettings || !propSchema) {
5180
- throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
5181
- }
5182
- const propValues = {};
5183
- const stylePropValues = {};
5184
- Schema3.configurableKeys(propSchema).forEach((key) => {
5185
- propValues[key] = structuredClone(elementRawSettings.get(key));
5186
- });
5187
- const elementStyles = getElementStyles2(elementId) || {};
5188
- const localStyle = Object.values(elementStyles).find((style) => style.label === "local");
5189
- if (localStyle) {
5190
- const defaultVariant = localStyle.variants.find(
5191
- (variant) => variant.meta.breakpoint === "desktop" && !variant.meta.state
5192
- );
5193
- if (defaultVariant) {
5194
- const styleProps = defaultVariant.props || {};
5195
- Object.keys(styleProps).forEach((stylePropName) => {
5196
- if (typeof styleProps[stylePropName] !== "undefined") {
5197
- stylePropValues[stylePropName] = structuredClone(styleProps[stylePropName]);
5198
- }
5199
- });
5200
- if (defaultVariant.custom_css) {
5201
- stylePropValues.custom_css = atob(defaultVariant.custom_css.raw);
5261
+ try {
5262
+ const { data } = await httpService6().post(MCP_PROXY_URL5, {
5263
+ tool: "get-page-structure",
5264
+ input: {
5265
+ post_id: resolvedPostId,
5266
+ ...elementId ? { element_id: elementId } : {},
5267
+ ...includeContent ? { include_content: true } : {}
5202
5268
  }
5203
- }
5269
+ });
5270
+ return {
5271
+ elements: data.data.elements
5272
+ };
5273
+ } catch (error) {
5274
+ throw new Error(getMcpErrorMessage(error, "get-page-structure"));
5204
5275
  }
5205
- return {
5206
- properties: {
5207
- ...propValues
5208
- },
5209
- style: {
5210
- ...stylePropValues
5211
- },
5212
- childElements: structuredElements(element)
5213
- };
5214
5276
  }
5215
5277
  });
5216
5278
  };
5217
5279
 
5218
5280
  // src/mcp/canvas-mcp.ts
5219
5281
  var initCanvasMcp = (reg) => {
5220
- Schema4.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5282
+ Schema3.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5221
5283
  initWidgetsSchemaResource(reg);
5222
5284
  initAvailableWidgetsResource(reg);
5223
5285
  initDocumentStructureResource(reg);
@@ -5226,17 +5288,15 @@ var initCanvasMcp = (reg) => {
5226
5288
  initEditorStateResource(reg);
5227
5289
  initGeneralContextResource(reg);
5228
5290
  initBestPracticesResource(reg);
5229
- initGetElementConfigTool(reg);
5230
5291
  initConfigureElementTool(reg);
5231
- initCreateElementTool(reg);
5232
- initBuildCompositionTool(reg);
5292
+ initGetPageStructureTool(reg);
5233
5293
  initBreakpointsResource(reg);
5234
5294
  };
5235
5295
 
5236
5296
  // src/mcp/mcp-description.ts
5237
5297
  var ELEMENT_SCHEMA_URI = WIDGET_SCHEMA_URI.replace("{widgetType}", "element-schema");
5238
5298
  var mcpDescription = `Elementor Canvas MCP
5239
- This MCP enables creation, configuration, and styling of elements on the Elementor canvas using the build_composition tool.
5299
+ This MCP enables configuration and styling of existing V4 elements on the Elementor canvas using the configure-element tool.
5240
5300
 
5241
5301
  # Core Concepts
5242
5302
 
@@ -5255,66 +5315,54 @@ The \`$$type\` defines how Elementor interprets the value. Providing the correct
5255
5315
  - **Global Classes**: Reusable style sets that can be applied to elements (\`elementor://global-classes\`)
5256
5316
  - **Widget Schemas**: Configuration options for each widget type (\`${WIDGET_SCHEMA_URI}\`)
5257
5317
 
5258
- # Building Compositions with build_composition
5318
+ # Configuring Elements with configure-element
5259
5319
 
5260
- The \`build_composition\` tool is the primary way to create elements. It accepts structure (XML), configuration, and styling in a single operation.
5320
+ The \`configure-element\` tool updates settings and styles on existing V4 elements. Read the configure-element guide resource before use.
5261
5321
 
5262
5322
  ## Complete Workflow
5263
5323
 
5264
5324
  ### 1. Parse User Requirements
5265
- Understand what needs to be built: structure, content, and styling.
5325
+ Understand what needs to change: content, settings, or styling on existing elements.
5266
5326
 
5267
5327
  ### 2. Check Global Resources FIRST
5268
- Always check existing resources before building:
5328
+ Always check existing resources before styling:
5269
5329
  - List \`elementor://global-variables\` for available variables (colors, sizes, fonts)
5270
5330
  - List \`elementor://global-classes\` for available style sets
5271
5331
  - **Always prefer using existing global resources over creating inline styles**
5272
5332
 
5273
5333
  ### 3. Retrieve Widget Schemas
5274
- For each widget you'll use:
5334
+ For each element you will configure:
5275
5335
  - List \`${WIDGET_SCHEMA_URI}\` to see available widgets
5276
5336
  - Retrieve configuration schema from \`${ELEMENT_SCHEMA_URI}\` for each widget
5277
- - Check the \`llm_guidance\` property for container nesting, \`default_styles\`, and \`default_settings\` (omit default_settings from elementConfig unless the user asks to change them)
5337
+ - Check the \`llm_guidance\` property for container nesting, \`default_styles\`, and \`default_settings\`
5278
5338
 
5279
- ### 4. Build XML Structure
5280
- Create valid XML with configuration-ids:
5281
- - Each element must have a unique \`configuration-id\` attribute
5282
- - No text nodes, classes, or IDs in XML - structure only
5283
- - Example:
5284
- \`\`\`xml
5285
- <e-container configuration-id="container-1">
5286
- <e-heading configuration-id="heading-1" />
5287
- <e-text configuration-id="text-1" />
5288
- </e-container>
5289
- \`\`\`
5339
+ ### 4. Get Current Element State
5340
+ Use page structure and element configuration resources to find element IDs and current values.
5290
5341
 
5291
- ### 5. Create elementConfig
5292
- Map each configuration-id to its widget properties using PropValues:
5342
+ ### 5. Create propertiesToChange
5343
+ Map property names to PropValues using the widget schema:
5293
5344
  - Use correct \`$$type\` matching the widget's schema
5294
5345
  - Use global variables in PropValues where applicable
5295
5346
  - Example:
5296
5347
  \`\`\`json
5297
5348
  {
5298
- "heading-1": {
5299
- "text": { "$$type": "string", "value": "Welcome" },
5300
- "tag": { "$$type": "string", "value": "h1" }
5301
- }
5349
+ "text": { "$$type": "string", "value": "Welcome" },
5350
+ "tag": { "$$type": "string", "value": "h1" }
5302
5351
  }
5303
5352
  \`\`\`
5304
5353
 
5305
5354
  ### 6. Create style
5306
- Map each configuration-id to raw CSS declarations (property \u2192 value strings). The server converts them to native styles and stores any unconvertible declarations as the element custom CSS.
5355
+ Provide raw CSS declarations (property \u2192 value strings). The server converts them to native styles and stores any unconvertible declarations as the element custom CSS.
5307
5356
  - Example:
5308
5357
  \`\`\`json
5309
5358
  {
5310
- "heading-1": "color: #1a1a1a; font-size: 2rem;"
5311
- }
5359
+ "color": "#1a1a1a",
5360
+ "font-size": "2rem"
5312
5361
  }
5313
5362
  \`\`\`
5314
5363
 
5315
- ### 7. Execute build_composition
5316
- Call the tool with your XML structure, elementConfig, and style. The response will contain the created element IDs.
5317
- At the response you will also find llm_instructions for you to do afterwards, read and follow them!
5364
+ ### 7. Execute configure-element
5365
+ Call the tool with elementId, elementType, propertiesToChange, and style as needed.
5318
5366
 
5319
5367
  ## Key Points
5320
5368
 
@@ -5429,7 +5477,7 @@ function shouldBlock(sourceElements, targetElements) {
5429
5477
  }
5430
5478
 
5431
5479
  // src/style-commands/paste-style.ts
5432
- import { getContainer as getContainer7, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
5480
+ import { getContainer as getContainer5, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
5433
5481
  import { classesPropTypeUtil } from "@elementor/editor-props";
5434
5482
  import {
5435
5483
  __privateListenTo as listenTo5,
@@ -5438,7 +5486,7 @@ import {
5438
5486
  } from "@elementor/editor-v1-adapters";
5439
5487
 
5440
5488
  // src/utils/command-utils.ts
5441
- import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
5489
+ import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
5442
5490
  import { CLASSES_PROP_KEY } from "@elementor/editor-props";
5443
5491
  import { __ as __5 } from "@wordpress/i18n";
5444
5492
  function hasAtomicWidgets(args) {
@@ -5463,7 +5511,7 @@ function getClassesProp(container) {
5463
5511
  }
5464
5512
  function getContainerSchema(container) {
5465
5513
  const type = container?.model.get("widgetType") || container?.model.get("elType");
5466
- const widgetsCache = getWidgetsCache8();
5514
+ const widgetsCache = getWidgetsCache7();
5467
5515
  const elementType = widgetsCache?.[type];
5468
5516
  return elementType?.atomic_props_schema ?? null;
5469
5517
  }
@@ -5483,7 +5531,7 @@ function getTitleForContainers(containers) {
5483
5531
  import {
5484
5532
  createElementStyle as createElementStyle2,
5485
5533
  deleteElementStyle,
5486
- getElementStyles as getElementStyles3,
5534
+ getElementStyles as getElementStyles2,
5487
5535
  updateElementStyle as updateElementStyle2
5488
5536
  } from "@elementor/editor-elements";
5489
5537
  import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
@@ -5498,7 +5546,7 @@ var undoablePasteElementStyle = () => undoable2(
5498
5546
  if (!classesProp) {
5499
5547
  return null;
5500
5548
  }
5501
- const originalStyles = getElementStyles3(container.id);
5549
+ const originalStyles = getElementStyles2(container.id);
5502
5550
  const [styleId, styleDef] = Object.entries(originalStyles ?? {})[0] ?? [];
5503
5551
  const originalStyle = Object.keys(styleDef ?? {}).length ? styleDef : null;
5504
5552
  const revertData = {
@@ -5582,7 +5630,7 @@ function pasteStyles(args, pasteLocalStyle) {
5582
5630
  }
5583
5631
  const clipboardElements = getClipboardElements(storageKey);
5584
5632
  const [clipboardElement] = clipboardElements ?? [];
5585
- const clipboardContainer = getContainer7(clipboardElement.id);
5633
+ const clipboardContainer = getContainer5(clipboardElement.id);
5586
5634
  if (!clipboardElement || !clipboardContainer || !isAtomicWidget(clipboardContainer)) {
5587
5635
  return;
5588
5636
  }
@@ -5628,7 +5676,7 @@ import {
5628
5676
  } from "@elementor/editor-v1-adapters";
5629
5677
 
5630
5678
  // src/style-commands/undoable-actions/reset-element-style.ts
5631
- import { createElementStyle as createElementStyle3, deleteElementStyle as deleteElementStyle2, getElementStyles as getElementStyles4 } from "@elementor/editor-elements";
5679
+ import { createElementStyle as createElementStyle3, deleteElementStyle as deleteElementStyle2, getElementStyles as getElementStyles3 } from "@elementor/editor-elements";
5632
5680
  import { ELEMENTS_STYLES_RESERVED_LABEL as ELEMENTS_STYLES_RESERVED_LABEL2 } from "@elementor/editor-styles-repository";
5633
5681
  import { undoable as undoable3 } from "@elementor/editor-v1-adapters";
5634
5682
  import { __ as __7 } from "@wordpress/i18n";
@@ -5637,7 +5685,7 @@ var undoableResetElementStyle = () => undoable3(
5637
5685
  do: ({ containers }) => {
5638
5686
  return containers.map((container) => {
5639
5687
  const elementId = container.model.get("id");
5640
- const containerStyles = getElementStyles4(elementId);
5688
+ const containerStyles = getElementStyles3(elementId);
5641
5689
  Object.keys(containerStyles ?? {}).forEach(
5642
5690
  (styleId) => deleteElementStyle2(elementId, styleId)
5643
5691
  );
@@ -5729,15 +5777,15 @@ function init() {
5729
5777
  initCanvasMcp(
5730
5778
  getMCPByDomain("canvas", {
5731
5779
  instructions: `Everything related to V4 ( Atomic ) canvas.
5732
- # Canvas workflow for new compositions
5733
- - Configure elements settings and styles
5734
- - Build compositions/sections out of V4 atomic elements using context aware designs using the website resources
5735
- - Get and retrieve element configuration values
5780
+ # Canvas workflow
5781
+ - Configure element settings and styles with configure-element
5782
+ - Get page structure and element configuration values
5736
5783
  `,
5737
5784
  docs: mcpDescription
5738
5785
  })
5739
5786
  );
5740
5787
  initTabsModelExtensions();
5788
+ initListType();
5741
5789
  }
5742
5790
 
5743
5791
  // src/sync/drag-element-from-panel.ts
@@ -5859,10 +5907,10 @@ function useEscapeOnCanvas(canvasDocument, onEscape) {
5859
5907
  }
5860
5908
 
5861
5909
  // src/utils/after-render.ts
5862
- import { getContainer as getContainer8 } from "@elementor/editor-elements";
5910
+ import { getContainer as getContainer6 } from "@elementor/editor-elements";
5863
5911
  function doAfterRender(elementIds, callback) {
5864
5912
  const pending = elementIds.map((elementId) => {
5865
- const view = getContainer8(elementId)?.view;
5913
+ const view = getContainer6(elementId)?.view;
5866
5914
  if (!view || !hasDoAfterRender(view)) {
5867
5915
  return void 0;
5868
5916
  }