@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.js CHANGED
@@ -334,12 +334,13 @@ var buildOperationsArray = (originalVariables, currentVariables, deletedVariable
334
334
  // src/storage.ts
335
335
  var STORAGE_KEY = "elementor-global-variables";
336
336
  var STORAGE_WATERMARK_KEY = "elementor-global-variables-watermark";
337
+ var STORAGE_UPDATED_EVENT = "variables:updated";
337
338
  var OP_RW = "RW";
338
339
  var OP_RO = "RO";
339
340
  var Storage = class {
340
341
  state;
341
342
  notifyChange() {
342
- window.dispatchEvent(new Event("variables:updated"));
343
+ window.dispatchEvent(new Event(STORAGE_UPDATED_EVENT));
343
344
  }
344
345
  constructor() {
345
346
  this.state = {
@@ -1059,10 +1060,30 @@ var useVariablesManagerState = () => {
1059
1060
  const [isDirty, setIsDirty] = (0, import_react5.useState)(false);
1060
1061
  const [isSaving, setIsSaving] = (0, import_react5.useState)(false);
1061
1062
  const [searchValue, setSearchValue] = (0, import_react5.useState)("");
1063
+ (0, import_react5.useEffect)(() => {
1064
+ const handleStorageUpdated = () => {
1065
+ setVariables(getVariables(false));
1066
+ setDeletedVariables([]);
1067
+ setIsDirty(false);
1068
+ };
1069
+ window.addEventListener(STORAGE_UPDATED_EVENT, handleStorageUpdated);
1070
+ return () => {
1071
+ window.removeEventListener(STORAGE_UPDATED_EVENT, handleStorageUpdated);
1072
+ };
1073
+ }, []);
1062
1074
  const handleOnChange = (0, import_react5.useCallback)(
1063
1075
  (newVariables) => {
1064
- setVariables({ ...variables, ...newVariables });
1065
- setIsDirty(true);
1076
+ const hasChanges = Object.entries(newVariables).some(([id2, newVar]) => {
1077
+ const existingVar = variables[id2];
1078
+ if (!existingVar) {
1079
+ return true;
1080
+ }
1081
+ 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);
1082
+ });
1083
+ if (hasChanges) {
1084
+ setVariables({ ...variables, ...newVariables });
1085
+ setIsDirty(true);
1086
+ }
1066
1087
  },
1067
1088
  [variables]
1068
1089
  );
@@ -1792,7 +1813,7 @@ var VariableRow = (props) => {
1792
1813
  value,
1793
1814
  onChange,
1794
1815
  onPropTypeKeyChange: (type) => {
1795
- if (!isDisabled) {
1816
+ if (!isDisabled && type !== row.type) {
1796
1817
  handleOnChange({
1797
1818
  ...variables,
1798
1819
  [row.id]: { ...variables[row.id], type }
@@ -2321,8 +2342,7 @@ function VariablesManagerPanelRoot({
2321
2342
  display: "flex",
2322
2343
  flexDirection: "column",
2323
2344
  flex: 1,
2324
- minHeight: 0,
2325
- overflow: "hidden"
2345
+ minHeight: 0
2326
2346
  }
2327
2347
  },
2328
2348
  bodyInner
@@ -2401,7 +2421,7 @@ var StopSyncConfirmationDialog = ({ open, onClose, onConfirm }) => {
2401
2421
  // src/init.ts
2402
2422
  var import_editor = require("@elementor/editor");
2403
2423
  var import_editor_controls18 = require("@elementor/editor-controls");
2404
- var import_editor_mcp = require("@elementor/editor-mcp");
2424
+ var import_editor_mcp2 = require("@elementor/editor-mcp");
2405
2425
  var import_editor_panels2 = require("@elementor/editor-panels");
2406
2426
  var import_editor_props10 = require("@elementor/editor-props");
2407
2427
  var import_editor_v1_adapters8 = require("@elementor/editor-v1-adapters");
@@ -2412,14 +2432,7 @@ var import_react14 = require("react");
2412
2432
  var import_editor_canvas4 = require("@elementor/editor-canvas");
2413
2433
  function GlobalStylesImportListener() {
2414
2434
  (0, import_react14.useEffect)(() => {
2415
- const handleGlobalStylesImported = (event) => {
2416
- const importedVars = event.detail?.global_variables;
2417
- if (!importedVars) {
2418
- return;
2419
- }
2420
- if (importedVars.data && typeof importedVars.data === "object") {
2421
- styleVariablesRepository.update(importedVars.data);
2422
- }
2435
+ const handleGlobalStylesImported = () => {
2423
2436
  service.load();
2424
2437
  };
2425
2438
  window.addEventListener(import_editor_canvas4.GLOBAL_STYLES_IMPORTED_EVENT, handleGlobalStylesImported);
@@ -3920,6 +3933,84 @@ var trackOpenVariablePopover = (path, variableType) => {
3920
3933
 
3921
3934
  // src/mcp/manage-variable-tool.ts
3922
3935
  var import_schema3 = require("@elementor/schema");
3936
+ var import_utils4 = require("@elementor/utils");
3937
+
3938
+ // src/mcp/variable-tool-prompt.ts
3939
+ var import_editor_mcp = require("@elementor/editor-mcp");
3940
+ var import_utils3 = require("@elementor/utils");
3941
+ var MANAGE_VARIABLES_GUIDE_URI = "elementor://variables/tools/manage-global-variable-guide";
3942
+ var generateVariablesPrompt = () => {
3943
+ const prompt = (0, import_editor_mcp.toolPrompts)("manage-global-variable");
3944
+ const proIsActive = (0, import_utils3.isProActive)();
3945
+ 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\`
3946
+ - **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)
3947
+ - ~~global-custom-size-variable~~ \u2014 requires Elementor Pro (not available on this site)`;
3948
+ prompt.description(`
3949
+ # Purpose
3950
+ Create, update, or delete V4 global CSS variables. These are distinct from legacy v3 globals and map 1:1 to \`--css-var: VALUE\`.
3951
+
3952
+ # Available Types
3953
+ - **global-color-variable** \u2014 CSS color value. Example: \`#FF0000\`, \`rgba(255,0,0,1)\`, \`hsl(0,100%,50%)\`
3954
+ - **global-font-variable** \u2014 Font family name ONLY \u2014 NOT a size or px value. Example: \`Roboto\`, \`Open Sans\`. NEVER pass px/rem here.
3955
+ ${sizeVariableSection}
3956
+
3957
+ # Naming Rules
3958
+ - Labels must be **lowercase**, using only letters (a-z), numbers, digits (0-9), dashes (-), or underscores (_)
3959
+ - No spaces, no special characters
3960
+ - Example: "Headline Primary" \u2192 \`headline-primary\`
3961
+ - Labels must be unique \u2014 always check [elementor://global-variables] first
3962
+
3963
+ # Value Rules
3964
+ - Provide a **plain CSS value** only \u2014 do NOT pass JSON, legacy-globals object structures, or variable references
3965
+ - Values are inserted as-is: \`--css-var: <value>\`
3966
+ - NEVER store a px/rem value inside a \`global-font-variable\` \u2014 use \`global-size-variable\` (Pro) instead
3967
+
3968
+ # Operations
3969
+ - **create** \u2014 requires \`type\`, \`label\`, \`value\`. Label must be unique.
3970
+ - **update** \u2014 requires \`id\`, \`label\`, \`value\`. Get \`id\` from [elementor://global-variables]. When renaming: keep existing value. When changing value: keep exact existing label.
3971
+ - **delete** \u2014 requires \`id\`. DESTRUCTIVE \u2014 always confirm with user before executing.
3972
+ `);
3973
+ prompt.parameter("action", '"create", "update", or "delete".');
3974
+ prompt.parameter("type", "Variable type. Required for create. See Available Types above.");
3975
+ prompt.parameter("label", "Variable name (lowercase, dash-separated). Required for create/update.");
3976
+ prompt.parameter(
3977
+ "value",
3978
+ "Plain CSS value matching the variable type. Required for create/update. Do NOT pass JSON."
3979
+ );
3980
+ prompt.parameter("id", "Variable ID. Required for update/delete. Obtain from [elementor://global-variables].");
3981
+ prompt.example(`
3982
+ Create a brand color:
3983
+ { "action": "create", "type": "global-color-variable", "label": "brand-primary", "value": "#1A73E8" }
3984
+
3985
+ Create a heading font:
3986
+ { "action": "create", "type": "global-font-variable", "label": "font-heading", "value": "Playfair Display" }
3987
+
3988
+ Create a simple spacing size:
3989
+ { "action": "create", "type": "global-size-variable", "label": "spacing-md", "value": "16px" }
3990
+
3991
+ Create a fluid/responsive size using a CSS function (use global-custom-size-variable, NOT global-size-variable):
3992
+ { "action": "create", "type": "global-custom-size-variable", "label": "spacing-fluid", "value": "clamp(1rem, 2vw, 2rem)" }
3993
+
3994
+ Create a size that is a keyword:
3995
+ { "action": "create", "type": "global-custom-size-variable", "label": "width-auto", "value": "auto" }
3996
+
3997
+ Create a size using calc():
3998
+ { "action": "create", "type": "global-custom-size-variable", "label": "sidebar-width", "value": "calc(100% - 32px)" }
3999
+
4000
+ Update a variable's value (keep exact label):
4001
+ { "action": "update", "id": "abc123", "label": "brand-primary", "value": "#0D47A1" }
4002
+
4003
+ Rename a variable (keep existing value):
4004
+ { "action": "update", "id": "abc123", "label": "brand-secondary", "value": "#1A73E8" }
4005
+
4006
+ Delete a variable:
4007
+ { "action": "delete", "id": "abc123" }
4008
+ `);
4009
+ prompt.instruction(
4010
+ "Always read [elementor://global-variables] before creating to check existing variables and avoid duplicate labels."
4011
+ );
4012
+ return prompt.prompt();
4013
+ };
3923
4014
 
3924
4015
  // src/mcp/variables-resource.ts
3925
4016
  var import_editor_v1_adapters6 = require("@elementor/editor-v1-adapters");
@@ -3956,22 +4047,59 @@ var initVariablesResource = (variablesMcpEntry, canvasMcpEntry) => {
3956
4047
  };
3957
4048
  }
3958
4049
  );
3959
- window.addEventListener("variables:updated", notifyGlobalVariablesUpdated);
4050
+ window.addEventListener(STORAGE_UPDATED_EVENT, notifyGlobalVariablesUpdated);
3960
4051
  (0, import_editor_v1_adapters6.__privateListenTo)((0, import_editor_v1_adapters6.commandEndEvent)("document/save/update"), notifyGlobalVariablesUpdated);
3961
4052
  });
3962
4053
  };
3963
4054
 
3964
4055
  // src/mcp/manage-variable-tool.ts
4056
+ var VARIABLE_TYPES = {
4057
+ COLOR: "global-color-variable",
4058
+ FONT: "global-font-variable",
4059
+ SIZE: "global-size-variable",
4060
+ CUSTOM_SIZE: "global-custom-size-variable"
4061
+ };
4062
+ var LENGTH_UNIT_PATTERN = /^(auto|\d+(\.\d+)?(px|rem|em|vh|vw|%|ch|s|ms))$/i;
4063
+ var COLOR_PATTERN = /^(#[0-9a-f]{3,8}|rgba?\(|hsl)/i;
4064
+ function validateValueForType(type, value) {
4065
+ if (type === VARIABLE_TYPES.FONT && LENGTH_UNIT_PATTERN.test(value.trim())) {
4066
+ 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.`;
4067
+ }
4068
+ if (type === VARIABLE_TYPES.COLOR && !COLOR_PATTERN.test(value.trim())) {
4069
+ return `Color variable value should be a CSS color (e.g. "#FF0000"), got "${value}".`;
4070
+ }
4071
+ if (type === VARIABLE_TYPES.SIZE && !LENGTH_UNIT_PATTERN.test(value.trim())) {
4072
+ return `Size variable value should include a CSS unit (e.g. "16px") or be "auto", got "${value}".`;
4073
+ }
4074
+ return null;
4075
+ }
3965
4076
  var initManageVariableTool = (reg) => {
3966
- const { addTool } = reg;
4077
+ const { addTool, resource } = reg;
4078
+ resource(
4079
+ "manage-global-variable-guide",
4080
+ MANAGE_VARIABLES_GUIDE_URI,
4081
+ {
4082
+ title: "Manage Global Variable Guide",
4083
+ description: "Detailed guide for using the manage-global-variable tool",
4084
+ mimeType: "text/plain"
4085
+ },
4086
+ async (uri) => ({
4087
+ contents: [{ uri: uri.href, mimeType: "text/plain", text: generateVariablesPrompt() }]
4088
+ })
4089
+ );
3967
4090
  addTool({
3968
4091
  name: "manage-global-variable",
4092
+ description: "Manage V4 global variables (color, font, size). Read the guide resource before use.",
3969
4093
  schema: {
3970
4094
  action: import_schema3.z.enum(["create", "update", "delete"]).describe("Operation to perform"),
3971
- id: import_schema3.z.string().optional().describe("Variable id (required for update/delete). Get from list-global-variables."),
3972
- type: import_schema3.z.string().optional().describe('Variable type: "global-color-variable" or "global-font-variable" (required for create)'),
3973
- label: import_schema3.z.string().optional().describe("Variable label (required for create/update)"),
3974
- value: import_schema3.z.string().optional().describe("Variable value (required for create/update)")
4095
+ id: import_schema3.z.string().optional().describe("Variable id \u2014 required for update/delete. Get from the global-variables resource."),
4096
+ type: import_schema3.z.string().optional().describe(
4097
+ '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.'
4098
+ ),
4099
+ label: import_schema3.z.string().optional().describe("Variable label (lowercase, dash-separated) \u2014 required for create/update."),
4100
+ value: import_schema3.z.string().optional().describe(
4101
+ '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.'
4102
+ )
3975
4103
  },
3976
4104
  outputSchema: {
3977
4105
  status: import_schema3.z.enum(["ok"]).describe("Operation status"),
@@ -3982,32 +4110,22 @@ var initManageVariableTool = (reg) => {
3982
4110
  speedPriority: 0.75
3983
4111
  },
3984
4112
  requiredResources: [
4113
+ { uri: MANAGE_VARIABLES_GUIDE_URI, description: "Full guide for variable types, naming rules, and usage" },
3985
4114
  {
3986
4115
  uri: GLOBAL_VARIABLES_URI,
3987
- description: "Global variables"
4116
+ description: "Current global variables \u2014 check before creating to avoid duplicates"
3988
4117
  }
3989
4118
  ],
3990
- description: `Manages global variables (create/update/delete). Existing variables available in resources.
3991
- CREATE: requires type, label, value. Ensure label is unique.
3992
- UPDATE: requires id, label, value. When renaming: keep existing value. When updating value: keep exact label.
3993
- DELETE: requires id. DESTRUCTIVE - confirm with user first.
3994
-
3995
- # NAMING - IMPORTANT
3996
- the variables names should ALWAYS be lowercased and dashed spaced. example: "Headline Primary" should be "headline-primary"
3997
- `,
4119
+ isDestructive: true,
3998
4120
  handler: async (params) => {
3999
4121
  const operations = getServiceActions(service);
4000
4122
  const op = operations[params.action];
4001
4123
  if (op) {
4002
4124
  await op(params);
4003
- return {
4004
- status: "ok"
4005
- };
4125
+ return { status: "ok" };
4006
4126
  }
4007
4127
  throw new Error(`Unknown action ${params.action}`);
4008
- },
4009
- isDestructive: true
4010
- // Because delete is destructive
4128
+ }
4011
4129
  });
4012
4130
  };
4013
4131
  function getServiceActions(svc) {
@@ -4016,10 +4134,17 @@ function getServiceActions(svc) {
4016
4134
  if (!type || !label || !value) {
4017
4135
  throw new Error("Create requires type, label, and value");
4018
4136
  }
4137
+ if ((type === VARIABLE_TYPES.SIZE || type === VARIABLE_TYPES.CUSTOM_SIZE) && !(0, import_utils4.isProActive)()) {
4138
+ throw new Error("Creating size variables requires Elementor Pro.");
4139
+ }
4019
4140
  const labelError = validateLabel(label);
4020
4141
  if (labelError) {
4021
4142
  throw new Error(labelError);
4022
4143
  }
4144
+ const valueError = validateValueForType(type, value);
4145
+ if (valueError) {
4146
+ throw new Error(valueError);
4147
+ }
4023
4148
  return svc.create({ type, label, value });
4024
4149
  },
4025
4150
  update({ id: id2, label, value }) {
@@ -4030,6 +4155,13 @@ function getServiceActions(svc) {
4030
4155
  if (labelError) {
4031
4156
  throw new Error(labelError);
4032
4157
  }
4158
+ const existingVariable = svc.variables()[id2];
4159
+ if (existingVariable) {
4160
+ const valueError = validateValueForType(existingVariable.type, value);
4161
+ if (valueError) {
4162
+ throw new Error(valueError);
4163
+ }
4164
+ }
4033
4165
  return svc.update(id2, { label, value });
4034
4166
  },
4035
4167
  delete({ id: id2 }) {
@@ -4043,15 +4175,6 @@ function getServiceActions(svc) {
4043
4175
 
4044
4176
  // src/mcp/index.ts
4045
4177
  function initMcp(reg, canvasMcpEntry) {
4046
- const { setMCPDescription } = reg;
4047
- setMCPDescription(
4048
- `Everything related to V4 ( Atomic ) variables.
4049
- # Global variables
4050
- - Create/update/delete global variables
4051
- - Get list of global variables
4052
- - Get details of a global variable
4053
- `
4054
- );
4055
4178
  initManageVariableTool(reg);
4056
4179
  initVariablesResource(reg, canvasMcpEntry);
4057
4180
  }
@@ -4563,7 +4686,15 @@ function init() {
4563
4686
  useProps: usePropVariableAction
4564
4687
  });
4565
4688
  service.init().then(() => {
4566
- initMcp((0, import_editor_mcp.getMCPByDomain)("variables"), (0, import_editor_mcp.getMCPByDomain)("canvas"));
4689
+ const variablesMcpRegistry = (0, import_editor_mcp2.getMCPByDomain)("variables", {
4690
+ instructions: `Everything related to V4 ( Atomic ) variables.
4691
+ # Global variables
4692
+ - Create/update/delete global variables
4693
+ - Get list of global variables
4694
+ - Get details of a global variable
4695
+ `
4696
+ });
4697
+ initMcp(variablesMcpRegistry, (0, import_editor_mcp2.getMCPByDomain)("canvas"));
4567
4698
  });
4568
4699
  (0, import_editor.injectIntoTop)({
4569
4700
  id: "canvas-style-variables-render",