@elementor/editor-variables 4.1.0-838 → 4.1.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/components/variables-manager/variables-manager-panel.tsx
2
2
  import * as React14 from "react";
3
- import { useCallback as useCallback6, useEffect as useEffect3, useMemo as useMemo3, useState as useState6 } from "react";
3
+ import { useCallback as useCallback6, useEffect as useEffect4, useMemo as useMemo3, useState as useState6 } from "react";
4
4
  import { useSuppressedMessage } from "@elementor/editor-current-user";
5
5
  import {
6
6
  __createPanel as createPanel,
@@ -310,12 +310,13 @@ var buildOperationsArray = (originalVariables, currentVariables, deletedVariable
310
310
  // src/storage.ts
311
311
  var STORAGE_KEY = "elementor-global-variables";
312
312
  var STORAGE_WATERMARK_KEY = "elementor-global-variables-watermark";
313
+ var STORAGE_UPDATED_EVENT = "variables:updated";
313
314
  var OP_RW = "RW";
314
315
  var OP_RO = "RO";
315
316
  var Storage = class {
316
317
  state;
317
318
  notifyChange() {
318
- window.dispatchEvent(new Event("variables:updated"));
319
+ window.dispatchEvent(new Event(STORAGE_UPDATED_EVENT));
319
320
  }
320
321
  constructor() {
321
322
  this.state = {
@@ -871,7 +872,7 @@ var useErrorNavigation = () => {
871
872
  };
872
873
 
873
874
  // src/components/variables-manager/hooks/use-variables-manager-state.ts
874
- import { useCallback as useCallback3, useState as useState2 } from "react";
875
+ import { useCallback as useCallback3, useEffect, useState as useState2 } from "react";
875
876
 
876
877
  // src/hooks/use-prop-variables.ts
877
878
  import { useMemo } from "react";
@@ -1035,10 +1036,30 @@ var useVariablesManagerState = () => {
1035
1036
  const [isDirty, setIsDirty] = useState2(false);
1036
1037
  const [isSaving, setIsSaving] = useState2(false);
1037
1038
  const [searchValue, setSearchValue] = useState2("");
1039
+ useEffect(() => {
1040
+ const handleStorageUpdated = () => {
1041
+ setVariables(getVariables(false));
1042
+ setDeletedVariables([]);
1043
+ setIsDirty(false);
1044
+ };
1045
+ window.addEventListener(STORAGE_UPDATED_EVENT, handleStorageUpdated);
1046
+ return () => {
1047
+ window.removeEventListener(STORAGE_UPDATED_EVENT, handleStorageUpdated);
1048
+ };
1049
+ }, []);
1038
1050
  const handleOnChange = useCallback3(
1039
1051
  (newVariables) => {
1040
- setVariables({ ...variables, ...newVariables });
1041
- setIsDirty(true);
1052
+ const hasChanges = Object.entries(newVariables).some(([id2, newVar]) => {
1053
+ const existingVar = variables[id2];
1054
+ if (!existingVar) {
1055
+ return true;
1056
+ }
1057
+ return existingVar.label !== newVar.label || existingVar.value !== newVar.value || existingVar.order !== newVar.order || existingVar.type !== newVar.type || (existingVar.sync_to_v3 ?? false) !== (newVar.sync_to_v3 ?? false);
1058
+ });
1059
+ if (hasChanges) {
1060
+ setVariables({ ...variables, ...newVariables });
1061
+ setIsDirty(true);
1062
+ }
1042
1063
  },
1043
1064
  [variables]
1044
1065
  );
@@ -1335,7 +1356,7 @@ var getDefaultName = (variables, baseName) => {
1335
1356
 
1336
1357
  // src/components/variables-manager/variables-manager-table.tsx
1337
1358
  import * as React13 from "react";
1338
- import { useEffect as useEffect2, useRef as useRef6 } from "react";
1359
+ import { useEffect as useEffect3, useRef as useRef6 } from "react";
1339
1360
  import {
1340
1361
  Table,
1341
1362
  TableBody,
@@ -1460,7 +1481,7 @@ var LabelField = ({
1460
1481
 
1461
1482
  // src/components/variables-manager/variable-editable-cell.tsx
1462
1483
  import * as React10 from "react";
1463
- import { useCallback as useCallback5, useEffect, useRef as useRef4, useState as useState5 } from "react";
1484
+ import { useCallback as useCallback5, useEffect as useEffect2, useRef as useRef4, useState as useState5 } from "react";
1464
1485
  import { ClickAwayListener, Stack as Stack4 } from "@elementor/ui";
1465
1486
  var VariableEditableCell = React10.memo(
1466
1487
  ({
@@ -1488,10 +1509,10 @@ var VariableEditableCell = React10.memo(
1488
1509
  }
1489
1510
  setIsEditing(false);
1490
1511
  }, [value, onChange, fieldType, labelFieldError, valueFieldError]);
1491
- useEffect(() => {
1512
+ useEffect2(() => {
1492
1513
  onRowRef?.(rowRef?.current);
1493
1514
  }, [onRowRef]);
1494
- useEffect(() => {
1515
+ useEffect2(() => {
1495
1516
  if (autoEdit && !isEditing && !disabled) {
1496
1517
  setIsEditing(true);
1497
1518
  onAutoEditComplete?.();
@@ -1776,7 +1797,7 @@ var VariableRow = (props) => {
1776
1797
  value,
1777
1798
  onChange,
1778
1799
  onPropTypeKeyChange: (type) => {
1779
- if (!isDisabled) {
1800
+ if (!isDisabled && type !== row.type) {
1780
1801
  handleOnChange({
1781
1802
  ...variables,
1782
1803
  [row.id]: { ...variables[row.id], type }
@@ -1832,7 +1853,7 @@ var VariablesManagerTable = ({
1832
1853
  }) => {
1833
1854
  const tableContainerRef = useRef6(null);
1834
1855
  const variableRowRefs = useRef6(/* @__PURE__ */ new Map());
1835
- useEffect2(() => {
1856
+ useEffect3(() => {
1836
1857
  if (autoEditVariableId && tableContainerRef.current) {
1837
1858
  const rowElement = variableRowRefs.current.get(autoEditVariableId);
1838
1859
  if (rowElement) {
@@ -2000,7 +2021,7 @@ function VariablesManagerPanelRoot({
2000
2021
  }
2001
2022
  void closePanel();
2002
2023
  }, [isDirty, openSaveChangesDialog, closePanel]);
2003
- useEffect3(() => {
2024
+ useEffect4(() => {
2004
2025
  if (!embedded || !onExposeCloseAttempt) {
2005
2026
  return;
2006
2027
  }
@@ -2305,8 +2326,7 @@ function VariablesManagerPanelRoot({
2305
2326
  display: "flex",
2306
2327
  flexDirection: "column",
2307
2328
  flex: 1,
2308
- minHeight: 0,
2309
- overflow: "hidden"
2329
+ minHeight: 0
2310
2330
  }
2311
2331
  },
2312
2332
  bodyInner
@@ -2351,7 +2371,7 @@ function VariablesManagerPanelRoot({
2351
2371
  return embedded ? core : /* @__PURE__ */ React14.createElement(ThemeProvider, null, core);
2352
2372
  }
2353
2373
  var usePreventUnload = (isDirty) => {
2354
- useEffect3(() => {
2374
+ useEffect4(() => {
2355
2375
  const handleBeforeUnload = (event) => {
2356
2376
  if (isDirty) {
2357
2377
  event.preventDefault();
@@ -2392,18 +2412,11 @@ import { isExperimentActive as isExperimentActive2 } from "@elementor/editor-v1-
2392
2412
  import { controlActionsMenu } from "@elementor/menus";
2393
2413
 
2394
2414
  // src/components/global-styles-import-listener.tsx
2395
- import { useEffect as useEffect4 } from "react";
2415
+ import { useEffect as useEffect5 } from "react";
2396
2416
  import { GLOBAL_STYLES_IMPORTED_EVENT } from "@elementor/editor-canvas";
2397
2417
  function GlobalStylesImportListener() {
2398
- useEffect4(() => {
2399
- const handleGlobalStylesImported = (event) => {
2400
- const importedVars = event.detail?.global_variables;
2401
- if (!importedVars) {
2402
- return;
2403
- }
2404
- if (importedVars.data && typeof importedVars.data === "object") {
2405
- styleVariablesRepository.update(importedVars.data);
2406
- }
2418
+ useEffect5(() => {
2419
+ const handleGlobalStylesImported = () => {
2407
2420
  service.load();
2408
2421
  };
2409
2422
  window.addEventListener(GLOBAL_STYLES_IMPORTED_EVENT, handleGlobalStylesImported);
@@ -2415,7 +2428,7 @@ function GlobalStylesImportListener() {
2415
2428
  }
2416
2429
 
2417
2430
  // src/components/open-panel-from-event.tsx
2418
- import { useEffect as useEffect5, useRef as useRef7, useState as useState7 } from "react";
2431
+ import { useEffect as useEffect6, useRef as useRef7, useState as useState7 } from "react";
2419
2432
  import {
2420
2433
  __privateListenTo as listenTo,
2421
2434
  __privateOpenRoute as openRoute,
@@ -2427,13 +2440,13 @@ function OpenPanelFromEvent() {
2427
2440
  const { open } = usePanelActions();
2428
2441
  const pendingRef = useRef7(false);
2429
2442
  const [readyToOpen, setReadyToOpen] = useState7(false);
2430
- useEffect5(() => {
2443
+ useEffect6(() => {
2431
2444
  if (readyToOpen) {
2432
2445
  setReadyToOpen(false);
2433
2446
  open();
2434
2447
  }
2435
2448
  }, [readyToOpen, open]);
2436
- useEffect5(() => {
2449
+ useEffect6(() => {
2437
2450
  return listenTo(routeOpenEvent(DEFAULT_PANEL_ROUTE), () => {
2438
2451
  if (pendingRef.current) {
2439
2452
  pendingRef.current = false;
@@ -2441,7 +2454,7 @@ function OpenPanelFromEvent() {
2441
2454
  }
2442
2455
  });
2443
2456
  }, []);
2444
- useEffect5(() => {
2457
+ useEffect6(() => {
2445
2458
  const handler = () => {
2446
2459
  pendingRef.current = true;
2447
2460
  openRoute(DEFAULT_PANEL_ROUTE);
@@ -2453,7 +2466,7 @@ function OpenPanelFromEvent() {
2453
2466
  }
2454
2467
 
2455
2468
  // src/components/open-panel-from-url.tsx
2456
- import { useEffect as useEffect6, useRef as useRef8 } from "react";
2469
+ import { useEffect as useEffect7, useRef as useRef8 } from "react";
2457
2470
  import { __privateListenTo as listenTo2, routeOpenEvent as routeOpenEvent2 } from "@elementor/editor-v1-adapters";
2458
2471
  var ACTIVE_PANEL_PARAM = "active-panel";
2459
2472
  var PANEL_ID = "variables-manager";
@@ -2461,7 +2474,7 @@ var DEFAULT_PANEL_ROUTE2 = "panel/elements";
2461
2474
  function OpenPanelFromUrl() {
2462
2475
  const { open } = usePanelActions();
2463
2476
  const hasOpened = useRef8(false);
2464
- useEffect6(() => {
2477
+ useEffect7(() => {
2465
2478
  const urlParams = new URLSearchParams(window.location.search);
2466
2479
  const activePanel = urlParams.get(ACTIVE_PANEL_PARAM);
2467
2480
  if (activePanel !== PANEL_ID) {
@@ -2714,7 +2727,7 @@ var VariableCreation = ({ onGoBack, onClose }) => {
2714
2727
 
2715
2728
  // src/components/variable-edit.tsx
2716
2729
  import * as React19 from "react";
2717
- import { useEffect as useEffect7, useState as useState11 } from "react";
2730
+ import { useEffect as useEffect8, useState as useState11 } from "react";
2718
2731
  import { PopoverContent as PopoverContent2, useBoundProp as useBoundProp5 } from "@elementor/editor-controls";
2719
2732
  import { useSuppressedMessage as useSuppressedMessage2 } from "@elementor/editor-current-user";
2720
2733
  import { PopoverHeader as PopoverHeader2, SectionPopoverBody as SectionPopoverBody2 } from "@elementor/editor-ui";
@@ -2791,7 +2804,7 @@ var VariableEdit = ({ onClose, onGoBack, onSubmit, editId }) => {
2791
2804
  const userPermissions = usePermissions();
2792
2805
  const [value, setValue] = useState11(() => variable.value);
2793
2806
  const [label, setLabel] = useState11(() => variable.label);
2794
- useEffect7(() => {
2807
+ useEffect8(() => {
2795
2808
  styleVariablesRepository.update({
2796
2809
  [editId]: {
2797
2810
  ...variable,
@@ -2956,7 +2969,7 @@ var VariableEdit = ({ onClose, onGoBack, onSubmit, editId }) => {
2956
2969
  };
2957
2970
 
2958
2971
  // src/components/variables-selection.tsx
2959
- import { useEffect as useEffect8, useState as useState12 } from "react";
2972
+ import { useEffect as useEffect9, useState as useState12 } from "react";
2960
2973
  import * as React21 from "react";
2961
2974
  import { trackUpgradePromotionClick as trackUpgradePromotionClick2, trackViewPromotion as trackViewPromotion2 } from "@elementor/editor-controls";
2962
2975
  import {
@@ -3146,7 +3159,7 @@ var VariablesSelection = ({ closePopover, onAdd, onEdit, onSettings, disabled =
3146
3159
  const handleClearSearch = () => {
3147
3160
  setSearchValue("");
3148
3161
  };
3149
- useEffect8(() => {
3162
+ useEffect9(() => {
3150
3163
  if (disabled) {
3151
3164
  trackViewPromotion2({
3152
3165
  target_name: "variables_popover",
@@ -3923,6 +3936,84 @@ var trackOpenVariablePopover = (path, variableType) => {
3923
3936
 
3924
3937
  // src/mcp/manage-variable-tool.ts
3925
3938
  import { z as z3 } from "@elementor/schema";
3939
+ import { isProActive as isProActive2 } from "@elementor/utils";
3940
+
3941
+ // src/mcp/variable-tool-prompt.ts
3942
+ import { toolPrompts } from "@elementor/editor-mcp";
3943
+ import { isProActive } from "@elementor/utils";
3944
+ var MANAGE_VARIABLES_GUIDE_URI = "elementor://variables/tools/manage-global-variable-guide";
3945
+ var generateVariablesPrompt = () => {
3946
+ const prompt = toolPrompts("manage-global-variable");
3947
+ const proIsActive = isProActive();
3948
+ const sizeVariableSection = proIsActive ? `- **global-size-variable** \u2014 A simple CSS length with a unit (Elementor Pro). Use this for fixed spacing, font sizes, or layout values. Example: \`16px\`, \`1.5rem\`, \`2em\`, \`10vh\`
3949
+ - **global-custom-size-variable** \u2014 Any CSS size expression that goes beyond a simple number + unit (Elementor Pro). Use this when the value is a CSS function, a keyword, or a combination of units that \`global-size-variable\` cannot represent. Example: \`auto\`, \`clamp(1rem, 2vw, 2rem)\`, \`calc(100% - 32px)\`, \`min(50vw, 600px)\`, \`300ms\`, \`2ch\`. When in doubt: if the value contains a function call or a keyword, use \`global-custom-size-variable\`.` : `- ~~global-size-variable~~ \u2014 requires Elementor Pro (not available on this site)
3950
+ - ~~global-custom-size-variable~~ \u2014 requires Elementor Pro (not available on this site)`;
3951
+ prompt.description(`
3952
+ # Purpose
3953
+ Create, update, or delete V4 global CSS variables. These are distinct from legacy v3 globals and map 1:1 to \`--css-var: VALUE\`.
3954
+
3955
+ # Available Types
3956
+ - **global-color-variable** \u2014 CSS color value. Example: \`#FF0000\`, \`rgba(255,0,0,1)\`, \`hsl(0,100%,50%)\`
3957
+ - **global-font-variable** \u2014 Font family name ONLY \u2014 NOT a size or px value. Example: \`Roboto\`, \`Open Sans\`. NEVER pass px/rem here.
3958
+ ${sizeVariableSection}
3959
+
3960
+ # Naming Rules
3961
+ - Labels must be **lowercase**, using only letters (a-z), numbers, digits (0-9), dashes (-), or underscores (_)
3962
+ - No spaces, no special characters
3963
+ - Example: "Headline Primary" \u2192 \`headline-primary\`
3964
+ - Labels must be unique \u2014 always check [elementor://global-variables] first
3965
+
3966
+ # Value Rules
3967
+ - Provide a **plain CSS value** only \u2014 do NOT pass JSON, legacy-globals object structures, or variable references
3968
+ - Values are inserted as-is: \`--css-var: <value>\`
3969
+ - NEVER store a px/rem value inside a \`global-font-variable\` \u2014 use \`global-size-variable\` (Pro) instead
3970
+
3971
+ # Operations
3972
+ - **create** \u2014 requires \`type\`, \`label\`, \`value\`. Label must be unique.
3973
+ - **update** \u2014 requires \`id\`, \`label\`, \`value\`. Get \`id\` from [elementor://global-variables]. When renaming: keep existing value. When changing value: keep exact existing label.
3974
+ - **delete** \u2014 requires \`id\`. DESTRUCTIVE \u2014 always confirm with user before executing.
3975
+ `);
3976
+ prompt.parameter("action", '"create", "update", or "delete".');
3977
+ prompt.parameter("type", "Variable type. Required for create. See Available Types above.");
3978
+ prompt.parameter("label", "Variable name (lowercase, dash-separated). Required for create/update.");
3979
+ prompt.parameter(
3980
+ "value",
3981
+ "Plain CSS value matching the variable type. Required for create/update. Do NOT pass JSON."
3982
+ );
3983
+ prompt.parameter("id", "Variable ID. Required for update/delete. Obtain from [elementor://global-variables].");
3984
+ prompt.example(`
3985
+ Create a brand color:
3986
+ { "action": "create", "type": "global-color-variable", "label": "brand-primary", "value": "#1A73E8" }
3987
+
3988
+ Create a heading font:
3989
+ { "action": "create", "type": "global-font-variable", "label": "font-heading", "value": "Playfair Display" }
3990
+
3991
+ Create a simple spacing size:
3992
+ { "action": "create", "type": "global-size-variable", "label": "spacing-md", "value": "16px" }
3993
+
3994
+ Create a fluid/responsive size using a CSS function (use global-custom-size-variable, NOT global-size-variable):
3995
+ { "action": "create", "type": "global-custom-size-variable", "label": "spacing-fluid", "value": "clamp(1rem, 2vw, 2rem)" }
3996
+
3997
+ Create a size that is a keyword:
3998
+ { "action": "create", "type": "global-custom-size-variable", "label": "width-auto", "value": "auto" }
3999
+
4000
+ Create a size using calc():
4001
+ { "action": "create", "type": "global-custom-size-variable", "label": "sidebar-width", "value": "calc(100% - 32px)" }
4002
+
4003
+ Update a variable's value (keep exact label):
4004
+ { "action": "update", "id": "abc123", "label": "brand-primary", "value": "#0D47A1" }
4005
+
4006
+ Rename a variable (keep existing value):
4007
+ { "action": "update", "id": "abc123", "label": "brand-secondary", "value": "#1A73E8" }
4008
+
4009
+ Delete a variable:
4010
+ { "action": "delete", "id": "abc123" }
4011
+ `);
4012
+ prompt.instruction(
4013
+ "Always read [elementor://global-variables] before creating to check existing variables and avoid duplicate labels."
4014
+ );
4015
+ return prompt.prompt();
4016
+ };
3926
4017
 
3927
4018
  // src/mcp/variables-resource.ts
3928
4019
  import { __privateListenTo as listenTo3, commandEndEvent } from "@elementor/editor-v1-adapters";
@@ -3959,22 +4050,59 @@ var initVariablesResource = (variablesMcpEntry, canvasMcpEntry) => {
3959
4050
  };
3960
4051
  }
3961
4052
  );
3962
- window.addEventListener("variables:updated", notifyGlobalVariablesUpdated);
4053
+ window.addEventListener(STORAGE_UPDATED_EVENT, notifyGlobalVariablesUpdated);
3963
4054
  listenTo3(commandEndEvent("document/save/update"), notifyGlobalVariablesUpdated);
3964
4055
  });
3965
4056
  };
3966
4057
 
3967
4058
  // src/mcp/manage-variable-tool.ts
4059
+ var VARIABLE_TYPES = {
4060
+ COLOR: "global-color-variable",
4061
+ FONT: "global-font-variable",
4062
+ SIZE: "global-size-variable",
4063
+ CUSTOM_SIZE: "global-custom-size-variable"
4064
+ };
4065
+ var LENGTH_UNIT_PATTERN = /^(auto|\d+(\.\d+)?(px|rem|em|vh|vw|%|ch|s|ms))$/i;
4066
+ var COLOR_PATTERN = /^(#[0-9a-f]{3,8}|rgba?\(|hsl)/i;
4067
+ function validateValueForType(type, value) {
4068
+ if (type === VARIABLE_TYPES.FONT && LENGTH_UNIT_PATTERN.test(value.trim())) {
4069
+ return `Font variable value must be a font family name (e.g. "Roboto"), not a size value like "${value}". Use "global-size-variable" or "global-custom-size-variable" for spacing/size values.`;
4070
+ }
4071
+ if (type === VARIABLE_TYPES.COLOR && !COLOR_PATTERN.test(value.trim())) {
4072
+ return `Color variable value should be a CSS color (e.g. "#FF0000"), got "${value}".`;
4073
+ }
4074
+ if (type === VARIABLE_TYPES.SIZE && !LENGTH_UNIT_PATTERN.test(value.trim())) {
4075
+ return `Size variable value should include a CSS unit (e.g. "16px") or be "auto", got "${value}".`;
4076
+ }
4077
+ return null;
4078
+ }
3968
4079
  var initManageVariableTool = (reg) => {
3969
- const { addTool } = reg;
4080
+ const { addTool, resource } = reg;
4081
+ resource(
4082
+ "manage-global-variable-guide",
4083
+ MANAGE_VARIABLES_GUIDE_URI,
4084
+ {
4085
+ title: "Manage Global Variable Guide",
4086
+ description: "Detailed guide for using the manage-global-variable tool",
4087
+ mimeType: "text/plain"
4088
+ },
4089
+ async (uri) => ({
4090
+ contents: [{ uri: uri.href, mimeType: "text/plain", text: generateVariablesPrompt() }]
4091
+ })
4092
+ );
3970
4093
  addTool({
3971
4094
  name: "manage-global-variable",
4095
+ description: "Manage V4 global variables (color, font, size). Read the guide resource before use.",
3972
4096
  schema: {
3973
4097
  action: z3.enum(["create", "update", "delete"]).describe("Operation to perform"),
3974
- id: z3.string().optional().describe("Variable id (required for update/delete). Get from list-global-variables."),
3975
- type: z3.string().optional().describe('Variable type: "global-color-variable" or "global-font-variable" (required for create)'),
3976
- label: z3.string().optional().describe("Variable label (required for create/update)"),
3977
- value: z3.string().optional().describe("Variable value (required for create/update)")
4098
+ id: z3.string().optional().describe("Variable id \u2014 required for update/delete. Get from the global-variables resource."),
4099
+ type: z3.string().optional().describe(
4100
+ 'Variable type \u2014 required for create. One of: "global-color-variable", "global-font-variable", "global-size-variable", "global-custom-size-variable" (size types require Elementor Pro). NEVER store px/rem values in a font variable.'
4101
+ ),
4102
+ label: z3.string().optional().describe("Variable label (lowercase, dash-separated) \u2014 required for create/update."),
4103
+ value: z3.string().optional().describe(
4104
+ 'Plain CSS value \u2014 required for create/update. Color: hex/rgba/hsl. Font: family name only, never px/rem. Size: value with unit e.g. "16px", or "auto" (Pro). Do NOT pass JSON.'
4105
+ )
3978
4106
  },
3979
4107
  outputSchema: {
3980
4108
  status: z3.enum(["ok"]).describe("Operation status"),
@@ -3985,32 +4113,22 @@ var initManageVariableTool = (reg) => {
3985
4113
  speedPriority: 0.75
3986
4114
  },
3987
4115
  requiredResources: [
4116
+ { uri: MANAGE_VARIABLES_GUIDE_URI, description: "Full guide for variable types, naming rules, and usage" },
3988
4117
  {
3989
4118
  uri: GLOBAL_VARIABLES_URI,
3990
- description: "Global variables"
4119
+ description: "Current global variables \u2014 check before creating to avoid duplicates"
3991
4120
  }
3992
4121
  ],
3993
- description: `Manages global variables (create/update/delete). Existing variables available in resources.
3994
- CREATE: requires type, label, value. Ensure label is unique.
3995
- UPDATE: requires id, label, value. When renaming: keep existing value. When updating value: keep exact label.
3996
- DELETE: requires id. DESTRUCTIVE - confirm with user first.
3997
-
3998
- # NAMING - IMPORTANT
3999
- the variables names should ALWAYS be lowercased and dashed spaced. example: "Headline Primary" should be "headline-primary"
4000
- `,
4122
+ isDestructive: true,
4001
4123
  handler: async (params) => {
4002
4124
  const operations = getServiceActions(service);
4003
4125
  const op = operations[params.action];
4004
4126
  if (op) {
4005
4127
  await op(params);
4006
- return {
4007
- status: "ok"
4008
- };
4128
+ return { status: "ok" };
4009
4129
  }
4010
4130
  throw new Error(`Unknown action ${params.action}`);
4011
- },
4012
- isDestructive: true
4013
- // Because delete is destructive
4131
+ }
4014
4132
  });
4015
4133
  };
4016
4134
  function getServiceActions(svc) {
@@ -4019,10 +4137,17 @@ function getServiceActions(svc) {
4019
4137
  if (!type || !label || !value) {
4020
4138
  throw new Error("Create requires type, label, and value");
4021
4139
  }
4140
+ if ((type === VARIABLE_TYPES.SIZE || type === VARIABLE_TYPES.CUSTOM_SIZE) && !isProActive2()) {
4141
+ throw new Error("Creating size variables requires Elementor Pro.");
4142
+ }
4022
4143
  const labelError = validateLabel(label);
4023
4144
  if (labelError) {
4024
4145
  throw new Error(labelError);
4025
4146
  }
4147
+ const valueError = validateValueForType(type, value);
4148
+ if (valueError) {
4149
+ throw new Error(valueError);
4150
+ }
4026
4151
  return svc.create({ type, label, value });
4027
4152
  },
4028
4153
  update({ id: id2, label, value }) {
@@ -4033,6 +4158,13 @@ function getServiceActions(svc) {
4033
4158
  if (labelError) {
4034
4159
  throw new Error(labelError);
4035
4160
  }
4161
+ const existingVariable = svc.variables()[id2];
4162
+ if (existingVariable) {
4163
+ const valueError = validateValueForType(existingVariable.type, value);
4164
+ if (valueError) {
4165
+ throw new Error(valueError);
4166
+ }
4167
+ }
4036
4168
  return svc.update(id2, { label, value });
4037
4169
  },
4038
4170
  delete({ id: id2 }) {
@@ -4046,15 +4178,6 @@ function getServiceActions(svc) {
4046
4178
 
4047
4179
  // src/mcp/index.ts
4048
4180
  function initMcp(reg, canvasMcpEntry) {
4049
- const { setMCPDescription } = reg;
4050
- setMCPDescription(
4051
- `Everything related to V4 ( Atomic ) variables.
4052
- # Global variables
4053
- - Create/update/delete global variables
4054
- - Get list of global variables
4055
- - Get details of a global variable
4056
- `
4057
- );
4058
4181
  initManageVariableTool(reg);
4059
4182
  initVariablesResource(reg, canvasMcpEntry);
4060
4183
  }
@@ -4262,7 +4385,7 @@ function registerVariableTypes() {
4262
4385
 
4263
4386
  // src/renderers/style-variables-renderer.tsx
4264
4387
  import * as React38 from "react";
4265
- import { useEffect as useEffect9, useState as useState20 } from "react";
4388
+ import { useEffect as useEffect10, useState as useState20 } from "react";
4266
4389
  import {
4267
4390
  __privateUseListenTo as useListenTo,
4268
4391
  commandEndEvent as commandEndEvent2,
@@ -4286,7 +4409,7 @@ function usePortalContainer() {
4286
4409
  }
4287
4410
  function useStyleVariables() {
4288
4411
  const [variables, setVariables] = useState20({});
4289
- useEffect9(() => {
4412
+ useEffect10(() => {
4290
4413
  const unsubscribe = styleVariablesRepository.subscribe(setVariables);
4291
4414
  return () => {
4292
4415
  unsubscribe();
@@ -4583,7 +4706,15 @@ function init() {
4583
4706
  useProps: usePropVariableAction
4584
4707
  });
4585
4708
  service.init().then(() => {
4586
- initMcp(getMCPByDomain("variables"), getMCPByDomain("canvas"));
4709
+ const variablesMcpRegistry = getMCPByDomain("variables", {
4710
+ instructions: `Everything related to V4 ( Atomic ) variables.
4711
+ # Global variables
4712
+ - Create/update/delete global variables
4713
+ - Get list of global variables
4714
+ - Get details of a global variable
4715
+ `
4716
+ });
4717
+ initMcp(variablesMcpRegistry, getMCPByDomain("canvas"));
4587
4718
  });
4588
4719
  injectIntoTop({
4589
4720
  id: "canvas-style-variables-render",