@elementor/editor-canvas 4.0.0-manual → 4.0.1

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 (38) hide show
  1. package/dist/index.d.mts +11 -2
  2. package/dist/index.d.ts +11 -2
  3. package/dist/index.js +307 -128
  4. package/dist/index.mjs +273 -94
  5. package/package.json +19 -18
  6. package/src/components/__tests__/style-renderer.test.tsx +4 -0
  7. package/src/hooks/__tests__/use-style-items.test.ts +125 -0
  8. package/src/hooks/use-style-items.ts +40 -16
  9. package/src/index.ts +1 -1
  10. package/src/init-settings-transformers.ts +2 -0
  11. package/src/legacy/create-nested-templated-element-type.ts +15 -2
  12. package/src/legacy/create-templated-element-type.ts +8 -0
  13. package/src/legacy/replacements/base.ts +4 -0
  14. package/src/legacy/replacements/inline-editing/canvas-inline-editor.tsx +49 -27
  15. package/src/legacy/replacements/inline-editing/inline-editing-elements.tsx +16 -10
  16. package/src/legacy/replacements/inline-editing/inline-editing-utils.ts +12 -1
  17. package/src/legacy/replacements/manager.ts +13 -0
  18. package/src/legacy/types.ts +6 -1
  19. package/src/mcp/canvas-mcp.ts +5 -7
  20. package/src/mcp/resources/breakpoints-resource.ts +11 -4
  21. package/src/mcp/resources/document-structure-resource.ts +18 -13
  22. package/src/mcp/tools/build-composition/schema.ts +1 -1
  23. package/src/mcp/tools/build-composition/tool.ts +5 -1
  24. package/src/mcp/utils/__tests__/get-composition-target-container.test.ts +59 -0
  25. package/src/mcp/utils/get-composition-target-container.ts +15 -0
  26. package/src/renderers/__tests__/create-styles-renderer.test.ts +117 -0
  27. package/src/renderers/create-styles-renderer.ts +13 -3
  28. package/src/style-commands/__tests__/paste-style.test.ts +5 -3
  29. package/src/style-commands/__tests__/reset-style.test.ts +3 -3
  30. package/src/style-commands/paste-style.ts +7 -1
  31. package/src/style-commands/reset-style.ts +1 -1
  32. package/src/style-commands/undoable-actions/paste-element-style.ts +1 -1
  33. package/src/style-commands/undoable-actions/reset-element-style.ts +1 -1
  34. package/src/transformers/shared/__tests__/svg-src-transformer.test.ts +184 -0
  35. package/src/transformers/shared/svg-src-transformer.ts +87 -0
  36. package/src/transformers/styles/__tests__/size-transformer.test.ts +24 -0
  37. package/src/transformers/styles/size-transformer.ts +3 -0
  38. /package/src/{style-commands/utils.ts → utils/command-utils.ts} +0 -0
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { v1ReadyEvent } from "@elementor/editor-v1-adapters";
3
3
  var BREAKPOINTS_SCHEMA_URI = "elementor://breakpoints/list";
4
4
  var initBreakpointsResource = (reg) => {
5
- const { mcpServer, sendResourceUpdated } = reg;
5
+ const { resource, sendResourceUpdated } = reg;
6
6
  const getBreakpointsList = () => {
7
7
  const { breakpoints } = window.elementor?.config?.responsive || {};
8
8
  if (!breakpoints) {
@@ -26,9 +26,16 @@ var initBreakpointsResource = (reg) => {
26
26
  }
27
27
  ]
28
28
  });
29
- mcpServer.resource("breakpoints ", BREAKPOINTS_SCHEMA_URI, () => {
30
- return buildResourceResponse();
31
- });
29
+ resource(
30
+ "breakpoints ",
31
+ BREAKPOINTS_SCHEMA_URI,
32
+ {
33
+ description: "Breakpoints list."
34
+ },
35
+ () => {
36
+ return buildResourceResponse();
37
+ }
38
+ );
32
39
  window.addEventListener(v1ReadyEvent().name, () => {
33
40
  sendResourceUpdated({
34
41
  uri: BREAKPOINTS_SCHEMA_URI,
@@ -799,14 +806,22 @@ var UnknownStyleStateError = createError({
799
806
  var SELECTORS_MAP = {
800
807
  class: "."
801
808
  };
809
+ var DEFAULT_BREAKPOINT = "desktop";
810
+ var DEFAULT_STATE = "normal";
811
+ function getStyleUniqueKey(style) {
812
+ const breakpoint = style.variants[0]?.meta?.breakpoint ?? DEFAULT_BREAKPOINT;
813
+ const state = style.variants[0]?.meta?.state ?? DEFAULT_STATE;
814
+ return `${style.id}-${breakpoint}-${state}`;
815
+ }
802
816
  function createStylesRenderer({ resolve, breakpoints, selectorPrefix = "" }) {
803
817
  return async ({ styles, signal }) => {
804
- const seenIds = /* @__PURE__ */ new Set();
818
+ const seenKeys = /* @__PURE__ */ new Set();
805
819
  const uniqueStyles = styles.filter((style) => {
806
- if (seenIds.has(style.id)) {
820
+ const key = getStyleUniqueKey(style);
821
+ if (seenKeys.has(key)) {
807
822
  return false;
808
823
  }
809
- seenIds.add(style.id);
824
+ seenKeys.add(key);
810
825
  return true;
811
826
  });
812
827
  const stylesCssPromises = uniqueStyles.map(async (style) => {
@@ -895,22 +910,30 @@ function useStyleItems() {
895
910
  const [styleItems, setStyleItems] = useState3({});
896
911
  const styleItemsCacheRef = useRef2(/* @__PURE__ */ new Map());
897
912
  const providerAndSubscribers = useMemo4(() => {
898
- return stylesRepository2.getProviders().map((provider) => {
899
- const providerKey = provider.getKey();
913
+ const createEmptyCache = () => {
914
+ return { orderedIds: [], itemsById: /* @__PURE__ */ new Map() };
915
+ };
916
+ const getCache = (provider) => {
917
+ const providerKey = safeGetKey(provider);
918
+ if (!providerKey) {
919
+ return createEmptyCache();
920
+ }
900
921
  if (!styleItemsCacheRef.current.has(providerKey)) {
901
- styleItemsCacheRef.current.set(providerKey, { orderedIds: [], itemsById: /* @__PURE__ */ new Map() });
922
+ styleItemsCacheRef.current.set(providerKey, createEmptyCache());
902
923
  }
903
- const cache = styleItemsCacheRef.current.get(providerKey);
904
- return {
924
+ return styleItemsCacheRef.current.get(providerKey);
925
+ };
926
+ return stylesRepository2.getProviders().map(
927
+ (provider) => ({
905
928
  provider,
906
929
  subscriber: createProviderSubscriber2({
907
930
  provider,
908
931
  renderStyles,
909
932
  setStyleItems,
910
- cache
933
+ getCache: () => getCache(provider)
911
934
  })
912
- };
913
- });
935
+ })
936
+ );
914
937
  }, [renderStyles]);
915
938
  useEffect6(() => {
916
939
  const unsubscribes = providerAndSubscribers.map(
@@ -950,15 +973,23 @@ function stateSorter({ state: stateA }, { state: stateB }) {
950
973
  function createBreakpointSorter(breakpointsOrder) {
951
974
  return ({ breakpoint: breakpointA }, { breakpoint: breakpointB }) => breakpointsOrder.indexOf(breakpointA) - breakpointsOrder.indexOf(breakpointB);
952
975
  }
953
- function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cache }) {
976
+ function safeGetKey(provider) {
977
+ try {
978
+ return provider.getKey();
979
+ } catch {
980
+ return null;
981
+ }
982
+ }
983
+ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, getCache }) {
954
984
  return abortPreviousRuns(
955
985
  (abortController, previous, current) => signalizedProcess(abortController.signal).then((_, signal) => {
986
+ const cache = getCache();
956
987
  const hasDiffInfo = current !== void 0 && previous !== void 0;
957
988
  const hasCache = cache.orderedIds.length > 0;
958
989
  if (hasDiffInfo && hasCache) {
959
- return updateItems(previous, current, signal);
990
+ return updateItems(cache, previous, current, signal);
960
991
  }
961
- return createItems(signal);
992
+ return createItems(cache, signal);
962
993
  }).then((items) => {
963
994
  setStyleItems((prev) => ({
964
995
  ...prev,
@@ -966,7 +997,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
966
997
  }));
967
998
  }).execute()
968
999
  );
969
- async function updateItems(previous, current, signal) {
1000
+ async function updateItems(cache, previous, current, signal) {
970
1001
  const changedIds = getChangedStyleIds(previous, current);
971
1002
  cache.orderedIds = provider.actions.all().map((style) => style.id).reverse();
972
1003
  if (changedIds.length > 0) {
@@ -981,9 +1012,9 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
981
1012
  }
982
1013
  return getOrderedItems(cache);
983
1014
  }
984
- async function createItems(signal) {
1015
+ async function createItems(cache, signal) {
985
1016
  const allStyles = provider.actions.all();
986
- const styles = allStyles.reverse().map((style) => {
1017
+ const styles = [...allStyles].reverse().map((style) => {
987
1018
  return {
988
1019
  ...style,
989
1020
  cssName: provider.actions.resolveCssName(style.id)
@@ -991,7 +1022,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
991
1022
  });
992
1023
  return renderStyles({ styles: breakToBreakpoints(styles), signal }).then((rendered) => {
993
1024
  rebuildCache(cache, allStyles, rendered);
994
- return rendered;
1025
+ return getOrderedItems(cache);
995
1026
  });
996
1027
  }
997
1028
  function breakToBreakpoints(styles) {
@@ -1376,14 +1407,74 @@ var plainTransformer = createTransformer((value) => {
1376
1407
  return value;
1377
1408
  });
1378
1409
 
1379
- // src/transformers/shared/video-src-transformer.ts
1410
+ // src/transformers/shared/svg-src-transformer.ts
1411
+ import DOMPurify from "dompurify";
1380
1412
  import { getMediaAttachment as getMediaAttachment2 } from "@elementor/wp-media";
1413
+ var SVG_INLINE_STYLES = "width: 100%; height: 100%; overflow: unset;";
1414
+ function processSvgContent(svgText) {
1415
+ const sanitized = DOMPurify.sanitize(svgText, {
1416
+ USE_PROFILES: { svg: true, svgFilters: true }
1417
+ });
1418
+ const parser = new DOMParser();
1419
+ const doc = parser.parseFromString(sanitized, "image/svg+xml");
1420
+ const svgElement = doc.querySelector("svg");
1421
+ if (!svgElement) {
1422
+ return null;
1423
+ }
1424
+ svgElement.setAttribute("fill", "currentColor");
1425
+ const existingStyle = svgElement.getAttribute("style") ?? "";
1426
+ const trimmed = existingStyle.trim();
1427
+ const merged = trimmed ? `${trimmed.replace(/;$/, "")}; ${SVG_INLINE_STYLES}` : SVG_INLINE_STYLES;
1428
+ svgElement.setAttribute("style", merged);
1429
+ return svgElement.outerHTML;
1430
+ }
1431
+ async function fetchSvgContent(url, signal) {
1432
+ try {
1433
+ const response = await fetch(url, { signal });
1434
+ if (!response.ok) {
1435
+ return null;
1436
+ }
1437
+ const contentType = response.headers.get("content-type") ?? "";
1438
+ const isSvg = contentType.includes("svg") || contentType.includes("xml") || url.endsWith(".svg");
1439
+ if (!isSvg) {
1440
+ return null;
1441
+ }
1442
+ return await response.text();
1443
+ } catch {
1444
+ return null;
1445
+ }
1446
+ }
1447
+ function resolveSvgSrcId(id) {
1448
+ if (typeof id !== "number" || id <= 0) {
1449
+ return null;
1450
+ }
1451
+ return id;
1452
+ }
1453
+ var svgSrcTransformer = createTransformer(async (value, { signal }) => {
1454
+ const id = resolveSvgSrcId(value.id);
1455
+ const urlFromValue = typeof value.url === "string" ? value.url : null;
1456
+ let url = urlFromValue;
1457
+ if (id && !urlFromValue) {
1458
+ const attachment = await getMediaAttachment2({ id });
1459
+ url = attachment?.url ?? null;
1460
+ }
1461
+ const resolvedUrl = typeof url === "string" ? url : null;
1462
+ if (!resolvedUrl) {
1463
+ return { html: null, url: null };
1464
+ }
1465
+ const svgText = await fetchSvgContent(resolvedUrl, signal);
1466
+ const html = svgText ? processSvgContent(svgText) : null;
1467
+ return { html, url: resolvedUrl };
1468
+ });
1469
+
1470
+ // src/transformers/shared/video-src-transformer.ts
1471
+ import { getMediaAttachment as getMediaAttachment3 } from "@elementor/wp-media";
1381
1472
  var videoSrcTransformer = createTransformer(async (value) => {
1382
1473
  const { id, url } = value;
1383
1474
  if (!id) {
1384
1475
  return { id: null, url };
1385
1476
  }
1386
- const attachment = await getMediaAttachment2({ id });
1477
+ const attachment = await getMediaAttachment3({ id });
1387
1478
  return {
1388
1479
  id,
1389
1480
  url: attachment?.url ?? url
@@ -1392,7 +1483,7 @@ var videoSrcTransformer = createTransformer(async (value) => {
1392
1483
 
1393
1484
  // src/init-settings-transformers.ts
1394
1485
  function initSettingsTransformers() {
1395
- settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("query", queryTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("video-src", videoSrcTransformer).register("attributes", attributesTransformer).register("date-time", dateTimeTransformer).register("html-v2", htmlV2Transformer).register("html-v3", htmlV3Transformer).registerFallback(plainTransformer);
1486
+ 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).registerFallback(plainTransformer);
1396
1487
  }
1397
1488
 
1398
1489
  // src/transformers/styles/background-color-overlay-transformer.ts
@@ -1591,6 +1682,9 @@ var shadowTransformer = createTransformer((value) => {
1591
1682
 
1592
1683
  // src/transformers/styles/size-transformer.ts
1593
1684
  var sizeTransformer = createTransformer((value) => {
1685
+ if (value.unit === "auto") {
1686
+ return "auto";
1687
+ }
1594
1688
  return value.unit === "custom" ? value.size : `${value.size}${value.unit}`;
1595
1689
  });
1596
1690
 
@@ -1974,6 +2068,7 @@ function createTemplatedElementView({
1974
2068
  this._lastResolvedSettingsHash = settingsHash;
1975
2069
  const context = {
1976
2070
  id: this.model.get("id"),
2071
+ interaction_id: this.getInteractionId(),
1977
2072
  type,
1978
2073
  settings,
1979
2074
  base_styles: baseStylesDictionary
@@ -2008,6 +2103,11 @@ function createTemplatedElementView({
2008
2103
  _openEditingPanel(options) {
2009
2104
  this._doAfterRender(() => super._openEditingPanel(options));
2010
2105
  }
2106
+ getInteractionId() {
2107
+ const originId = this.model.get("originId");
2108
+ const id = this.model.get("id");
2109
+ return originId ?? id;
2110
+ }
2011
2111
  };
2012
2112
  }
2013
2113
 
@@ -2040,10 +2140,11 @@ function createNestedTemplatedElementType({
2040
2140
  }
2041
2141
  function buildEditorAttributes(model) {
2042
2142
  const id = model.get("id");
2143
+ const originId = model.get("originId");
2043
2144
  const cid = model.cid ?? "";
2044
2145
  const attrs = {
2045
2146
  "data-model-cid": cid,
2046
- "data-interaction-id": id,
2147
+ "data-interaction-id": originId ?? id,
2047
2148
  "x-ignore": "true"
2048
2149
  };
2049
2150
  return Object.entries(attrs).map(([key, value]) => `${key}="${value}"`).join(" ");
@@ -2077,6 +2178,9 @@ function createNestedTemplatedElementView({
2077
2178
  invalidateRenderCache() {
2078
2179
  this._lastResolvedSettingsHash = null;
2079
2180
  },
2181
+ renderOnChange() {
2182
+ this.render();
2183
+ },
2080
2184
  render() {
2081
2185
  this._abortController?.abort();
2082
2186
  this._abortController = new AbortController();
@@ -2120,6 +2224,7 @@ function createNestedTemplatedElementView({
2120
2224
  this._lastResolvedSettingsHash = settingsHash;
2121
2225
  const context = {
2122
2226
  id: model.get("id"),
2227
+ interaction_id: this.getInteractionId(),
2123
2228
  type,
2124
2229
  settings,
2125
2230
  base_styles: baseStylesDictionary,
@@ -2249,13 +2354,20 @@ function createNestedTemplatedElementView({
2249
2354
  },
2250
2355
  _openEditingPanel(options) {
2251
2356
  this._doAfterRender(() => parentOpenEditingPanel.call(this, options));
2357
+ },
2358
+ getInteractionId() {
2359
+ const originId = this.model.get("originId");
2360
+ const id = this.model.get("id");
2361
+ return originId ?? id;
2252
2362
  }
2253
2363
  });
2254
2364
  }
2255
2365
 
2366
+ // src/legacy/replacements/manager.ts
2367
+ import { createRoot } from "react-dom/client";
2368
+
2256
2369
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2257
2370
  import * as React6 from "react";
2258
- import { createRoot } from "react-dom/client";
2259
2371
  import { getContainer, getElementLabel, getElementType as getElementType2 } from "@elementor/editor-elements";
2260
2372
  import {
2261
2373
  htmlV3PropTypeUtil as htmlV3PropTypeUtil2,
@@ -2278,6 +2390,8 @@ var ReplacementBase = class {
2278
2390
  type;
2279
2391
  id;
2280
2392
  refreshView;
2393
+ reactRoot;
2394
+ reactContainer;
2281
2395
  constructor(settings) {
2282
2396
  this.getSetting = settings.getSetting;
2283
2397
  this.setSetting = settings.setSetting;
@@ -2285,6 +2399,8 @@ var ReplacementBase = class {
2285
2399
  this.type = settings.type;
2286
2400
  this.id = settings.id;
2287
2401
  this.refreshView = settings.refreshView;
2402
+ this.reactRoot = settings.reactRoot;
2403
+ this.reactContainer = settings.reactContainer;
2288
2404
  }
2289
2405
  static getTypes() {
2290
2406
  return null;
@@ -2305,7 +2421,7 @@ var ReplacementBase = class {
2305
2421
 
2306
2422
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2307
2423
  import * as React5 from "react";
2308
- import { useEffect as useEffect8, useLayoutEffect, useState as useState5 } from "react";
2424
+ import { useCallback as useCallback2, useEffect as useEffect8, useLayoutEffect, useState as useState5 } from "react";
2309
2425
  import { InlineEditor, InlineEditorToolbar } from "@elementor/editor-controls";
2310
2426
  import { Box as Box2, ThemeProvider } from "@elementor/ui";
2311
2427
  import { autoUpdate as autoUpdate2, flip, FloatingPortal as FloatingPortal2, useFloating as useFloating2 } from "@floating-ui/react";
@@ -2335,7 +2451,8 @@ var INLINE_EDITING_PROPERTY_PER_TYPE = {
2335
2451
  "e-button": "text",
2336
2452
  "e-form-label": "text",
2337
2453
  "e-heading": "title",
2338
- "e-paragraph": "paragraph"
2454
+ "e-paragraph": "paragraph",
2455
+ "e-form-submit-button": "text"
2339
2456
  };
2340
2457
  var getInlineEditorElement = (elementWrapper, expectedTag) => {
2341
2458
  return !expectedTag ? null : elementWrapper.querySelector(expectedTag);
@@ -2353,6 +2470,11 @@ var useOnClickOutsideIframe = (handleUnmount) => {
2353
2470
  };
2354
2471
  var useRenderToolbar = (ownerDocument, id) => {
2355
2472
  const [anchor, setAnchor] = useState4(null);
2473
+ useEffect7(() => {
2474
+ if (!anchor) {
2475
+ removeToolbarAnchor(ownerDocument, id);
2476
+ }
2477
+ }, [anchor, ownerDocument, id]);
2356
2478
  const onSelectionEnd = (view) => {
2357
2479
  const hasSelection = !view.state.selection.empty;
2358
2480
  removeToolbarAnchor(ownerDocument, id);
@@ -2362,7 +2484,10 @@ var useRenderToolbar = (ownerDocument, id) => {
2362
2484
  setAnchor(null);
2363
2485
  }
2364
2486
  };
2365
- return { onSelectionEnd, anchor };
2487
+ const clearAnchor = useCallback(() => {
2488
+ setAnchor(null);
2489
+ }, []);
2490
+ return { onSelectionEnd, anchor, clearAnchor };
2366
2491
  };
2367
2492
  var createAnchorBasedOnSelection = (ownerDocument, id) => {
2368
2493
  const frameWindow = ownerDocument.defaultView;
@@ -2429,46 +2554,59 @@ var horizontalShifterMiddleware = {
2429
2554
  };
2430
2555
 
2431
2556
  // src/legacy/replacements/inline-editing/canvas-inline-editor.tsx
2432
- var EDITOR_WRAPPER_SELECTOR = "inline-editor-wrapper";
2433
2557
  var CanvasInlineEditor = ({
2434
2558
  elementClasses,
2435
2559
  initialValue,
2436
2560
  expectedTag,
2437
2561
  rootElement,
2562
+ contentElement,
2438
2563
  id,
2439
2564
  setValue,
2440
- ...props
2565
+ requestDestroy
2441
2566
  }) => {
2567
+ const [active, setActive] = useState5(true);
2442
2568
  const [editor, setEditor] = useState5(null);
2443
- const { onSelectionEnd, anchor: toolbarAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2444
- const onBlur = () => {
2445
- removeToolbarAnchor(rootElement.ownerDocument, id);
2446
- props.onBlur();
2447
- };
2448
- useOnClickOutsideIframe(onBlur);
2449
- return /* @__PURE__ */ React5.createElement(ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement("style", null, `
2450
- .ProseMirror > * {
2451
- height: 100%;
2452
- }
2453
- .${EDITOR_WRAPPER_SELECTOR} .ProseMirror > button[contenteditable="true"] {
2454
- height: auto;
2455
- cursor: text;
2456
- }
2457
- `), /* @__PURE__ */ React5.createElement(
2569
+ const { onSelectionEnd, anchor: toolbarAnchor, clearAnchor } = useRenderToolbar(rootElement.ownerDocument, id);
2570
+ useEffect8(() => {
2571
+ if (!active) {
2572
+ clearAnchor();
2573
+ requestDestroy();
2574
+ }
2575
+ }, [active, clearAnchor, requestDestroy]);
2576
+ const dismiss = useCallback2(() => {
2577
+ setEditor(null);
2578
+ setActive(false);
2579
+ }, []);
2580
+ useOnClickOutsideIframe(dismiss);
2581
+ useEffect8(() => {
2582
+ const ownerDocument = contentElement.ownerDocument;
2583
+ const handleClickAway = (event) => {
2584
+ if (contentElement.contains(event.target)) {
2585
+ return;
2586
+ }
2587
+ dismiss();
2588
+ };
2589
+ ownerDocument.addEventListener("mousedown", handleClickAway);
2590
+ return () => ownerDocument.removeEventListener("mousedown", handleClickAway);
2591
+ }, [contentElement, dismiss]);
2592
+ if (!active) {
2593
+ return null;
2594
+ }
2595
+ return /* @__PURE__ */ React5.createElement(ThemeProvider, null, /* @__PURE__ */ React5.createElement(InlineEditingOverlay, { expectedTag, rootElement, id }), /* @__PURE__ */ React5.createElement(
2458
2596
  InlineEditor,
2459
2597
  {
2460
2598
  onEditorCreate: setEditor,
2599
+ mountElement: contentElement,
2461
2600
  editorProps: {
2462
2601
  attributes: {
2463
- style: "outline: none;overflow-wrap: normal;height:100%"
2602
+ style: "outline: none; display: inherit; justify-content: inherit; align-items: inherit; flex-direction: inherit; text-align: inherit;"
2464
2603
  }
2465
2604
  },
2466
2605
  elementClasses,
2467
2606
  value: initialValue,
2468
2607
  setValue,
2469
- onBlur,
2608
+ onBlur: dismiss,
2470
2609
  autofocus: true,
2471
- expectedTag,
2472
2610
  onSelectionEnd
2473
2611
  }
2474
2612
  ), toolbarAnchor && editor && /* @__PURE__ */ React5.createElement(InlineEditingToolbar, { anchor: toolbarAnchor, editor, id }));
@@ -2497,7 +2635,18 @@ var InlineEditingToolbar = ({ anchor, editor, id }) => {
2497
2635
  refs.setReference(anchor);
2498
2636
  return () => refs.setReference(null);
2499
2637
  }, [anchor, refs]);
2500
- return /* @__PURE__ */ React5.createElement(FloatingPortal2, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React5.createElement(Box2, { ref: refs.setFloating, role: "presentation", style: { ...floatingStyles, pointerEvents: "none" } }, /* @__PURE__ */ React5.createElement(InlineEditorToolbar, { editor, elementId: id })));
2638
+ return /* @__PURE__ */ React5.createElement(FloatingPortal2, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React5.createElement(
2639
+ Box2,
2640
+ {
2641
+ ref: refs.setFloating,
2642
+ role: "presentation",
2643
+ style: {
2644
+ ...floatingStyles,
2645
+ pointerEvents: "none"
2646
+ }
2647
+ },
2648
+ /* @__PURE__ */ React5.createElement(InlineEditorToolbar, { editor, elementId: id })
2649
+ ));
2501
2650
  };
2502
2651
 
2503
2652
  // src/legacy/replacements/inline-editing/inline-editing-eligibility.ts
@@ -2531,8 +2680,8 @@ var isInlineEditingAllowed = ({ rawValue, propTypeFromSchema }) => {
2531
2680
  // src/legacy/replacements/inline-editing/inline-editing-elements.tsx
2532
2681
  var HISTORY_DEBOUNCE_WAIT = 800;
2533
2682
  var InlineEditingReplacement = class extends ReplacementBase {
2534
- inlineEditorRoot = null;
2535
2683
  handlerAttached = false;
2684
+ editing = false;
2536
2685
  getReplacementKey() {
2537
2686
  return "inline-editing";
2538
2687
  }
@@ -2540,7 +2689,7 @@ var InlineEditingReplacement = class extends ReplacementBase {
2540
2689
  return Object.keys(INLINE_EDITING_PROPERTY_PER_TYPE);
2541
2690
  }
2542
2691
  isEditingModeActive() {
2543
- return !!this.inlineEditorRoot;
2692
+ return this.editing;
2544
2693
  }
2545
2694
  shouldRenderReplacement() {
2546
2695
  return this.isInlineEditingEligible() && getCurrentEditMode() === "edit";
@@ -2583,8 +2732,8 @@ var InlineEditingReplacement = class extends ReplacementBase {
2583
2732
  resetInlineEditorRoot() {
2584
2733
  this.element.removeEventListener("click", this.handleRenderInlineEditor);
2585
2734
  this.handlerAttached = false;
2586
- this.inlineEditorRoot?.unmount?.();
2587
- this.inlineEditorRoot = null;
2735
+ this.reactRoot.render(null);
2736
+ this.editing = false;
2588
2737
  }
2589
2738
  unmountInlineEditor() {
2590
2739
  this.resetInlineEditorRoot();
@@ -2695,12 +2844,16 @@ var InlineEditingReplacement = class extends ReplacementBase {
2695
2844
  if (this.isEditingModeActive()) {
2696
2845
  this.resetInlineEditorRoot();
2697
2846
  }
2698
- const elementClasses = this.element.children?.[0]?.classList.toString() ?? "";
2847
+ const contentElement = this.element.children?.[0];
2848
+ if (!contentElement) {
2849
+ return;
2850
+ }
2851
+ const elementClasses = contentElement.classList.toString();
2699
2852
  const propValue = this.getExtractedContentValue();
2700
2853
  const expectedTag = this.getExpectedTag();
2701
- this.element.innerHTML = "";
2702
- this.inlineEditorRoot = createRoot(this.element);
2703
- this.inlineEditorRoot.render(
2854
+ contentElement.innerHTML = "";
2855
+ this.editing = true;
2856
+ this.reactRoot.render(
2704
2857
  /* @__PURE__ */ React6.createElement(
2705
2858
  CanvasInlineEditor,
2706
2859
  {
@@ -2708,9 +2861,10 @@ var InlineEditingReplacement = class extends ReplacementBase {
2708
2861
  initialValue: propValue,
2709
2862
  expectedTag,
2710
2863
  rootElement: this.element,
2864
+ contentElement,
2711
2865
  id: this.id,
2712
2866
  setValue: this.setContentValue.bind(this),
2713
- onBlur: this.unmountInlineEditor.bind(this)
2867
+ requestDestroy: this.unmountInlineEditor.bind(this)
2714
2868
  }
2715
2869
  )
2716
2870
  );
@@ -2739,16 +2893,24 @@ var createViewWithReplacements = (options) => {
2739
2893
  return class extends TemplatedView {
2740
2894
  #replacement = null;
2741
2895
  #config;
2896
+ #reactContainer;
2897
+ #reactRoot;
2742
2898
  constructor(...args) {
2743
2899
  super(...args);
2744
2900
  const settings = this.model.get("settings");
2901
+ this.#reactContainer = this.el.ownerDocument.createElement("div");
2902
+ this.#reactContainer.style.display = "none";
2903
+ this.el.ownerDocument.body.appendChild(this.#reactContainer);
2904
+ this.#reactRoot = createRoot(this.#reactContainer);
2745
2905
  this.#config = {
2746
2906
  getSetting: settings.get.bind(settings),
2747
2907
  setSetting: settings.set.bind(settings),
2748
2908
  element: this.el,
2749
2909
  type: this?.model?.get("widgetType") ?? this.container?.model?.get("elType") ?? null,
2750
2910
  id: this?.model?.get("id") ?? null,
2751
- refreshView: this.refreshView.bind(this)
2911
+ refreshView: this.refreshView.bind(this),
2912
+ reactRoot: this.#reactRoot,
2913
+ reactContainer: this.#reactContainer
2752
2914
  };
2753
2915
  }
2754
2916
  refreshView() {
@@ -2769,6 +2931,8 @@ var createViewWithReplacements = (options) => {
2769
2931
  }
2770
2932
  onDestroy() {
2771
2933
  this.#triggerAltMethod("onDestroy");
2934
+ this.#reactRoot.unmount();
2935
+ this.#reactContainer.remove();
2772
2936
  }
2773
2937
  _afterRender() {
2774
2938
  this.#triggerAltMethod("_afterRender");
@@ -2907,7 +3071,7 @@ function initTabsModelExtensions() {
2907
3071
  import { __privateListenTo as listenTo, commandEndEvent as commandEndEvent4 } from "@elementor/editor-v1-adapters";
2908
3072
  var DOCUMENT_STRUCTURE_URI = "elementor://document/structure";
2909
3073
  var initDocumentStructureResource = (reg) => {
2910
- const { mcpServer, sendResourceUpdated } = reg;
3074
+ const { resource, sendResourceUpdated } = reg;
2911
3075
  let currentDocumentStructure = null;
2912
3076
  const updateDocumentStructure = () => {
2913
3077
  const structure = getDocumentStructure();
@@ -2929,17 +3093,23 @@ var initDocumentStructureResource = (reg) => {
2929
3093
  updateDocumentStructure
2930
3094
  );
2931
3095
  updateDocumentStructure();
2932
- mcpServer.resource("document-structure", DOCUMENT_STRUCTURE_URI, async () => {
2933
- const structure = getDocumentStructure();
2934
- return {
2935
- contents: [
2936
- {
2937
- uri: DOCUMENT_STRUCTURE_URI,
2938
- text: JSON.stringify(structure, null, 2)
2939
- }
2940
- ]
2941
- };
2942
- });
3096
+ resource(
3097
+ "document-structure",
3098
+ DOCUMENT_STRUCTURE_URI,
3099
+ {
3100
+ description: "Document structure."
3101
+ },
3102
+ async () => {
3103
+ return {
3104
+ contents: [
3105
+ {
3106
+ uri: DOCUMENT_STRUCTURE_URI,
3107
+ text: JSON.stringify(getDocumentStructure(), null, 2)
3108
+ }
3109
+ ]
3110
+ };
3111
+ }
3112
+ );
2943
3113
  };
2944
3114
  function getDocumentStructure() {
2945
3115
  const extendedWindow = window;
@@ -2979,6 +3149,7 @@ function extractElementData(element) {
2979
3149
  }
2980
3150
 
2981
3151
  // src/mcp/tools/build-composition/tool.ts
3152
+ import { getCurrentDocument } from "@elementor/editor-documents";
2982
3153
  import {
2983
3154
  createElement as createElement8,
2984
3155
  deleteElement,
@@ -3409,6 +3580,16 @@ var CompositionBuilder = class _CompositionBuilder {
3409
3580
  }
3410
3581
  };
3411
3582
 
3583
+ // src/mcp/utils/get-composition-target-container.ts
3584
+ import { COMPONENT_DOCUMENT_TYPE } from "@elementor/editor-documents";
3585
+ function getCompositionTargetContainer(documentContainer, documentType) {
3586
+ const firstChild = documentContainer.children?.[0];
3587
+ if (documentType === COMPONENT_DOCUMENT_TYPE && firstChild) {
3588
+ return firstChild;
3589
+ }
3590
+ return documentContainer;
3591
+ }
3592
+
3412
3593
  // src/mcp/tools/build-composition/prompt.ts
3413
3594
  import { toolPrompts } from "@elementor/editor-mcp";
3414
3595
  var generatePrompt = () => {
@@ -3548,7 +3729,7 @@ Note: No height/width specified on any element - flexbox handles layout automati
3548
3729
  };
3549
3730
 
3550
3731
  // src/mcp/tools/build-composition/schema.ts
3551
- import { zod as z } from "@elementor/editor-mcp";
3732
+ import { z } from "@elementor/schema";
3552
3733
  var inputSchema = {
3553
3734
  xmlStructure: z.string().describe("The XML structure representing the composition to be built"),
3554
3735
  elementConfig: z.record(
@@ -3604,6 +3785,8 @@ var initBuildCompositionsTool = (reg) => {
3604
3785
  const errors = [];
3605
3786
  const rootContainers = [];
3606
3787
  const documentContainer = getContainer3("document");
3788
+ const currentDocument = getCurrentDocument();
3789
+ const targetContainer = getCompositionTargetContainer(documentContainer, currentDocument?.type.value);
3607
3790
  try {
3608
3791
  const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
3609
3792
  createElement: createElement8,
@@ -3616,7 +3799,7 @@ var initBuildCompositionsTool = (reg) => {
3616
3799
  configErrors,
3617
3800
  invalidStyles,
3618
3801
  rootContainers: generatedRootContainers
3619
- } = compositionBuilder.build(documentContainer);
3802
+ } = compositionBuilder.build(targetContainer);
3620
3803
  rootContainers.push(...generatedRootContainers);
3621
3804
  generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
3622
3805
  if (configErrors.length) {
@@ -4000,14 +4183,12 @@ var initGetElementConfigTool = (reg) => {
4000
4183
  var initCanvasMcp = (reg) => {
4001
4184
  const { setMCPDescription } = reg;
4002
4185
  setMCPDescription(
4003
- `Everything related to creative design, layout, styling and building the pages, specifically element of type "widget".
4186
+ `Everything related to V4 ( Atomic ) canvas.
4004
4187
  # Canvas workflow for new compositions
4005
- - Check existing global variables
4006
- - Check existing global classes
4007
- - Create missing global variables
4008
- - Create reusable global classes
4009
- - Build valid XML with minimal inline styles (layout/positioning only)
4010
- - Apply global classes to elements`
4188
+ - Configure elements settings and styles
4189
+ - Build compositions/sections out of V4 atomic elements using context aware designs using the website resources
4190
+ - Get and retrieve element configuration values
4191
+ `
4011
4192
  );
4012
4193
  initWidgetsSchemaResource(reg);
4013
4194
  initDocumentStructureResource(reg);
@@ -4225,18 +4406,7 @@ import {
4225
4406
  commandStartEvent
4226
4407
  } from "@elementor/editor-v1-adapters";
4227
4408
 
4228
- // src/style-commands/undoable-actions/paste-element-style.ts
4229
- import {
4230
- createElementStyle as createElementStyle2,
4231
- deleteElementStyle,
4232
- getElementStyles as getElementStyles3,
4233
- updateElementStyle as updateElementStyle2
4234
- } from "@elementor/editor-elements";
4235
- import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
4236
- import { undoable as undoable2 } from "@elementor/editor-v1-adapters";
4237
- import { __ as __6 } from "@wordpress/i18n";
4238
-
4239
- // src/style-commands/utils.ts
4409
+ // src/utils/command-utils.ts
4240
4410
  import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
4241
4411
  import { CLASSES_PROP_KEY } from "@elementor/editor-props";
4242
4412
  import { __ as __5 } from "@wordpress/i18n";
@@ -4279,6 +4449,15 @@ function getTitleForContainers(containers) {
4279
4449
  }
4280
4450
 
4281
4451
  // src/style-commands/undoable-actions/paste-element-style.ts
4452
+ import {
4453
+ createElementStyle as createElementStyle2,
4454
+ deleteElementStyle,
4455
+ getElementStyles as getElementStyles3,
4456
+ updateElementStyle as updateElementStyle2
4457
+ } from "@elementor/editor-elements";
4458
+ import { ELEMENTS_STYLES_RESERVED_LABEL } from "@elementor/editor-styles-repository";
4459
+ import { undoable as undoable2 } from "@elementor/editor-v1-adapters";
4460
+ import { __ as __6 } from "@wordpress/i18n";
4282
4461
  var undoablePasteElementStyle = () => undoable2(
4283
4462
  {
4284
4463
  do: ({ containers, newStyle }) => {