@elementor/editor-elements 3.33.0-98 → 3.35.0-324

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 (51) hide show
  1. package/dist/index.d.mts +204 -69
  2. package/dist/index.d.ts +204 -69
  3. package/dist/index.js +1163 -293
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1149 -291
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +7 -5
  8. package/src/errors.ts +10 -0
  9. package/src/hooks/use-element-children.ts +12 -12
  10. package/src/hooks/use-element-editor-settings.ts +12 -0
  11. package/src/hooks/use-element-interactions.ts +25 -0
  12. package/src/hooks/use-element-setting.ts +1 -1
  13. package/src/hooks/use-selected-element.ts +2 -2
  14. package/src/index.ts +38 -20
  15. package/src/mcp/elements-tool.ts +345 -0
  16. package/src/mcp/handlers/common-style-utils.ts +23 -0
  17. package/src/mcp/handlers/create-element.ts +96 -0
  18. package/src/mcp/handlers/create-style.ts +42 -0
  19. package/src/mcp/handlers/delete-element.ts +17 -0
  20. package/src/mcp/handlers/delete-style.ts +22 -0
  21. package/src/mcp/handlers/deselect-element.ts +21 -0
  22. package/src/mcp/handlers/duplicate-element.ts +22 -0
  23. package/src/mcp/handlers/get-element-props.ts +28 -0
  24. package/src/mcp/handlers/get-element-schema.ts +17 -0
  25. package/src/mcp/handlers/get-selected.ts +5 -0
  26. package/src/mcp/handlers/get-styles.ts +26 -0
  27. package/src/mcp/handlers/list-available-types.ts +27 -0
  28. package/src/mcp/handlers/move-element.ts +30 -0
  29. package/src/mcp/handlers/select-element.ts +25 -0
  30. package/src/mcp/handlers/update-props.ts +22 -0
  31. package/src/mcp/handlers/update-styles.ts +45 -0
  32. package/src/mcp/index.ts +9 -0
  33. package/src/sync/delete-element.ts +8 -2
  34. package/src/sync/drop-element.ts +30 -0
  35. package/src/sync/duplicate-elements.ts +3 -4
  36. package/src/sync/get-current-document-container.ts +1 -1
  37. package/src/sync/get-element-editor-settings.ts +8 -0
  38. package/src/sync/get-element-interactions.ts +15 -0
  39. package/src/sync/get-element-label.ts +6 -1
  40. package/src/sync/get-element-type.ts +28 -0
  41. package/src/sync/get-elements.ts +1 -1
  42. package/src/sync/get-widgets-cache.ts +4 -3
  43. package/src/sync/move-element.ts +45 -0
  44. package/src/sync/move-elements.ts +127 -0
  45. package/src/sync/remove-elements.ts +11 -0
  46. package/src/sync/replace-element.ts +50 -12
  47. package/src/sync/types.ts +32 -3
  48. package/src/sync/update-element-editor-settings.ts +28 -0
  49. package/src/sync/update-element-interactions.ts +32 -0
  50. package/src/types.ts +16 -1
  51. package/src/hooks/use-element-type.ts +0 -35
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- // src/hooks/use-element-setting.ts
2
- import { __privateUseListenTo as useListenTo, commandEndEvent } from "@elementor/editor-v1-adapters";
1
+ // src/hooks/use-element-children.ts
2
+ import { __privateUseListenTo as useListenTo, commandEndEvent, v1ReadyEvent } from "@elementor/editor-v1-adapters";
3
3
 
4
4
  // src/sync/get-container.ts
5
5
  import { __privateRunCommand as runCommand } from "@elementor/editor-v1-adapters";
@@ -16,6 +16,53 @@ var selectElement = (elementId) => {
16
16
  }
17
17
  };
18
18
 
19
+ // src/hooks/use-element-children.ts
20
+ function useElementChildren(elementId, childrenTypes) {
21
+ return useListenTo(
22
+ [
23
+ v1ReadyEvent(),
24
+ commandEndEvent("document/elements/create"),
25
+ commandEndEvent("document/elements/delete"),
26
+ commandEndEvent("document/elements/update"),
27
+ commandEndEvent("document/elements/set-settings")
28
+ ],
29
+ () => {
30
+ const container = getContainer(elementId);
31
+ const elementChildren = Object.entries(childrenTypes).reduce((acc, [parentType, childType]) => {
32
+ const parent = container?.children?.findRecursive?.(
33
+ ({ model }) => model.get("elType") === parentType
34
+ );
35
+ const children = parent?.children ?? [];
36
+ acc[childType] = children.filter(({ model }) => model.get("elType") === childType).map(({ id }) => ({ id }));
37
+ return acc;
38
+ }, {});
39
+ return elementChildren;
40
+ },
41
+ [elementId]
42
+ );
43
+ }
44
+
45
+ // src/hooks/use-element-editor-settings.ts
46
+ import { __privateUseListenTo as useListenTo2, windowEvent } from "@elementor/editor-v1-adapters";
47
+
48
+ // src/sync/get-element-editor-settings.ts
49
+ function getElementEditorSettings(elementId) {
50
+ const container = getContainer(elementId);
51
+ return container?.model.get("editor_settings") ?? {};
52
+ }
53
+
54
+ // src/hooks/use-element-editor-settings.ts
55
+ var useElementEditorSettings = (elementId) => {
56
+ return useListenTo2(
57
+ windowEvent("elementor/element/update_editor_settings"),
58
+ () => getElementEditorSettings(elementId),
59
+ [elementId]
60
+ );
61
+ };
62
+
63
+ // src/hooks/use-element-setting.ts
64
+ import { __privateUseListenTo as useListenTo3, commandEndEvent as commandEndEvent2 } from "@elementor/editor-v1-adapters";
65
+
19
66
  // src/sync/get-element-setting.ts
20
67
  var getElementSetting = (elementId, settingKey) => {
21
68
  const container = getContainer(elementId);
@@ -27,15 +74,15 @@ var getElementSettings = (elementId, settingKey) => {
27
74
 
28
75
  // src/hooks/use-element-setting.ts
29
76
  var useElementSetting = (elementId, settingKey) => {
30
- return useListenTo(
31
- commandEndEvent("document/elements/set-settings"),
77
+ return useListenTo3(
78
+ commandEndEvent2("document/elements/set-settings"),
32
79
  () => getElementSetting(elementId, settingKey),
33
80
  [elementId, settingKey]
34
81
  );
35
82
  };
36
83
  var useElementSettings = (elementId, settingKeys) => {
37
- return useListenTo(
38
- commandEndEvent("document/elements/set-settings"),
84
+ return useListenTo3(
85
+ commandEndEvent2("document/elements/set-settings"),
39
86
  () => settingKeys.reduce((settings, key) => {
40
87
  const value = getElementSetting(elementId, key);
41
88
  if (value !== null) {
@@ -43,49 +90,61 @@ var useElementSettings = (elementId, settingKeys) => {
43
90
  }
44
91
  return settings;
45
92
  }, {}),
46
- [elementId, ...settingKeys]
93
+ [elementId, settingKeys.join(",")]
47
94
  );
48
95
  };
49
96
 
50
- // src/hooks/use-element-type.ts
51
- import { __privateUseListenTo as useListenTo2, commandEndEvent as commandEndEvent2 } from "@elementor/editor-v1-adapters";
52
-
53
- // src/sync/get-widgets-cache.ts
54
- function getWidgetsCache() {
55
- const extendedWindow = window;
56
- return extendedWindow?.elementor?.widgetsCache || null;
57
- }
58
-
59
- // src/hooks/use-element-type.ts
60
- function useElementType(type) {
61
- return useListenTo2(
62
- commandEndEvent2("editor/documents/load"),
97
+ // src/hooks/use-parent-element.ts
98
+ import { __privateUseListenTo as useListenTo4, commandEndEvent as commandEndEvent3 } from "@elementor/editor-v1-adapters";
99
+ function useParentElement(elementId) {
100
+ return useListenTo4(
101
+ [commandEndEvent3("document/elements/create")],
63
102
  () => {
64
- if (!type) {
65
- return null;
66
- }
67
- const widgetsCache = getWidgetsCache();
68
- const elementType = widgetsCache?.[type];
69
- if (!elementType?.atomic_controls) {
103
+ if (!elementId) {
70
104
  return null;
71
105
  }
72
- if (!elementType?.atomic_props_schema) {
106
+ const extendedWindow = window;
107
+ const element = extendedWindow?.elementor?.getContainer?.(elementId);
108
+ if (!element) {
73
109
  return null;
74
110
  }
75
- return {
76
- key: type,
77
- controls: elementType.atomic_controls,
78
- propsSchema: elementType.atomic_props_schema,
79
- dependenciesPerTargetMapping: elementType.dependencies_per_target_mapping ?? {},
80
- title: elementType.title
81
- };
111
+ return element.parent;
82
112
  },
83
- [type]
113
+ [elementId]
84
114
  );
85
115
  }
86
116
 
87
117
  // src/hooks/use-selected-element.ts
88
- import { __privateUseListenTo as useListenTo3, commandEndEvent as commandEndEvent3 } from "@elementor/editor-v1-adapters";
118
+ import { __privateUseListenTo as useListenTo5, commandEndEvent as commandEndEvent4 } from "@elementor/editor-v1-adapters";
119
+
120
+ // src/sync/get-widgets-cache.ts
121
+ function getWidgetsCache() {
122
+ const extendedWindow = window;
123
+ return extendedWindow?.elementor?.widgetsCache || null;
124
+ }
125
+
126
+ // src/sync/get-element-type.ts
127
+ function getElementType(type) {
128
+ if (!type) {
129
+ return null;
130
+ }
131
+ const widgetsCache = getWidgetsCache();
132
+ const elementType = widgetsCache?.[type];
133
+ if (!elementType?.atomic_controls) {
134
+ return null;
135
+ }
136
+ if (!elementType?.atomic_props_schema) {
137
+ return null;
138
+ }
139
+ return {
140
+ key: type,
141
+ controls: elementType.atomic_controls,
142
+ propsSchema: elementType.atomic_props_schema,
143
+ dependenciesPerTargetMapping: elementType.dependencies_per_target_mapping ?? {},
144
+ title: elementType.title,
145
+ styleStates: elementType.atomic_style_states ?? []
146
+ };
147
+ }
89
148
 
90
149
  // src/sync/get-selected-elements.ts
91
150
  function getSelectedElements() {
@@ -105,72 +164,23 @@ function getSelectedElements() {
105
164
 
106
165
  // src/hooks/use-selected-element.ts
107
166
  function useSelectedElement() {
108
- const elements = useListenTo3(
167
+ const elements = useListenTo5(
109
168
  [
110
- commandEndEvent3("document/elements/select"),
111
- commandEndEvent3("document/elements/deselect"),
112
- commandEndEvent3("document/elements/select-all"),
113
- commandEndEvent3("document/elements/deselect-all")
169
+ commandEndEvent4("document/elements/select"),
170
+ commandEndEvent4("document/elements/deselect"),
171
+ commandEndEvent4("document/elements/select-all"),
172
+ commandEndEvent4("document/elements/deselect-all")
114
173
  ],
115
174
  getSelectedElements
116
175
  );
117
176
  const [element] = elements;
118
- const elementType = useElementType(element?.type);
177
+ const elementType = getElementType(element?.type);
119
178
  if (elements.length !== 1 || !elementType) {
120
179
  return { element: null, elementType: null };
121
180
  }
122
181
  return { element, elementType };
123
182
  }
124
183
 
125
- // src/hooks/use-parent-element.ts
126
- import { __privateUseListenTo as useListenTo4, commandEndEvent as commandEndEvent4 } from "@elementor/editor-v1-adapters";
127
- function useParentElement(elementId) {
128
- return useListenTo4(
129
- [commandEndEvent4("document/elements/create")],
130
- () => {
131
- if (!elementId) {
132
- return null;
133
- }
134
- const extendedWindow = window;
135
- const element = extendedWindow?.elementor?.getContainer?.(elementId);
136
- if (!element) {
137
- return null;
138
- }
139
- return element.parent;
140
- },
141
- [elementId]
142
- );
143
- }
144
-
145
- // src/hooks/use-element-children.ts
146
- import { __privateUseListenTo as useListenTo5, commandEndEvent as commandEndEvent5, v1ReadyEvent } from "@elementor/editor-v1-adapters";
147
- function useElementChildren(elementId, childrenTypes) {
148
- return useListenTo5(
149
- [
150
- v1ReadyEvent(),
151
- commandEndEvent5("document/elements/create"),
152
- commandEndEvent5("document/elements/delete"),
153
- commandEndEvent5("document/elements/update"),
154
- commandEndEvent5("document/elements/set-settings")
155
- ],
156
- () => {
157
- const container = getContainer(elementId);
158
- const elementChildren = childrenTypes.reduce((acc, type) => {
159
- acc[type] = [];
160
- return acc;
161
- }, {});
162
- container?.children?.forEachRecursive?.(({ model, id }) => {
163
- const elType = model.get("elType");
164
- if (elType && elType in elementChildren) {
165
- elementChildren[elType].push({ id });
166
- }
167
- });
168
- return elementChildren;
169
- },
170
- [elementId]
171
- );
172
- }
173
-
174
184
  // src/sync/create-element.ts
175
185
  import { __privateRunCommandSync as runCommandSync } from "@elementor/editor-v1-adapters";
176
186
  function createElement({ containerId, model, options }) {
@@ -185,41 +195,21 @@ function createElement({ containerId, model, options }) {
185
195
  });
186
196
  }
187
197
 
188
- // src/sync/duplicate-element.ts
189
- function duplicateElement({ elementId, options = {} }) {
190
- const elementToDuplicate = getContainer(elementId);
191
- if (!elementToDuplicate) {
192
- throw new Error(`Element with ID "${elementId}" not found`);
193
- }
194
- if (!elementToDuplicate.parent) {
195
- throw new Error(`Element with ID "${elementId}" has no parent container`);
196
- }
197
- const parentContainer = elementToDuplicate.parent;
198
- const elementModel = elementToDuplicate.model.toJSON();
199
- const currentIndex = elementToDuplicate.view?._index ?? 0;
200
- const insertPosition = options.clone !== false ? currentIndex + 1 : void 0;
201
- return createElement({
202
- containerId: parentContainer.id,
203
- model: elementModel,
204
- options: {
205
- at: insertPosition,
206
- ...options
207
- }
208
- });
209
- }
210
-
211
198
  // src/sync/create-elements.ts
212
199
  import { undoable } from "@elementor/editor-v1-adapters";
213
200
  import { __ } from "@wordpress/i18n";
214
201
 
215
202
  // src/sync/delete-element.ts
216
203
  import { __privateRunCommand as runCommand2 } from "@elementor/editor-v1-adapters";
217
- function deleteElement({ elementId, options = {} }) {
204
+ function deleteElement({
205
+ elementId,
206
+ options = {}
207
+ }) {
218
208
  const container = getContainer(elementId);
219
209
  if (!container) {
220
210
  throw new Error(`Element with ID "${elementId}" not found`);
221
211
  }
222
- runCommand2("document/elements/delete", {
212
+ return runCommand2("document/elements/delete", {
223
213
  container,
224
214
  options
225
215
  });
@@ -289,14 +279,50 @@ var createElements = ({
289
279
  return undoableCreate({ elements });
290
280
  };
291
281
 
282
+ // src/sync/drop-element.ts
283
+ import { __privateRunCommandSync as runCommandSync2 } from "@elementor/editor-v1-adapters";
284
+ function dropElement({ containerId, model, options }) {
285
+ const container = getContainer(containerId);
286
+ if (!container) {
287
+ throw new Error(`Container with ID "${containerId}" not found`);
288
+ }
289
+ return runCommandSync2("preview/drop", {
290
+ container,
291
+ model,
292
+ options
293
+ });
294
+ }
295
+
296
+ // src/sync/duplicate-element.ts
297
+ function duplicateElement({ elementId, options = {} }) {
298
+ const elementToDuplicate = getContainer(elementId);
299
+ if (!elementToDuplicate) {
300
+ throw new Error(`Element with ID "${elementId}" not found`);
301
+ }
302
+ if (!elementToDuplicate.parent) {
303
+ throw new Error(`Element with ID "${elementId}" has no parent container`);
304
+ }
305
+ const parentContainer = elementToDuplicate.parent;
306
+ const elementModel = elementToDuplicate.model.toJSON();
307
+ const currentIndex = elementToDuplicate.view?._index ?? 0;
308
+ const insertPosition = options.clone !== false ? currentIndex + 1 : void 0;
309
+ return createElement({
310
+ containerId: parentContainer.id,
311
+ model: elementModel,
312
+ options: {
313
+ at: insertPosition,
314
+ ...options
315
+ }
316
+ });
317
+ }
318
+
292
319
  // src/sync/duplicate-elements.ts
293
320
  import { undoable as undoable2 } from "@elementor/editor-v1-adapters";
294
321
  import { __ as __2 } from "@wordpress/i18n";
295
322
  var duplicateElements = ({
296
323
  elementIds,
297
324
  title,
298
- subtitle = __2("Item duplicated", "elementor"),
299
- onCreate
325
+ subtitle = __2("Item duplicated", "elementor")
300
326
  }) => {
301
327
  const undoableDuplicate = undoable2(
302
328
  {
@@ -319,7 +345,7 @@ var duplicateElements = ({
319
345
  }
320
346
  return acc;
321
347
  }, []);
322
- return { duplicatedElements: onCreate?.(duplicatedElements) ?? duplicatedElements };
348
+ return { duplicatedElements };
323
349
  },
324
350
  undo: (_, { duplicatedElements }) => {
325
351
  [...duplicatedElements].reverse().forEach(({ id }) => {
@@ -352,7 +378,7 @@ var duplicateElements = ({
352
378
  }
353
379
  return acc;
354
380
  }, []);
355
- return { duplicatedElements: onCreate?.(duplicatedElements) ?? duplicatedElements };
381
+ return { duplicatedElements };
356
382
  }
357
383
  },
358
384
  {
@@ -363,74 +389,23 @@ var duplicateElements = ({
363
389
  return undoableDuplicate({ elementIds });
364
390
  };
365
391
 
366
- // src/sync/remove-elements.ts
367
- import { undoable as undoable3 } from "@elementor/editor-v1-adapters";
368
- import { __ as __3 } from "@wordpress/i18n";
369
- var removeElements = ({
370
- elementIds,
371
- title,
372
- subtitle = __3("Item removed", "elementor")
373
- }) => {
374
- const undoableRemove = undoable3(
375
- {
376
- do: ({ elementIds: elementIdsParam }) => {
377
- const removedElements = [];
378
- elementIdsParam.forEach((elementId) => {
379
- const container = getContainer(elementId);
380
- if (container) {
381
- const model = container.model.toJSON();
382
- const parent = container.parent;
383
- const at = container.view?._index ?? 0;
384
- removedElements.push({
385
- elementId,
386
- model,
387
- parent: parent ?? null,
388
- at
389
- });
390
- }
391
- });
392
- elementIdsParam.forEach((elementId) => {
393
- deleteElement({
394
- elementId,
395
- options: { useHistory: false }
396
- });
397
- });
398
- return { elementIds: elementIdsParam, removedElements };
399
- },
400
- undo: (_, { removedElements }) => {
401
- [...removedElements].reverse().forEach(({ model, parent, at }) => {
402
- if (parent && model) {
403
- createElement({
404
- containerId: parent.id,
405
- model,
406
- options: { useHistory: false, at }
407
- });
408
- }
409
- });
410
- },
411
- redo: (_, { elementIds: originalElementIds, removedElements }) => {
412
- originalElementIds.forEach((elementId) => {
413
- deleteElement({
414
- elementId,
415
- options: { useHistory: false }
416
- });
417
- });
418
- return { elementIds: originalElementIds, removedElements };
419
- }
420
- },
421
- {
422
- title,
423
- subtitle
424
- }
425
- );
426
- return undoableRemove({ elementIds });
392
+ // src/sync/generate-element-id.ts
393
+ var generateElementId = () => {
394
+ const extendedWindow = window;
395
+ return extendedWindow.elementorCommon?.helpers?.getUniqueId?.() ?? `el-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
427
396
  };
428
397
 
429
- // src/sync/get-element-styles.ts
430
- var getElementStyles = (elementID) => {
431
- const container = getContainer(elementID);
432
- return container?.model.get("styles") || null;
433
- };
398
+ // src/sync/get-current-document-container.ts
399
+ function getCurrentDocumentContainer() {
400
+ const extendedWindow = window;
401
+ return extendedWindow.elementor?.documents?.getCurrent?.()?.container ?? null;
402
+ }
403
+
404
+ // src/sync/get-current-document-id.ts
405
+ function getCurrentDocumentId() {
406
+ const extendedWindow = window;
407
+ return extendedWindow.elementor?.documents?.getCurrentId?.() ?? null;
408
+ }
434
409
 
435
410
  // src/errors.ts
436
411
  import { createError } from "@elementor/utils";
@@ -450,9 +425,20 @@ var ElementLabelNotExistsError = createError({
450
425
  code: "element_label_not_exists",
451
426
  message: "Element label does not exist."
452
427
  });
428
+ var ElementParentNotFoundError = createError({
429
+ code: "element_parent_not_found",
430
+ message: "Element parent not found."
431
+ });
432
+ var ElementIndexNotFoundError = createError({
433
+ code: "element_index_not_found",
434
+ message: "Element index not found."
435
+ });
453
436
 
454
437
  // src/sync/get-element-label.ts
455
438
  function getElementLabel(elementId) {
439
+ if (!elementId) {
440
+ elementId = getSelectedElements()?.[0]?.id;
441
+ }
456
442
  const container = getContainer(elementId);
457
443
  const type = container?.model.get("widgetType") || container?.model.get("elType");
458
444
  if (!type) {
@@ -465,11 +451,11 @@ function getElementLabel(elementId) {
465
451
  return label;
466
452
  }
467
453
 
468
- // src/sync/get-current-document-container.ts
469
- function getCurrentDocumentContainer() {
470
- const extendedWindow = window;
471
- return extendedWindow.elementor?.documents?.getCurrent?.()?.container ?? null;
472
- }
454
+ // src/sync/get-element-styles.ts
455
+ var getElementStyles = (elementID) => {
456
+ const container = getContainer(elementID);
457
+ return container?.model.get("styles") || null;
458
+ };
473
459
 
474
460
  // src/sync/get-elements.ts
475
461
  function getElements(root) {
@@ -483,14 +469,239 @@ function getElements(root) {
483
469
  return [container, ...children];
484
470
  }
485
471
 
486
- // src/sync/get-current-document-id.ts
487
- function getCurrentDocumentId() {
488
- const extendedWindow = window;
489
- return extendedWindow.elementor?.documents?.getCurrentId?.() ?? null;
472
+ // src/sync/move-element.ts
473
+ function moveElement({ elementId, targetContainerId, options = {} }) {
474
+ const container = getContainer(elementId);
475
+ const target = getContainer(targetContainerId);
476
+ if (!container) {
477
+ throw new Error(`Element with ID "${elementId}" not found`);
478
+ }
479
+ if (!target) {
480
+ throw new Error(`Target container with ID "${targetContainerId}" not found`);
481
+ }
482
+ const modelToRecreate = container.model.toJSON();
483
+ deleteElement({
484
+ elementId,
485
+ // prevent inner history from being created
486
+ options: { ...options, useHistory: false }
487
+ });
488
+ const newContainer = createElement({
489
+ containerId: targetContainerId,
490
+ model: modelToRecreate,
491
+ // prevent inner history from being created
492
+ options: { edit: false, ...options, useHistory: false }
493
+ });
494
+ return newContainer;
495
+ }
496
+
497
+ // src/sync/move-elements.ts
498
+ import { undoable as undoable3 } from "@elementor/editor-v1-adapters";
499
+ import { __ as __3 } from "@wordpress/i18n";
500
+ var moveElements = ({
501
+ moves: movesToMake,
502
+ title,
503
+ subtitle = __3("Elements moved", "elementor"),
504
+ onMoveElements,
505
+ onRestoreElements
506
+ }) => {
507
+ const undoableMove = undoable3(
508
+ {
509
+ do: ({ moves }) => {
510
+ const movedElements = [];
511
+ onMoveElements?.();
512
+ moves.forEach((move) => {
513
+ const { elementId } = move;
514
+ const sourceContainer = getContainer(elementId);
515
+ if (!sourceContainer) {
516
+ throw new Error(`Element with ID "${elementId}" not found`);
517
+ }
518
+ const originalContainerId = sourceContainer.parent?.id || "";
519
+ const originalIndex = sourceContainer.parent?.children?.indexOf(sourceContainer) ?? -1;
520
+ const originalPosition = {
521
+ elementId,
522
+ originalContainerId,
523
+ originalIndex
524
+ };
525
+ const element = moveElement({
526
+ ...move,
527
+ options: { ...move.options, useHistory: false }
528
+ });
529
+ movedElements.push({
530
+ elementId,
531
+ originalPosition,
532
+ move,
533
+ element
534
+ });
535
+ });
536
+ return { movedElements };
537
+ },
538
+ undo: (_, { movedElements }) => {
539
+ onRestoreElements?.();
540
+ [...movedElements].reverse().forEach(({ originalPosition }) => {
541
+ const { elementId, originalContainerId, originalIndex } = originalPosition;
542
+ moveElement({
543
+ elementId,
544
+ targetContainerId: originalContainerId,
545
+ options: {
546
+ useHistory: false,
547
+ at: originalIndex >= 0 ? originalIndex : void 0
548
+ }
549
+ });
550
+ });
551
+ },
552
+ redo: (_, { movedElements }) => {
553
+ const newMovedElements = [];
554
+ onMoveElements?.();
555
+ movedElements.forEach(({ move, originalPosition }) => {
556
+ const element = moveElement({
557
+ ...move,
558
+ options: { ...move.options, useHistory: false }
559
+ });
560
+ newMovedElements.push({
561
+ elementId: move.elementId,
562
+ originalPosition,
563
+ move,
564
+ element
565
+ });
566
+ });
567
+ return { movedElements: newMovedElements };
568
+ }
569
+ },
570
+ {
571
+ title,
572
+ subtitle
573
+ }
574
+ );
575
+ return undoableMove({ moves: movesToMake });
576
+ };
577
+
578
+ // src/sync/remove-elements.ts
579
+ import { undoable as undoable4 } from "@elementor/editor-v1-adapters";
580
+ import { __ as __4 } from "@wordpress/i18n";
581
+ var removeElements = ({
582
+ elementIds,
583
+ title,
584
+ subtitle = __4("Item removed", "elementor"),
585
+ onRemoveElements,
586
+ onRestoreElements
587
+ }) => {
588
+ const undoableRemove = undoable4(
589
+ {
590
+ do: ({ elementIds: elementIdsParam }) => {
591
+ const removedElements = [];
592
+ elementIdsParam.forEach((elementId) => {
593
+ const container = getContainer(elementId);
594
+ if (container) {
595
+ const model = container.model.toJSON();
596
+ const parent = container.parent;
597
+ const at = container.view?._index ?? 0;
598
+ removedElements.push({
599
+ elementId,
600
+ model,
601
+ parent: parent ?? null,
602
+ at
603
+ });
604
+ }
605
+ });
606
+ onRemoveElements?.();
607
+ elementIdsParam.forEach((elementId) => {
608
+ deleteElement({
609
+ elementId,
610
+ options: { useHistory: false }
611
+ });
612
+ });
613
+ return { elementIds: elementIdsParam, removedElements };
614
+ },
615
+ undo: (_, { removedElements }) => {
616
+ onRestoreElements?.();
617
+ [...removedElements].reverse().forEach(({ model, parent, at }) => {
618
+ if (parent && model) {
619
+ createElement({
620
+ containerId: parent.id,
621
+ model,
622
+ options: { useHistory: false, at }
623
+ });
624
+ }
625
+ });
626
+ },
627
+ redo: (_, { elementIds: originalElementIds, removedElements }) => {
628
+ onRemoveElements?.();
629
+ originalElementIds.forEach((elementId) => {
630
+ deleteElement({
631
+ elementId,
632
+ options: { useHistory: false }
633
+ });
634
+ });
635
+ return { elementIds: originalElementIds, removedElements };
636
+ }
637
+ },
638
+ {
639
+ title,
640
+ subtitle
641
+ }
642
+ );
643
+ return undoableRemove({ elementIds });
644
+ };
645
+
646
+ // src/sync/replace-element.ts
647
+ var replaceElement = ({ currentElement, newElement, withHistory = true }) => {
648
+ const { containerId, index } = getNewElementLocation(currentElement, newElement);
649
+ createElement({
650
+ containerId,
651
+ model: newElement,
652
+ options: { at: index, useHistory: withHistory }
653
+ });
654
+ deleteElement({ elementId: currentElement.id, options: { useHistory: withHistory } });
655
+ };
656
+ function getNewElementLocation(currentElement, newElement) {
657
+ let location;
658
+ const currentElementContainer = getContainer(currentElement.id);
659
+ if (!currentElementContainer) {
660
+ throw new ElementNotFoundError({ context: { elementId: currentElement.id } });
661
+ }
662
+ const parent = currentElementContainer.parent;
663
+ if (!parent) {
664
+ throw new ElementParentNotFoundError({ context: { elementId: currentElement.id } });
665
+ }
666
+ const elementIndex = currentElementContainer.view?._index ?? 0;
667
+ if (elementIndex === void 0 || elementIndex === -1) {
668
+ throw new ElementIndexNotFoundError({ context: { elementId: currentElement.id } });
669
+ }
670
+ location = { containerId: parent.id, index: elementIndex };
671
+ if (parent.id === "document" && newElement.elType === "widget") {
672
+ location = createWrapperForWidget(parent.id, elementIndex);
673
+ }
674
+ return location;
675
+ }
676
+ function createWrapperForWidget(parentId, elementIndex) {
677
+ const container = createElement({
678
+ containerId: parentId,
679
+ model: { elType: "container" },
680
+ options: { at: elementIndex, useHistory: false }
681
+ });
682
+ return { containerId: container.id, index: 0 };
683
+ }
684
+
685
+ // src/sync/update-element-editor-settings.ts
686
+ import { __privateRunCommandSync as runCommandSync3 } from "@elementor/editor-v1-adapters";
687
+ var updateElementEditorSettings = ({
688
+ elementId,
689
+ settings
690
+ }) => {
691
+ const element = getContainer(elementId);
692
+ if (!element) {
693
+ throw new Error(`Element with id ${elementId} not found`);
694
+ }
695
+ const editorSettings = element.model.get("editor_settings") ?? {};
696
+ element.model.set("editor_settings", { ...editorSettings, ...settings });
697
+ setDocumentModifiedStatus(true);
698
+ };
699
+ function setDocumentModifiedStatus(status) {
700
+ runCommandSync3("document/save/set-is-modified", { status }, { internal: true });
490
701
  }
491
702
 
492
703
  // src/sync/update-element-settings.ts
493
- import { __privateRunCommandSync as runCommandSync2 } from "@elementor/editor-v1-adapters";
704
+ import { __privateRunCommandSync as runCommandSync4 } from "@elementor/editor-v1-adapters";
494
705
  var updateElementSettings = ({ id, props, withHistory = true }) => {
495
706
  const container = getContainer(id);
496
707
  const args = {
@@ -498,45 +709,105 @@ var updateElementSettings = ({ id, props, withHistory = true }) => {
498
709
  settings: { ...props }
499
710
  };
500
711
  if (withHistory) {
501
- runCommandSync2("document/elements/settings", args);
712
+ runCommandSync4("document/elements/settings", args);
502
713
  } else {
503
- runCommandSync2("document/elements/set-settings", args, { internal: true });
714
+ runCommandSync4("document/elements/set-settings", args, { internal: true });
504
715
  }
505
716
  };
506
717
 
507
- // src/sync/generate-element-id.ts
508
- var generateElementId = () => {
509
- const extendedWindow = window;
510
- return extendedWindow.elementorCommon?.helpers?.getUniqueId?.() ?? `el-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
511
- };
512
-
513
- // src/sync/replace-element.ts
514
- var replaceElement = ({ currentElement, newElement, withHistory = true }) => {
515
- const parent = getContainer(currentElement.id)?.parent;
516
- if (!parent) {
517
- throw new Error(`Parent not found for element ${currentElement.id}. Cannot replace element.`);
718
+ // src/link-restriction.ts
719
+ function getLinkInLinkRestriction(elementId) {
720
+ const anchoredDescendantId = getAnchoredDescendantId(elementId);
721
+ if (anchoredDescendantId) {
722
+ return {
723
+ shouldRestrict: true,
724
+ reason: "descendant",
725
+ elementId: anchoredDescendantId
726
+ };
518
727
  }
519
- const elementIndex = parent.children?.findIndex((child) => child.id === currentElement.id);
520
- if (elementIndex === void 0 || elementIndex === -1) {
521
- throw new Error(`Element ${currentElement.id} not found in parent container. Cannot replace element.`);
728
+ const ancestor = getAnchoredAncestorId(elementId);
729
+ if (ancestor) {
730
+ return {
731
+ shouldRestrict: true,
732
+ reason: "ancestor",
733
+ elementId: ancestor
734
+ };
522
735
  }
523
- createElement({
524
- containerId: parent.id,
525
- model: newElement,
526
- options: { at: elementIndex, useHistory: withHistory }
527
- });
528
- deleteElement({ elementId: currentElement.id, options: { useHistory: withHistory } });
529
- };
736
+ return {
737
+ shouldRestrict: false
738
+ };
739
+ }
740
+ function getAnchoredDescendantId(elementId) {
741
+ const element = getElementDOM(elementId);
742
+ if (!element) {
743
+ return null;
744
+ }
745
+ for (const childAnchorElement of Array.from(element.querySelectorAll("a"))) {
746
+ const childElementId = findElementIdOf(childAnchorElement);
747
+ if (childElementId !== elementId) {
748
+ return childElementId;
749
+ }
750
+ }
751
+ return null;
752
+ }
753
+ function getAnchoredAncestorId(elementId) {
754
+ const element = getElementDOM(elementId);
755
+ if (!element || element.parentElement === null) {
756
+ return null;
757
+ }
758
+ const parentAnchor = element.parentElement.closest("a");
759
+ return parentAnchor ? findElementIdOf(parentAnchor) : null;
760
+ }
761
+ function isElementAnchored(elementId) {
762
+ const element = getElementDOM(elementId);
763
+ if (!element) {
764
+ return false;
765
+ }
766
+ if (isAnchorTag(element.tagName)) {
767
+ return true;
768
+ }
769
+ return doesElementContainAnchor(element);
770
+ }
771
+ function doesElementContainAnchor(element) {
772
+ for (const child of element.children) {
773
+ if (isElementorElement(child)) {
774
+ continue;
775
+ }
776
+ if (isAnchorTag(child.tagName)) {
777
+ return true;
778
+ }
779
+ if (doesElementContainAnchor(child)) {
780
+ return true;
781
+ }
782
+ }
783
+ return false;
784
+ }
785
+ function findElementIdOf(element) {
786
+ return element.closest("[data-id]")?.dataset.id || null;
787
+ }
788
+ function getElementDOM(id) {
789
+ try {
790
+ return getContainer(id)?.view?.el || null;
791
+ } catch {
792
+ return null;
793
+ }
794
+ }
795
+ function isAnchorTag(tagName) {
796
+ return tagName.toLowerCase() === "a";
797
+ }
798
+ function isElementorElement(element) {
799
+ return element.hasAttribute("data-id");
800
+ }
530
801
 
531
802
  // src/styles/consts.ts
532
- import { commandEndEvent as commandEndEvent6, windowEvent } from "@elementor/editor-v1-adapters";
803
+ import { commandEndEvent as commandEndEvent5, windowEvent as windowEvent2 } from "@elementor/editor-v1-adapters";
533
804
  var ELEMENT_STYLE_CHANGE_EVENT = "elementor/editor-v2/editor-elements/style";
534
805
  var styleRerenderEvents = [
535
- commandEndEvent6("document/elements/create"),
536
- commandEndEvent6("document/elements/duplicate"),
537
- commandEndEvent6("document/elements/import"),
538
- commandEndEvent6("document/elements/paste"),
539
- windowEvent(ELEMENT_STYLE_CHANGE_EVENT)
806
+ commandEndEvent5("document/elements/create"),
807
+ commandEndEvent5("document/elements/duplicate"),
808
+ commandEndEvent5("document/elements/import"),
809
+ commandEndEvent5("document/elements/paste"),
810
+ windowEvent2(ELEMENT_STYLE_CHANGE_EVENT)
540
811
  ];
541
812
 
542
813
  // src/styles/create-element-style.ts
@@ -547,7 +818,7 @@ import {
547
818
 
548
819
  // src/styles/mutate-element-styles.ts
549
820
  import { classesPropTypeUtil } from "@elementor/editor-props";
550
- import { __privateRunCommandSync as runCommandSync3 } from "@elementor/editor-v1-adapters";
821
+ import { __privateRunCommandSync as runCommandSync5 } from "@elementor/editor-v1-adapters";
551
822
  function mutateElementStyles(elementId, mutator) {
552
823
  const container = getContainer(elementId);
553
824
  if (!container) {
@@ -603,7 +874,7 @@ function getClassesProps(container) {
603
874
  }
604
875
  function notifyChanges() {
605
876
  dispatchChangeEvent();
606
- runCommandSync3("document/save/set-is-modified", { status: true }, { internal: true });
877
+ runCommandSync5("document/save/set-is-modified", { status: true }, { internal: true });
607
878
  }
608
879
  function dispatchChangeEvent() {
609
880
  window.dispatchEvent(new CustomEvent(ELEMENT_STYLE_CHANGE_EVENT));
@@ -655,6 +926,14 @@ function shouldCreateNewLocalStyle(payload) {
655
926
  return !payload?.styleId && !payload?.provider;
656
927
  }
657
928
 
929
+ // src/styles/delete-element-style.ts
930
+ function deleteElementStyle(elementId, styleId) {
931
+ mutateElementStyles(elementId, (styles) => {
932
+ delete styles[styleId];
933
+ return styles;
934
+ });
935
+ }
936
+
658
937
  // src/styles/update-element-style.ts
659
938
  import { mergeProps } from "@elementor/editor-props";
660
939
  import { getVariantByMeta } from "@elementor/editor-styles";
@@ -676,96 +955,663 @@ function updateElementStyle(args) {
676
955
  });
677
956
  }
678
957
 
679
- // src/styles/delete-element-style.ts
680
- function deleteElementStyle(elementId, styleId) {
681
- mutateElementStyles(elementId, (styles) => {
682
- delete styles[styleId];
683
- return styles;
958
+ // src/hooks/use-element-interactions.ts
959
+ import { useState } from "react";
960
+ import { __privateUseListenTo as useListenTo6, windowEvent as windowEvent3 } from "@elementor/editor-v1-adapters";
961
+
962
+ // src/sync/get-element-interactions.ts
963
+ function getElementInteractions(elementId) {
964
+ const container = getContainer(elementId);
965
+ const interactions = container?.model?.get("interactions");
966
+ if (typeof interactions === "string") {
967
+ return JSON.parse(interactions);
968
+ }
969
+ return interactions;
970
+ }
971
+
972
+ // src/hooks/use-element-interactions.ts
973
+ var useElementInteractions = (elementId) => {
974
+ const [interactions, setInteractions] = useState(() => {
975
+ const initial = getElementInteractions(elementId);
976
+ return initial ?? { version: 1, items: [] };
684
977
  });
978
+ useListenTo6(
979
+ windowEvent3("elementor/element/update_interactions"),
980
+ () => {
981
+ const newInteractions = getElementInteractions(elementId);
982
+ setInteractions(newInteractions ?? { version: 1, items: [] });
983
+ },
984
+ [elementId]
985
+ );
986
+ return interactions;
987
+ };
988
+
989
+ // src/sync/update-element-interactions.ts
990
+ import { __privateRunCommandSync as runCommandSync6 } from "@elementor/editor-v1-adapters";
991
+ var updateElementInteractions = ({
992
+ elementId,
993
+ interactions
994
+ }) => {
995
+ const element = getContainer(elementId);
996
+ if (!element) {
997
+ throw new Error(`Element with id ${elementId} not found`);
998
+ }
999
+ element.model.set("interactions", interactions);
1000
+ window.dispatchEvent(new CustomEvent("elementor/element/update_interactions"));
1001
+ setDocumentModifiedStatus2(true);
1002
+ };
1003
+ var playElementInteractions = (elementId, animationId) => {
1004
+ window.top?.dispatchEvent(new CustomEvent("atomic/play_interactions", { detail: { elementId, animationId } }));
1005
+ };
1006
+ function setDocumentModifiedStatus2(status) {
1007
+ runCommandSync6("document/save/set-is-modified", { status }, { internal: true });
685
1008
  }
686
1009
 
687
- // src/link-restriction.ts
688
- function getLinkInLinkRestriction(elementId) {
689
- const anchoredDescendantId = getAnchoredDescendantId(elementId);
690
- if (anchoredDescendantId) {
691
- return {
692
- shouldRestrict: true,
693
- reason: "descendant",
694
- elementId: anchoredDescendantId
695
- };
1010
+ // src/mcp/index.ts
1011
+ import { getMCPByDomain as getMCPByDomain2 } from "@elementor/editor-mcp";
1012
+
1013
+ // src/mcp/elements-tool.ts
1014
+ import { getMCPByDomain } from "@elementor/editor-mcp";
1015
+ import { z } from "@elementor/schema";
1016
+
1017
+ // src/mcp/handlers/create-element.ts
1018
+ function handleCreateElement({
1019
+ elementType,
1020
+ containerId,
1021
+ props = {},
1022
+ styles
1023
+ }) {
1024
+ let container = containerId === "document" ? getCurrentDocumentContainer() : getContainer(containerId);
1025
+ if (!container) {
1026
+ if (containerId === "document") {
1027
+ throw new Error("Document container not found. Please ensure the editor is initialized.");
1028
+ }
1029
+ throw new Error(`Container with ID "${containerId}" not found`);
696
1030
  }
697
- const ancestor = getAnchoredAncestorId(elementId);
698
- if (ancestor) {
699
- return {
700
- shouldRestrict: true,
701
- reason: "ancestor",
702
- elementId: ancestor
1031
+ const containerElType = container.model.get("elType");
1032
+ const isDocument = container.id === "document" || containerElType === "document";
1033
+ if (isDocument) {
1034
+ const containerModel = {
1035
+ elType: "e-div-block"
703
1036
  };
1037
+ const createdContainer = createElement({
1038
+ containerId: container.id,
1039
+ model: containerModel,
1040
+ options: { useHistory: true }
1041
+ });
1042
+ createElementStyle({
1043
+ elementId: createdContainer.id,
1044
+ classesProp: "classes",
1045
+ label: "local",
1046
+ meta: { breakpoint: "desktop", state: null },
1047
+ props: {
1048
+ display: { $$type: "string", value: "flex" },
1049
+ "flex-direction": { $$type: "string", value: "row" },
1050
+ "flex-wrap": { $$type: "string", value: "wrap" }
1051
+ }
1052
+ });
1053
+ container = getContainer(createdContainer.id);
1054
+ if (!container) {
1055
+ throw new Error("Failed to create container for widget. Cannot create widgets directly in the document.");
1056
+ }
1057
+ }
1058
+ const actualContainerId = container.id;
1059
+ const elementTypeData = getElementType(elementType);
1060
+ if (!elementTypeData) {
1061
+ throw new Error(`Element type "${elementType}" not found or is not atomic`);
1062
+ }
1063
+ const model = {
1064
+ widgetType: elementType,
1065
+ elType: "widget",
1066
+ settings: props
1067
+ };
1068
+ const createdElement = createElement({
1069
+ containerId: actualContainerId,
1070
+ model,
1071
+ options: { useHistory: true }
1072
+ });
1073
+ if (styles) {
1074
+ createElementStyle({
1075
+ elementId: createdElement.id,
1076
+ classesProp: "classes",
1077
+ label: "local",
1078
+ meta: { breakpoint: "desktop", state: null },
1079
+ props: styles
1080
+ });
704
1081
  }
705
1082
  return {
706
- shouldRestrict: false
1083
+ elementId: createdElement.id,
1084
+ type: elementType
707
1085
  };
708
1086
  }
709
- function getAnchoredDescendantId(elementId) {
710
- const element = getElementDOM(elementId);
711
- if (!element) {
1087
+
1088
+ // src/mcp/handlers/common-style-utils.ts
1089
+ var VALID_BREAKPOINTS = [
1090
+ "widescreen",
1091
+ "desktop",
1092
+ "laptop",
1093
+ "tablet_extra",
1094
+ "tablet",
1095
+ "mobile_extra",
1096
+ "mobile"
1097
+ ];
1098
+ function resolveBreakpointId(breakpoint) {
1099
+ if (breakpoint === null) {
712
1100
  return null;
713
1101
  }
714
- for (const childAnchorElement of Array.from(element.querySelectorAll("a"))) {
715
- const childElementId = findElementIdOf(childAnchorElement);
716
- if (childElementId !== elementId) {
717
- return childElementId;
718
- }
1102
+ if (VALID_BREAKPOINTS.includes(breakpoint)) {
1103
+ return breakpoint;
719
1104
  }
720
- return null;
1105
+ return "desktop";
721
1106
  }
722
- function getAnchoredAncestorId(elementId) {
723
- const element = getElementDOM(elementId);
724
- if (!element || element.parentElement === null) {
725
- return null;
1107
+
1108
+ // src/mcp/handlers/create-style.ts
1109
+ function handleCreateStyle({
1110
+ elementId,
1111
+ styleId,
1112
+ classesProp = "classes",
1113
+ label = "local",
1114
+ styles,
1115
+ breakpoint = "desktop",
1116
+ state = null,
1117
+ customCss = null
1118
+ }) {
1119
+ const resolvedBreakpoint = resolveBreakpointId(breakpoint);
1120
+ const resolvedState = state === null || state === void 0 ? null : state;
1121
+ const createdStyleId = createElementStyle({
1122
+ styleId,
1123
+ elementId,
1124
+ classesProp,
1125
+ label,
1126
+ meta: { breakpoint: resolvedBreakpoint, state: resolvedState },
1127
+ props: styles,
1128
+ custom_css: customCss
1129
+ });
1130
+ return { styleId: createdStyleId };
1131
+ }
1132
+
1133
+ // src/mcp/handlers/delete-element.ts
1134
+ function handleDeleteElement(elementId) {
1135
+ const container = getContainer(elementId);
1136
+ if (!container) {
1137
+ throw new Error(`Element with ID "${elementId}" not found`);
726
1138
  }
727
- const parentAnchor = element.parentElement.closest("a");
728
- return parentAnchor ? findElementIdOf(parentAnchor) : null;
1139
+ deleteElement({
1140
+ elementId,
1141
+ options: { useHistory: true }
1142
+ });
1143
+ return { success: true };
729
1144
  }
730
- function isElementAnchored(elementId) {
731
- const element = getElementDOM(elementId);
732
- if (!element) {
733
- return false;
1145
+
1146
+ // src/mcp/handlers/delete-style.ts
1147
+ function handleDeleteStyle({ elementId, styleId }) {
1148
+ const elementStyles = getElementStyles(elementId);
1149
+ if (!elementStyles) {
1150
+ throw new Error(`Element with ID "${elementId}" has no styles.`);
734
1151
  }
735
- if (isAnchorTag(element.tagName)) {
736
- return true;
1152
+ const resolvedStyleId = styleId || Object.keys(elementStyles)[0];
1153
+ if (!resolvedStyleId) {
1154
+ throw new Error(`Element with ID "${elementId}" has no styles to delete.`);
737
1155
  }
738
- return doesElementContainAnchor(element);
1156
+ deleteElementStyle(elementId, resolvedStyleId);
1157
+ return { success: true };
739
1158
  }
740
- function doesElementContainAnchor(element) {
741
- for (const child of element.children) {
742
- if (isElementorElement(child)) {
743
- continue;
744
- }
745
- if (isAnchorTag(child.tagName)) {
746
- return true;
1159
+
1160
+ // src/mcp/handlers/deselect-element.ts
1161
+ import { __privateRunCommand as runCommand3 } from "@elementor/editor-v1-adapters";
1162
+ function handleDeselectElement(elementId) {
1163
+ const container = getContainer(elementId);
1164
+ if (!container) {
1165
+ throw new Error(`Element with ID "${elementId}" not found`);
1166
+ }
1167
+ runCommand3("document/elements/deselect", { container });
1168
+ return { success: true };
1169
+ }
1170
+ function handleDeselectAllElements() {
1171
+ runCommand3("document/elements/deselect-all", {});
1172
+ return { success: true };
1173
+ }
1174
+
1175
+ // src/mcp/handlers/duplicate-element.ts
1176
+ function handleDuplicateElement(elementId) {
1177
+ const container = getContainer(elementId);
1178
+ if (!container) {
1179
+ throw new Error(`Element with ID "${elementId}" not found`);
1180
+ }
1181
+ const duplicatedElement = duplicateElement({
1182
+ elementId,
1183
+ options: { useHistory: true }
1184
+ });
1185
+ const type = duplicatedElement.model.get("widgetType") || duplicatedElement.model.get("elType") || "";
1186
+ return {
1187
+ elementId: duplicatedElement.id,
1188
+ type
1189
+ };
1190
+ }
1191
+
1192
+ // src/mcp/handlers/get-element-props.ts
1193
+ function handleGetElementProps(elementId) {
1194
+ const container = getContainer(elementId);
1195
+ if (!container) {
1196
+ throw new Error(`Element with ID "${elementId}" not found`);
1197
+ }
1198
+ const type = container.model.get("widgetType") || container.model.get("elType");
1199
+ if (!type) {
1200
+ throw new Error(`Element with ID "${elementId}" has no type`);
1201
+ }
1202
+ const elementType = getElementType(type);
1203
+ if (!elementType) {
1204
+ throw new Error(`Element type "${type}" is not atomic`);
1205
+ }
1206
+ const propsSchema = elementType.propsSchema;
1207
+ const propKeys = Object.keys(propsSchema);
1208
+ return getElementSettings(elementId, propKeys);
1209
+ }
1210
+
1211
+ // src/mcp/handlers/get-element-schema.ts
1212
+ import { getStylesSchema } from "@elementor/editor-styles";
1213
+ function handleGetElementSchema(elementType) {
1214
+ const elementTypeData = getElementType(elementType);
1215
+ if (!elementTypeData) {
1216
+ throw new Error(`Element type "${elementType}" not found or is not atomic`);
1217
+ }
1218
+ return { ...elementTypeData, stylesSchema: getStylesSchema() };
1219
+ }
1220
+
1221
+ // src/mcp/handlers/get-selected.ts
1222
+ function handleGetSelected() {
1223
+ return getSelectedElements();
1224
+ }
1225
+
1226
+ // src/mcp/handlers/get-styles.ts
1227
+ function handleGetStyles(elementId) {
1228
+ const styles = getElementStyles(elementId);
1229
+ if (!styles) {
1230
+ return null;
1231
+ }
1232
+ return Object.fromEntries(
1233
+ Object.entries(styles).map(([id, style]) => [
1234
+ id,
1235
+ {
1236
+ id: style.id,
1237
+ label: style.label,
1238
+ type: style.type,
1239
+ variants: style.variants.map((variant) => ({
1240
+ meta: variant.meta,
1241
+ props: variant.props,
1242
+ custom_css: variant.custom_css
1243
+ }))
1244
+ }
1245
+ ])
1246
+ );
1247
+ }
1248
+
1249
+ // src/mcp/handlers/list-available-types.ts
1250
+ function handleListAvailableTypes() {
1251
+ const widgetsCache = getWidgetsCache();
1252
+ if (!widgetsCache) {
1253
+ return [];
1254
+ }
1255
+ const availableTypes = [];
1256
+ Object.entries(widgetsCache).forEach(([type, config]) => {
1257
+ if (config?.atomic_controls && config?.atomic_props_schema) {
1258
+ availableTypes.push({
1259
+ type,
1260
+ title: config.title || type
1261
+ });
747
1262
  }
748
- if (doesElementContainAnchor(child)) {
749
- return true;
1263
+ });
1264
+ return availableTypes;
1265
+ }
1266
+
1267
+ // src/mcp/handlers/move-element.ts
1268
+ function handleMoveElement({
1269
+ elementId,
1270
+ targetContainerId
1271
+ }) {
1272
+ const container = getContainer(elementId);
1273
+ if (!container) {
1274
+ throw new Error(`Element with ID "${elementId}" not found`);
1275
+ }
1276
+ const targetContainer = getContainer(targetContainerId);
1277
+ if (!targetContainer) {
1278
+ throw new Error(`Target container with ID "${targetContainerId}" not found`);
1279
+ }
1280
+ moveElement({
1281
+ elementId,
1282
+ targetContainerId,
1283
+ options: { useHistory: true }
1284
+ });
1285
+ return { success: true };
1286
+ }
1287
+
1288
+ // src/mcp/handlers/select-element.ts
1289
+ function handleSelectElement(elementId) {
1290
+ const container = getContainer(elementId);
1291
+ if (!container) {
1292
+ throw new Error(`Element with ID "${elementId}" not found`);
1293
+ }
1294
+ selectElement(elementId);
1295
+ return { success: true };
1296
+ }
1297
+ function handleSelectMultipleElements(elementIds) {
1298
+ elementIds.forEach((elementId) => {
1299
+ const container = getContainer(elementId);
1300
+ if (container) {
1301
+ selectElement(elementId);
750
1302
  }
1303
+ });
1304
+ return { success: true };
1305
+ }
1306
+
1307
+ // src/mcp/handlers/update-props.ts
1308
+ function handleUpdateProps({ elementId, props }) {
1309
+ const container = getContainer(elementId);
1310
+ if (!container) {
1311
+ throw new Error(`Element with ID "${elementId}" not found`);
751
1312
  }
752
- return false;
1313
+ updateElementSettings({
1314
+ id: elementId,
1315
+ props,
1316
+ withHistory: true
1317
+ });
1318
+ return { success: true };
753
1319
  }
754
- function findElementIdOf(element) {
755
- return element.closest("[data-id]")?.dataset.id || null;
1320
+
1321
+ // src/mcp/handlers/update-styles.ts
1322
+ function handleUpdateStyles({
1323
+ elementId,
1324
+ styleId,
1325
+ styles,
1326
+ breakpoint = "desktop",
1327
+ state = null
1328
+ }) {
1329
+ const resolvedBreakpoint = resolveBreakpointId(breakpoint);
1330
+ const resolvedState = state === null || state === void 0 ? null : state;
1331
+ const elementStyles = getElementStyles(elementId);
1332
+ if (!elementStyles) {
1333
+ throw new Error(`Element with ID "${elementId}" has no styles. Create a style first.`);
1334
+ }
1335
+ const resolvedStyleId = styleId || Object.keys(elementStyles)[0];
1336
+ if (!resolvedStyleId) {
1337
+ throw new Error(`Element with ID "${elementId}" has no styles. Create a style first.`);
1338
+ }
1339
+ updateElementStyle({
1340
+ elementId,
1341
+ styleId: resolvedStyleId,
1342
+ meta: { breakpoint: resolvedBreakpoint, state: resolvedState },
1343
+ props: styles
1344
+ });
1345
+ return { success: true };
756
1346
  }
757
- function getElementDOM(id) {
1347
+
1348
+ // src/mcp/elements-tool.ts
1349
+ var actionEnum = z.enum([
1350
+ "get-element-schema",
1351
+ "get-element-props",
1352
+ "create-element",
1353
+ "update-props",
1354
+ "create-style",
1355
+ "get-styles",
1356
+ "update-styles",
1357
+ "delete-style",
1358
+ "delete",
1359
+ "duplicate",
1360
+ "move",
1361
+ "select",
1362
+ "deselect",
1363
+ "deselect-all",
1364
+ "get-selected",
1365
+ "list-available-types"
1366
+ ]);
1367
+ var schema = {
1368
+ action: actionEnum.describe("The element operation to perform."),
1369
+ elementId: z.string().optional().describe("The ID of the target element"),
1370
+ elementIds: z.array(z.string()).optional().describe("Array of element IDs for multi-element operations"),
1371
+ elementType: z.string().optional().describe(
1372
+ "The type of element to create. Must be an atomic element type (required for create-element and get-element-schema actions)"
1373
+ ),
1374
+ props: z.record(z.any()).optional().describe("Props object for creating or updating an element. Must match the element type's propsSchema."),
1375
+ containerId: z.string().optional().describe(
1376
+ 'Parent container ID for element creation or move operations. Use "document" if parent is the document root.'
1377
+ ),
1378
+ targetContainerId: z.string().optional().describe("Target container ID for move operations"),
1379
+ styles: z.record(z.any()).optional().describe(
1380
+ "Styles object for creating or updating element styles. Must match the element type's stylesSchema."
1381
+ ),
1382
+ styleId: z.string().optional().describe(
1383
+ "Style definition ID for style operations. If not provided, the first available style will be used (for update/delete)."
1384
+ ),
1385
+ breakpoint: z.string().optional().describe('Breakpoint for style operations (e.g., "desktop", "tablet", "mobile"). Defaults to "desktop".'),
1386
+ state: z.string().optional().describe('State for style operations (e.g., "hover", "active", or null). Defaults to null.'),
1387
+ classesProp: z.string().optional().describe('Classes property name for create-style action. Defaults to "classes".'),
1388
+ label: z.string().optional().describe('Label for create-style action. Defaults to "local".'),
1389
+ custom_css: z.object({ raw: z.string() }).optional().describe("Custom CSS object with raw CSS string for create-style action.")
1390
+ };
1391
+ function routeAction(params) {
758
1392
  try {
759
- return getContainer(id)?.view?.el || null;
760
- } catch {
761
- return null;
1393
+ switch (params.action) {
1394
+ case "get-element-schema":
1395
+ if (!params.elementType) {
1396
+ throw new Error("elementType is required for get-element-schema action");
1397
+ }
1398
+ return handleGetElementSchema(params.elementType);
1399
+ case "get-element-props":
1400
+ if (!params.elementId) {
1401
+ throw new Error("elementId is required for get-element-props action");
1402
+ }
1403
+ return handleGetElementProps(params.elementId);
1404
+ case "create-element":
1405
+ if (!params.elementType) {
1406
+ throw new Error("elementType is required for create-element action");
1407
+ }
1408
+ if (!params.containerId) {
1409
+ throw new Error("containerId is required for create-element action");
1410
+ }
1411
+ return handleCreateElement({
1412
+ elementType: params.elementType,
1413
+ containerId: params.containerId,
1414
+ props: params.props,
1415
+ styles: params.styles
1416
+ });
1417
+ case "update-props":
1418
+ if (!params.elementId) {
1419
+ throw new Error("elementId is required for update-props action");
1420
+ }
1421
+ if (!params.props) {
1422
+ throw new Error("props is required for update-props action");
1423
+ }
1424
+ return handleUpdateProps({
1425
+ elementId: params.elementId,
1426
+ props: params.props
1427
+ });
1428
+ case "create-style":
1429
+ if (!params.elementId) {
1430
+ throw new Error("elementId is required for create-style action");
1431
+ }
1432
+ if (!params.styles) {
1433
+ throw new Error("styles is required for create-style action");
1434
+ }
1435
+ return handleCreateStyle({
1436
+ elementId: params.elementId,
1437
+ styleId: params.styleId,
1438
+ classesProp: params.classesProp,
1439
+ label: params.label,
1440
+ styles: params.styles,
1441
+ breakpoint: params.breakpoint,
1442
+ state: params.state,
1443
+ customCss: params.custom_css
1444
+ });
1445
+ case "get-styles":
1446
+ if (!params.elementId) {
1447
+ throw new Error("elementId is required for get-styles action");
1448
+ }
1449
+ return handleGetStyles(params.elementId);
1450
+ case "update-styles":
1451
+ if (!params.elementId) {
1452
+ throw new Error("elementId is required for update-styles action");
1453
+ }
1454
+ if (!params.styles) {
1455
+ throw new Error("styles is required for update-styles action");
1456
+ }
1457
+ return handleUpdateStyles({
1458
+ elementId: params.elementId,
1459
+ styleId: params.styleId,
1460
+ styles: params.styles,
1461
+ breakpoint: params.breakpoint,
1462
+ state: params.state
1463
+ });
1464
+ case "delete-style":
1465
+ if (!params.elementId) {
1466
+ throw new Error("elementId is required for delete-style action");
1467
+ }
1468
+ return handleDeleteStyle({
1469
+ elementId: params.elementId,
1470
+ styleId: params.styleId
1471
+ });
1472
+ case "delete":
1473
+ if (!params.elementId) {
1474
+ throw new Error("elementId is required for delete action");
1475
+ }
1476
+ return handleDeleteElement(params.elementId);
1477
+ case "duplicate":
1478
+ if (!params.elementId) {
1479
+ throw new Error("elementId is required for duplicate action");
1480
+ }
1481
+ return handleDuplicateElement(params.elementId);
1482
+ case "move":
1483
+ if (!params.elementId) {
1484
+ throw new Error("elementId is required for move action");
1485
+ }
1486
+ if (!params.targetContainerId) {
1487
+ throw new Error("targetContainerId is required for move action");
1488
+ }
1489
+ return handleMoveElement({
1490
+ elementId: params.elementId,
1491
+ targetContainerId: params.targetContainerId
1492
+ });
1493
+ case "select":
1494
+ if (params.elementIds && params.elementIds.length > 0) {
1495
+ return handleSelectMultipleElements(params.elementIds);
1496
+ }
1497
+ if (!params.elementId) {
1498
+ throw new Error("elementId or elementIds is required for select action");
1499
+ }
1500
+ return handleSelectElement(params.elementId);
1501
+ case "deselect":
1502
+ if (!params.elementId) {
1503
+ throw new Error("elementId is required for deselect action");
1504
+ }
1505
+ return handleDeselectElement(params.elementId);
1506
+ case "deselect-all":
1507
+ return handleDeselectAllElements();
1508
+ case "get-selected":
1509
+ return handleGetSelected();
1510
+ case "list-available-types":
1511
+ return handleListAvailableTypes();
1512
+ default:
1513
+ throw new Error(`Unknown action: ${params.action}`);
1514
+ }
1515
+ } catch (error) {
1516
+ const errorMessage = error instanceof Error ? error.message : String(error);
1517
+ throw new Error(`Failed to execute action "${params.action}": ${errorMessage}`);
762
1518
  }
763
1519
  }
764
- function isAnchorTag(tagName) {
765
- return tagName.toLowerCase() === "a";
1520
+ function initElementsTool() {
1521
+ getMCPByDomain("elements").addTool({
1522
+ name: "elements",
1523
+ schema,
1524
+ description: `This tool manages individual Elementor atomic elements (v4).
1525
+
1526
+ **When to use this tool:**
1527
+
1528
+ Use this tool to create, update, delete, duplicate, move, and select individual atomic elements, as well as retrieve their schemas and current props.
1529
+
1530
+ **Available actions:**
1531
+
1532
+ - \`list-available-types\`: List all available atomic element types.
1533
+ - \`get-element-schema\`: Get the propsSchema and controls for an element type. Required before creating elements of a new type.
1534
+ - \`get-element-props\`: Get the current prop values for an existing element.
1535
+ - \`create-element\`: Create a new atomic element with specified props and styles (Important to match props and styles by the schema, use get-element-schema to get the schema first).
1536
+ - \`update-props\`: Update props for an existing element.
1537
+ - \`create-style\`: Create a new style definition for an element.
1538
+ - \`get-styles\`: Get all style definitions for an element.
1539
+ - \`update-styles\`: Update styles for an existing element's style variant.
1540
+ - \`delete-style\`: Delete a style definition from an element.
1541
+ - \`delete\`: Delete an element.
1542
+ - \`duplicate\`: Duplicate an existing element.
1543
+ - \`move\`: Move an element to a different container.
1544
+ - \`select\`: Select one or more elements.
1545
+ - \`deselect\`: Deselect a specific element.
1546
+ - \`deselect-all\`: Deselect all selected elements.
1547
+ - \`get-selected\`: Get currently selected elements.
1548
+
1549
+ **Constraints:**
1550
+
1551
+ - Before creating an element of a certain type for the first time, you MUST call \`get-element-schema\` to retrieve its schema.
1552
+ - You can only update props for existing elements.
1553
+ - All props must match the element type's propsSchema keys.
1554
+ - Element types must be atomic (have atomic_controls and atomic_props_schema).
1555
+ - Container IDs must exist and be valid before create/move operations.
1556
+
1557
+ ** Must do with every operation **
1558
+ As of the user can ask in multiple ways the creation of the element, you need to first get the list of available types with "list-available-types" action.
1559
+ After getting it, convert to the most relevant type that the user requested and if this is not clear, request for user input.
1560
+ After finding out the proper type, get the schema for it with "get-element-schema" action.
1561
+
1562
+ ** Styles and Settings propUtils **
1563
+ Getting the schema is important as it introduces the propUtils for the styles and settings.
1564
+ You can use the propUtils to create, update, delete, and get the values of the styles and settings.
1565
+ Settings exists in the result of the get-element-schema action -> propsSchema.
1566
+ Styles exists in the result of the get-element-schema action -> stylesSchema.
1567
+
1568
+ **Examples:**
1569
+
1570
+ Get schema for heading element:
1571
+ \`\`\`json
1572
+ { "action": "get-element-schema", "elementType": "e-heading" }
1573
+ \`\`\`
1574
+
1575
+ Create a heading element:
1576
+ \`\`\`json
1577
+ { "action": "create-element", "elementType": "e-heading", "containerId": "document", "props": { "title": { $$type: "string", "value": "Hello World" } } }
1578
+ \`\`\`
1579
+
1580
+ Update element props:
1581
+ \`\`\`json
1582
+ { "action": "update-props", "elementId": "abc123", "props": { "title": "Updated Title" } }
1583
+ \`\`\`
1584
+
1585
+ Create element style:
1586
+ \`\`\`json
1587
+ { "action": "create-style", "elementId": "abc123", "styles": { "padding": "20px", "margin": "10px" } }
1588
+ \`\`\`
1589
+
1590
+ Get element styles:
1591
+ \`\`\`json
1592
+ { "action": "get-styles", "elementId": "abc123" }
1593
+ \`\`\`
1594
+
1595
+ Update element styles:
1596
+ \`\`\`json
1597
+ { "action": "update-styles", "elementId": "abc123", "styles": { "padding": "20px", "margin": "10px" } }
1598
+ \`\`\`
1599
+
1600
+ Delete element style:
1601
+ \`\`\`json
1602
+ { "action": "delete-style", "elementId": "abc123", "styleId": "style-id-123" }
1603
+ \`\`\``,
1604
+ handler: async (params) => {
1605
+ return routeAction(params);
1606
+ }
1607
+ });
766
1608
  }
767
- function isElementorElement(element) {
768
- return element.hasAttribute("data-id");
1609
+
1610
+ // src/mcp/index.ts
1611
+ function initMcp() {
1612
+ const { setMCPDescription } = getMCPByDomain2("elements");
1613
+ setMCPDescription("Tools for managing atomic elements in Elementor v4 editor");
1614
+ initElementsTool();
769
1615
  }
770
1616
  export {
771
1617
  ELEMENT_STYLE_CHANGE_EVENT,
@@ -774,33 +1620,45 @@ export {
774
1620
  createElements,
775
1621
  deleteElement,
776
1622
  deleteElementStyle,
1623
+ dropElement,
777
1624
  duplicateElement,
778
1625
  duplicateElements,
779
1626
  generateElementId,
780
1627
  getAnchoredAncestorId,
781
1628
  getAnchoredDescendantId,
782
1629
  getContainer,
1630
+ getCurrentDocumentContainer,
783
1631
  getCurrentDocumentId,
1632
+ getElementEditorSettings,
1633
+ getElementInteractions,
784
1634
  getElementLabel,
785
1635
  getElementSetting,
786
1636
  getElementSettings,
787
1637
  getElementStyles,
1638
+ getElementType,
788
1639
  getElements,
789
1640
  getLinkInLinkRestriction,
790
1641
  getSelectedElements,
791
1642
  getWidgetsCache,
1643
+ initMcp as initElementsMcp,
792
1644
  isElementAnchored,
1645
+ moveElement,
1646
+ moveElements,
1647
+ playElementInteractions,
793
1648
  removeElements,
794
1649
  replaceElement,
795
1650
  selectElement,
796
1651
  shouldCreateNewLocalStyle,
797
1652
  styleRerenderEvents,
1653
+ updateElementEditorSettings,
1654
+ updateElementInteractions,
798
1655
  updateElementSettings,
799
1656
  updateElementStyle,
800
1657
  useElementChildren,
1658
+ useElementEditorSettings,
1659
+ useElementInteractions,
801
1660
  useElementSetting,
802
1661
  useElementSettings,
803
- useElementType,
804
1662
  useParentElement,
805
1663
  useSelectedElement
806
1664
  };