@elementor/editor-canvas 4.3.0-999 → 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 (49) hide show
  1. package/dist/index.d.mts +3 -1
  2. package/dist/index.d.ts +3 -1
  3. package/dist/index.js +457 -371
  4. package/dist/index.mjs +438 -348
  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 -4
  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/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 z4 = getVal2(value.z);
2422
- if (x === DEFAULT_XY && y === DEFAULT_XY && z4 === 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} ${z4}`;
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,102 +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/get-element-config/tool.ts
5085
- import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
5086
- import { Schema as Schema3 } from "@elementor/editor-props";
5087
- import { z as z3 } from "@elementor/schema";
5088
- var schema = {
5089
- elementId: z3.string()
5090
- };
5091
- var outputSchema2 = {
5092
- properties: z3.record(z3.string(), z3.any()).describe("A record mapping PropTypes to their corresponding PropValues"),
5093
- style: z3.record(z3.string(), z3.any()).describe("A record mapping StyleSchema properties to their corresponding PropValues"),
5094
- childElements: z3.array(
5095
- z3.object({
5096
- id: z3.string(),
5097
- elementType: z3.string(),
5098
- childElements: z3.array(z3.any()).describe("An array of child element IDs, when applicable, same structure recursively")
5099
- })
5100
- ).describe("An array of child element IDs, when applicable, with recursive structure")
5101
- };
5102
- var structuredElements = (element) => {
5103
- const children = element.children || [];
5104
- return children.map((child) => {
5105
- return {
5106
- id: child.id,
5107
- elementType: child.model.get("elType") || child.model.get("widgetType") || "unknown",
5108
- childElements: structuredElements(child)
5109
- };
5110
- });
5111
- };
5112
- var initGetElementConfigTool = (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) => {
5113
5238
  const { addTool } = reg;
5114
5239
  addTool({
5115
- name: "get-element-configuration-values",
5116
- description: "Retrieve the element's configuration PropValues for a specific element by unique ID.",
5117
- schema,
5118
- outputSchema: outputSchema2,
5119
- handler: async ({ elementId }) => {
5120
- const element = getContainer6(elementId);
5121
- if (!element) {
5122
- throw new Error(`Element with ID ${elementId} not found.`);
5123
- }
5124
- const elementType = element.model.get("widgetType") || element.model.get("elType") || "";
5125
- const widgetData = getWidgetsCache7()?.[elementType];
5126
- if (!widgetData) {
5127
- throw new Error(
5128
- `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
5129
- );
5130
- }
5131
- if (!widgetData.atomic_props_schema) {
5132
- throw new Error(
5133
- `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${elementType}`
5134
- );
5135
- }
5136
- const elementRawSettings = element.settings;
5137
- const propSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
5138
- if (!elementRawSettings || !propSchema) {
5139
- throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
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.",
5242
+ schema: {
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
+ )
5250
+ },
5251
+ outputSchema: {
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
+ )
5255
+ },
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.");
5140
5260
  }
5141
- const propValues = {};
5142
- const stylePropValues = {};
5143
- Schema3.configurableKeys(propSchema).forEach((key) => {
5144
- propValues[key] = structuredClone(elementRawSettings.get(key));
5145
- });
5146
- const elementStyles = getElementStyles2(elementId) || {};
5147
- const localStyle = Object.values(elementStyles).find((style) => style.label === "local");
5148
- if (localStyle) {
5149
- const defaultVariant = localStyle.variants.find(
5150
- (variant) => variant.meta.breakpoint === "desktop" && !variant.meta.state
5151
- );
5152
- if (defaultVariant) {
5153
- const styleProps = defaultVariant.props || {};
5154
- Object.keys(styleProps).forEach((stylePropName) => {
5155
- if (typeof styleProps[stylePropName] !== "undefined") {
5156
- stylePropValues[stylePropName] = structuredClone(styleProps[stylePropName]);
5157
- }
5158
- });
5159
- if (defaultVariant.custom_css) {
5160
- 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 } : {}
5161
5268
  }
5162
- }
5269
+ });
5270
+ return {
5271
+ elements: data.data.elements
5272
+ };
5273
+ } catch (error) {
5274
+ throw new Error(getMcpErrorMessage(error, "get-page-structure"));
5163
5275
  }
5164
- return {
5165
- properties: {
5166
- ...propValues
5167
- },
5168
- style: {
5169
- ...stylePropValues
5170
- },
5171
- childElements: structuredElements(element)
5172
- };
5173
5276
  }
5174
5277
  });
5175
5278
  };
5176
5279
 
5177
5280
  // src/mcp/canvas-mcp.ts
5178
5281
  var initCanvasMcp = (reg) => {
5179
- Schema4.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5282
+ Schema3.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5180
5283
  initWidgetsSchemaResource(reg);
5181
5284
  initAvailableWidgetsResource(reg);
5182
5285
  initDocumentStructureResource(reg);
@@ -5185,16 +5288,15 @@ var initCanvasMcp = (reg) => {
5185
5288
  initEditorStateResource(reg);
5186
5289
  initGeneralContextResource(reg);
5187
5290
  initBestPracticesResource(reg);
5188
- initGetElementConfigTool(reg);
5189
5291
  initConfigureElementTool(reg);
5190
- initBuildCompositionTool(reg);
5292
+ initGetPageStructureTool(reg);
5191
5293
  initBreakpointsResource(reg);
5192
5294
  };
5193
5295
 
5194
5296
  // src/mcp/mcp-description.ts
5195
5297
  var ELEMENT_SCHEMA_URI = WIDGET_SCHEMA_URI.replace("{widgetType}", "element-schema");
5196
5298
  var mcpDescription = `Elementor Canvas MCP
5197
- 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.
5198
5300
 
5199
5301
  # Core Concepts
5200
5302
 
@@ -5213,66 +5315,54 @@ The \`$$type\` defines how Elementor interprets the value. Providing the correct
5213
5315
  - **Global Classes**: Reusable style sets that can be applied to elements (\`elementor://global-classes\`)
5214
5316
  - **Widget Schemas**: Configuration options for each widget type (\`${WIDGET_SCHEMA_URI}\`)
5215
5317
 
5216
- # Building Compositions with build_composition
5318
+ # Configuring Elements with configure-element
5217
5319
 
5218
- 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.
5219
5321
 
5220
5322
  ## Complete Workflow
5221
5323
 
5222
5324
  ### 1. Parse User Requirements
5223
- Understand what needs to be built: structure, content, and styling.
5325
+ Understand what needs to change: content, settings, or styling on existing elements.
5224
5326
 
5225
5327
  ### 2. Check Global Resources FIRST
5226
- Always check existing resources before building:
5328
+ Always check existing resources before styling:
5227
5329
  - List \`elementor://global-variables\` for available variables (colors, sizes, fonts)
5228
5330
  - List \`elementor://global-classes\` for available style sets
5229
5331
  - **Always prefer using existing global resources over creating inline styles**
5230
5332
 
5231
5333
  ### 3. Retrieve Widget Schemas
5232
- For each widget you'll use:
5334
+ For each element you will configure:
5233
5335
  - List \`${WIDGET_SCHEMA_URI}\` to see available widgets
5234
5336
  - Retrieve configuration schema from \`${ELEMENT_SCHEMA_URI}\` for each widget
5235
- - 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\`
5236
5338
 
5237
- ### 4. Build XML Structure
5238
- Create valid XML with configuration-ids:
5239
- - Each element must have a unique \`configuration-id\` attribute
5240
- - No text nodes, classes, or IDs in XML - structure only
5241
- - Example:
5242
- \`\`\`xml
5243
- <e-container configuration-id="container-1">
5244
- <e-heading configuration-id="heading-1" />
5245
- <e-text configuration-id="text-1" />
5246
- </e-container>
5247
- \`\`\`
5339
+ ### 4. Get Current Element State
5340
+ Use page structure and element configuration resources to find element IDs and current values.
5248
5341
 
5249
- ### 5. Create elementConfig
5250
- Map each configuration-id to its widget properties using PropValues:
5342
+ ### 5. Create propertiesToChange
5343
+ Map property names to PropValues using the widget schema:
5251
5344
  - Use correct \`$$type\` matching the widget's schema
5252
5345
  - Use global variables in PropValues where applicable
5253
5346
  - Example:
5254
5347
  \`\`\`json
5255
5348
  {
5256
- "heading-1": {
5257
- "text": { "$$type": "string", "value": "Welcome" },
5258
- "tag": { "$$type": "string", "value": "h1" }
5259
- }
5349
+ "text": { "$$type": "string", "value": "Welcome" },
5350
+ "tag": { "$$type": "string", "value": "h1" }
5260
5351
  }
5261
5352
  \`\`\`
5262
5353
 
5263
5354
  ### 6. Create style
5264
- 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.
5265
5356
  - Example:
5266
5357
  \`\`\`json
5267
5358
  {
5268
- "heading-1": "color: #1a1a1a; font-size: 2rem;"
5269
- }
5359
+ "color": "#1a1a1a",
5360
+ "font-size": "2rem"
5270
5361
  }
5271
5362
  \`\`\`
5272
5363
 
5273
- ### 7. Execute build_composition
5274
- Call the tool with your XML structure, elementConfig, and style. The response will contain the created element IDs.
5275
- 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.
5276
5366
 
5277
5367
  ## Key Points
5278
5368
 
@@ -5387,7 +5477,7 @@ function shouldBlock(sourceElements, targetElements) {
5387
5477
  }
5388
5478
 
5389
5479
  // src/style-commands/paste-style.ts
5390
- 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";
5391
5481
  import { classesPropTypeUtil } from "@elementor/editor-props";
5392
5482
  import {
5393
5483
  __privateListenTo as listenTo5,
@@ -5396,7 +5486,7 @@ import {
5396
5486
  } from "@elementor/editor-v1-adapters";
5397
5487
 
5398
5488
  // src/utils/command-utils.ts
5399
- import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
5489
+ import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
5400
5490
  import { CLASSES_PROP_KEY } from "@elementor/editor-props";
5401
5491
  import { __ as __5 } from "@wordpress/i18n";
5402
5492
  function hasAtomicWidgets(args) {
@@ -5421,7 +5511,7 @@ function getClassesProp(container) {
5421
5511
  }
5422
5512
  function getContainerSchema(container) {
5423
5513
  const type = container?.model.get("widgetType") || container?.model.get("elType");
5424
- const widgetsCache = getWidgetsCache8();
5514
+ const widgetsCache = getWidgetsCache7();
5425
5515
  const elementType = widgetsCache?.[type];
5426
5516
  return elementType?.atomic_props_schema ?? null;
5427
5517
  }
@@ -5441,7 +5531,7 @@ function getTitleForContainers(containers) {
5441
5531
  import {
5442
5532
  createElementStyle as createElementStyle2,
5443
5533
  deleteElementStyle,
5444
- getElementStyles as getElementStyles3,
5534
+ getElementStyles as getElementStyles2,
5445
5535
  updateElementStyle as updateElementStyle2
5446
5536
  } from "@elementor/editor-elements";
5447
5537
  import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
@@ -5456,7 +5546,7 @@ var undoablePasteElementStyle = () => undoable2(
5456
5546
  if (!classesProp) {
5457
5547
  return null;
5458
5548
  }
5459
- const originalStyles = getElementStyles3(container.id);
5549
+ const originalStyles = getElementStyles2(container.id);
5460
5550
  const [styleId, styleDef] = Object.entries(originalStyles ?? {})[0] ?? [];
5461
5551
  const originalStyle = Object.keys(styleDef ?? {}).length ? styleDef : null;
5462
5552
  const revertData = {
@@ -5540,7 +5630,7 @@ function pasteStyles(args, pasteLocalStyle) {
5540
5630
  }
5541
5631
  const clipboardElements = getClipboardElements(storageKey);
5542
5632
  const [clipboardElement] = clipboardElements ?? [];
5543
- const clipboardContainer = getContainer7(clipboardElement.id);
5633
+ const clipboardContainer = getContainer5(clipboardElement.id);
5544
5634
  if (!clipboardElement || !clipboardContainer || !isAtomicWidget(clipboardContainer)) {
5545
5635
  return;
5546
5636
  }
@@ -5586,7 +5676,7 @@ import {
5586
5676
  } from "@elementor/editor-v1-adapters";
5587
5677
 
5588
5678
  // src/style-commands/undoable-actions/reset-element-style.ts
5589
- 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";
5590
5680
  import { ELEMENTS_STYLES_RESERVED_LABEL as ELEMENTS_STYLES_RESERVED_LABEL2 } from "@elementor/editor-styles-repository";
5591
5681
  import { undoable as undoable3 } from "@elementor/editor-v1-adapters";
5592
5682
  import { __ as __7 } from "@wordpress/i18n";
@@ -5595,7 +5685,7 @@ var undoableResetElementStyle = () => undoable3(
5595
5685
  do: ({ containers }) => {
5596
5686
  return containers.map((container) => {
5597
5687
  const elementId = container.model.get("id");
5598
- const containerStyles = getElementStyles4(elementId);
5688
+ const containerStyles = getElementStyles3(elementId);
5599
5689
  Object.keys(containerStyles ?? {}).forEach(
5600
5690
  (styleId) => deleteElementStyle2(elementId, styleId)
5601
5691
  );
@@ -5687,15 +5777,15 @@ function init() {
5687
5777
  initCanvasMcp(
5688
5778
  getMCPByDomain("canvas", {
5689
5779
  instructions: `Everything related to V4 ( Atomic ) canvas.
5690
- # Canvas workflow for new compositions
5691
- - Configure elements settings and styles
5692
- - Build compositions/sections out of V4 atomic elements using context aware designs using the website resources
5693
- - 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
5694
5783
  `,
5695
5784
  docs: mcpDescription
5696
5785
  })
5697
5786
  );
5698
5787
  initTabsModelExtensions();
5788
+ initListType();
5699
5789
  }
5700
5790
 
5701
5791
  // src/sync/drag-element-from-panel.ts
@@ -5817,10 +5907,10 @@ function useEscapeOnCanvas(canvasDocument, onEscape) {
5817
5907
  }
5818
5908
 
5819
5909
  // src/utils/after-render.ts
5820
- import { getContainer as getContainer8 } from "@elementor/editor-elements";
5910
+ import { getContainer as getContainer6 } from "@elementor/editor-elements";
5821
5911
  function doAfterRender(elementIds, callback) {
5822
5912
  const pending = elementIds.map((elementId) => {
5823
- const view = getContainer8(elementId)?.view;
5913
+ const view = getContainer6(elementId)?.view;
5824
5914
  if (!view || !hasDoAfterRender(view)) {
5825
5915
  return void 0;
5826
5916
  }