@elementor/editor-global-classes 4.2.0-930 → 4.2.0-932

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-global-classes",
3
- "version": "4.2.0-930",
3
+ "version": "4.2.0-932",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,29 +39,29 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.2.0-930",
43
- "@elementor/editor-current-user": "4.2.0-930",
44
- "@elementor/editor-documents": "4.2.0-930",
45
- "@elementor/editor-editing-panel": "4.2.0-930",
46
- "@elementor/editor-mcp": "4.2.0-930",
47
- "@elementor/editor-panels": "4.2.0-930",
48
- "@elementor/editor-props": "4.2.0-930",
49
- "@elementor/editor-variables": "4.2.0-930",
50
- "@elementor/editor-styles": "4.2.0-930",
51
- "@elementor/editor-canvas": "4.2.0-930",
52
- "@elementor/editor-styles-repository": "4.2.0-930",
53
- "@elementor/editor-ui": "4.2.0-930",
54
- "@elementor/editor-v1-adapters": "4.2.0-930",
55
- "@elementor/http-client": "4.2.0-930",
42
+ "@elementor/editor": "4.2.0-932",
43
+ "@elementor/editor-current-user": "4.2.0-932",
44
+ "@elementor/editor-documents": "4.2.0-932",
45
+ "@elementor/editor-editing-panel": "4.2.0-932",
46
+ "@elementor/editor-mcp": "4.2.0-932",
47
+ "@elementor/editor-panels": "4.2.0-932",
48
+ "@elementor/editor-props": "4.2.0-932",
49
+ "@elementor/editor-variables": "4.2.0-932",
50
+ "@elementor/editor-styles": "4.2.0-932",
51
+ "@elementor/editor-canvas": "4.2.0-932",
52
+ "@elementor/editor-styles-repository": "4.2.0-932",
53
+ "@elementor/editor-ui": "4.2.0-932",
54
+ "@elementor/editor-v1-adapters": "4.2.0-932",
55
+ "@elementor/http-client": "4.2.0-932",
56
56
  "@elementor/icons": "~1.75.1",
57
- "@elementor/query": "4.2.0-930",
58
- "@elementor/schema": "4.2.0-930",
59
- "@elementor/store": "4.2.0-930",
57
+ "@elementor/query": "4.2.0-932",
58
+ "@elementor/schema": "4.2.0-932",
59
+ "@elementor/store": "4.2.0-932",
60
60
  "@elementor/ui": "1.37.5",
61
- "@elementor/utils": "4.2.0-930",
61
+ "@elementor/utils": "4.2.0-932",
62
62
  "@tanstack/react-virtual": "^3.13.24",
63
63
  "@wordpress/i18n": "^5.13.0",
64
- "@elementor/events": "4.2.0-930"
64
+ "@elementor/events": "4.2.0-932"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "react": "^18.3.1",
@@ -45,7 +45,7 @@ export default function initMcpApplyUnapplyGlobalClasses( server: MCPRegistryEnt
45
45
  doApplyClasses( elementId, [ ...appliedClasses, classId ] );
46
46
  return {
47
47
  llm_instructions:
48
- 'Please check the element-configuration, find DUPLICATES in the style schema that are in the class, and remove them',
48
+ 'Please check the element configuration, find inline styles duplicated by the applied global class, and remove them',
49
49
  result: `Class ${ classId } applied to element ${ elementId } successfully.`,
50
50
  };
51
51
  },
@@ -1,10 +1,9 @@
1
- import { BREAKPOINTS_SCHEMA_FULL_URI, STYLE_SCHEMA_FULL_URI } from '@elementor/editor-canvas';
1
+ import { BREAKPOINTS_SCHEMA_FULL_URI, convertStyleBlocksToAtomic, type StyleBlock } from '@elementor/editor-canvas';
2
2
  import { type MCPRegistryEntry } from '@elementor/editor-mcp';
3
- import { type Props, Schema } from '@elementor/editor-props';
3
+ import { type Props } from '@elementor/editor-props';
4
4
  import { type BreakpointId } from '@elementor/editor-responsive';
5
- import { getStylesSchema, type StyleDefinitionState } from '@elementor/editor-styles';
5
+ import { type CustomCss, type StyleDefinitionState } from '@elementor/editor-styles';
6
6
  import { type StylesProvider } from '@elementor/editor-styles-repository';
7
- import { type Utils as IUtils } from '@elementor/editor-variables';
8
7
  import { z } from '@elementor/schema';
9
8
 
10
9
  import { globalClassesStylesProvider } from '../global-classes-styles-provider';
@@ -12,9 +11,6 @@ import { loadExistingClasses } from '../load-existing-classes';
12
11
  import { saveGlobalClasses } from '../save-global-classes';
13
12
  import { GLOBAL_CLASSES_URI } from './classes-resource';
14
13
 
15
- // TODO: see https://elementor.atlassian.net/browse/ED-22513 for better cross-module access
16
- type XElementor = z.infer< z.ZodAny >;
17
-
18
14
  const schema = {
19
15
  action: z.enum( [ 'create', 'modify', 'delete' ] ).describe( 'Operation to perform' ),
20
16
  classId: z
@@ -22,43 +18,18 @@ const schema = {
22
18
  .optional()
23
19
  .describe( 'Global class ID (required for modify). Get from elementor://global-classes resource.' ),
24
20
  globalClassName: z.string().optional().describe( 'Global class name (required for create)' ),
25
- props: z.object( {
26
- default: z
27
- .record(
28
- z.string().describe( 'The style property name' ),
29
- z.any().describe( `The style PropValue, refer to [${ STYLE_SCHEMA_FULL_URI }] how to generate values` )
30
- )
31
- .describe(
32
- 'An object record containing style property names and their new values. MUST contain at least one property — empty objects are rejected.'
33
- ),
34
- hover: z
35
- .record(
36
- z.string().describe( 'The style property name' ),
37
- z.any().describe( `The style PropValue, refer to [${ STYLE_SCHEMA_FULL_URI }] how to generate values` )
38
- )
39
- .describe(
40
- 'An object record containing style property names and their new values to be set on the element. for :hover css state. optional'
41
- )
42
- .optional(),
43
- focus: z
44
- .record(
45
- z.string().describe( 'The style property name' ),
46
- z.any().describe( `The style PropValue, refer to [${ STYLE_SCHEMA_FULL_URI }] how to generate values` )
47
- )
48
- .describe(
49
- 'An object record containing style property names and their new values to be set on the element. for :focus css state. optional'
50
- )
51
- .optional(),
52
- active: z
53
- .record(
54
- z.string().describe( 'The style property name' ),
55
- z.any().describe( `The style PropValue, refer to [${ STYLE_SCHEMA_FULL_URI }] how to generate values` )
56
- )
57
- .describe(
58
- 'An object record containing style property names and their new values to be set on the element. for :active css state. optional'
59
- )
60
- .optional(),
61
- } ),
21
+ style: z
22
+ .object( {
23
+ default: z
24
+ .string()
25
+ .describe( 'Plaintext CSS for the default state. MUST be non-empty blank strings are rejected.' ),
26
+ hover: z.string().describe( 'Plaintext CSS for the :hover state. optional' ).optional(),
27
+ focus: z.string().describe( 'Plaintext CSS for the :focus state. optional' ).optional(),
28
+ active: z.string().describe( 'Plaintext CSS for the :active state. optional' ).optional(),
29
+ } )
30
+ .describe(
31
+ 'Plaintext CSS per pseudo-state. All states are converted in one bulk request; unconvertible declarations are stored as custom CSS.'
32
+ ),
62
33
  breakpoint: z
63
34
  .nullable( z.string().describe( 'Responsive breakpoint name for styles. Defaults to desktop (null).' ) )
64
35
  .default( null )
@@ -74,9 +45,13 @@ const outputSchema = {
74
45
  type InputSchema = z.infer< ReturnType< typeof z.object< typeof schema > > >;
75
46
  type OutputSchema = z.infer< ReturnType< typeof z.object< typeof outputSchema > > >;
76
47
 
48
+ type ConvertedStateStyle = {
49
+ props: Props;
50
+ customCss: CustomCss | null;
51
+ };
52
+
77
53
  const handler = async ( input: InputSchema ): Promise< OutputSchema > => {
78
- const { action, classId: rawClassId, globalClassName, props: rawProps, breakpoint } = input;
79
- const propsWithStates = rawProps as unknown as Record< NonNullable< StyleDefinitionState >, Props >;
54
+ const { action, classId: rawClassId, globalClassName, style: rawStyle, breakpoint } = input;
80
55
  let classId = rawClassId;
81
56
  if ( action === 'create' && ! globalClassName ) {
82
57
  return {
@@ -107,55 +82,25 @@ const handler = async ( input: InputSchema ): Promise< OutputSchema > => {
107
82
  };
108
83
  }
109
84
 
110
- const errors: string[] = [];
111
- const stylesSchema = getStylesSchema();
112
- const validProps = Object.keys( stylesSchema );
113
- Object.values( propsWithStates ).forEach( ( props ) => {
114
- Object.keys( props ).forEach( ( key ) => {
115
- const propType = stylesSchema[ key ];
116
- if ( ! propType ) {
117
- errors.push( `Property "${ key }" does not exist in styles schema.` );
118
- return;
119
- }
120
- const { valid, jsonSchema } = Schema.validatePropValue( propType, props[ key ] );
121
- if ( ! valid ) {
122
- errors.push( `- Property "${ key }" has invalid value\n Expected schema: ${ jsonSchema }\n` );
123
- }
124
- } );
125
- } );
85
+ const styleBlocks = collectNonEmptyStyleBlocks( rawStyle );
126
86
 
127
- if ( action !== 'delete' ) {
128
- const hasAnyProps = Object.values( propsWithStates ).some(
129
- ( stateProps ) => Object.keys( stateProps ).length > 0
87
+ if ( action !== 'delete' && Object.keys( styleBlocks ).length === 0 ) {
88
+ throw new Error(
89
+ 'Style must not be empty. Provide plaintext CSS per state.\n\nExample: style.default = "display: flex; flex-direction: column; gap: 1rem;"'
130
90
  );
131
- if ( ! hasAnyProps ) {
132
- throw new Error(
133
- `Props must not be empty. Each prop must be a PropValue object from the style schema.\n\nExample: { "display": { "$$type": "string", "value": "flex" }, "flex-direction": { "$$type": "string", "value": "column" } }\n\n${ STYLE_SCHEMA_FULL_URI } to get the allowed values (look at the "value" enum in the schema response), then construct { "$$type": "string", "value": "<chosen value>" } for each property.\nAvailable Properties: ${ validProps.join(
134
- ', '
135
- ) }`
136
- );
137
- }
138
91
  }
139
92
 
140
- if ( errors.length > 0 ) {
141
- throw new Error(
142
- `Validation errors:\n${ errors.join( '\n' ) }\nAvailable Properties: ${ validProps.join(
143
- ', '
144
- ) }\nUpdate your input and try again.`
93
+ let convertedByState: Record< string, ConvertedStateStyle > = {};
94
+ if ( action !== 'delete' ) {
95
+ const conversionResults = await convertStyleBlocksToAtomic( styleBlocks );
96
+ convertedByState = Object.fromEntries(
97
+ Object.entries( conversionResults ).map( ( [ state, { props, customCss } ] ) => [
98
+ state,
99
+ { props: props as Props, customCss: toStoredCustomCss( customCss ) },
100
+ ] )
145
101
  );
146
102
  }
147
103
 
148
- // TODO: see https://elementor.atlassian.net/browse/ED-22513 for better cross-module access
149
- const Utils = ( ( ( window as XElementor ).elementorV2 as XElementor ).editorVariables as XElementor )
150
- .Utils as typeof IUtils;
151
- Object.values( propsWithStates ).forEach( ( props ) => {
152
- Object.keys( props ).forEach( ( key ) => {
153
- props[ key ] = Schema.adjustLlmPropValueSchema( props[ key ], {
154
- transformers: Utils.globalVariablesLLMResolvers,
155
- } );
156
- } );
157
- } );
158
-
159
104
  const breakpointValue = breakpoint ?? 'desktop';
160
105
  let result = {
161
106
  status: 'error',
@@ -176,18 +121,18 @@ const handler = async ( input: InputSchema ): Promise< OutputSchema > => {
176
121
  }
177
122
 
178
123
  let currentAction = action;
179
- for await ( const [ state, props ] of Object.entries( propsWithStates ) ) {
124
+ for await ( const [ state, { props, customCss } ] of Object.entries( convertedByState ) ) {
180
125
  switch ( currentAction ) {
181
126
  case 'create':
182
127
  const newClassId = await attemptCreate( {
183
128
  props,
129
+ customCss,
184
130
  className: globalClassName,
185
131
  stylesProvider: globalClassesStylesProvider,
186
132
  breakpoint: breakpointValue as BreakpointId,
187
133
  state: state as StyleDefinitionState,
188
134
  } );
189
135
  if ( newClassId && currentAction === 'create' ) {
190
- // NOTE: for multiple iterations as the state changes, the next execution would be update an existing class
191
136
  currentAction = 'modify';
192
137
  classId = newClassId;
193
138
  result = {
@@ -202,6 +147,7 @@ const handler = async ( input: InputSchema ): Promise< OutputSchema > => {
202
147
  const updated = await attemptUpdate( {
203
148
  classId,
204
149
  props,
150
+ customCss,
205
151
  stylesProvider: globalClassesStylesProvider,
206
152
  breakpoint: breakpointValue as BreakpointId,
207
153
  state: state as StyleDefinitionState,
@@ -232,21 +178,18 @@ export const initManageGlobalClasses = ( reg: MCPRegistryEntry ) => {
232
178
  name: 'manage-global-classes',
233
179
  requiredResources: [
234
180
  { uri: GLOBAL_CLASSES_URI, description: 'Global classes list' },
235
- { uri: STYLE_SCHEMA_FULL_URI, description: 'Style schema resources' },
236
181
  { uri: BREAKPOINTS_SCHEMA_FULL_URI, description: 'Breakpoints list' },
237
182
  ],
238
183
  description: `Create or modify global classes for reusable design-system styling. Class names must reflect purpose (e.g. heading-primary, button-cta). Create classes BEFORE applying them. Do NOT create classes for one-off styles.
239
184
 
240
- IMPORTANT: props must contain actual CSS property values — never pass empty objects.
241
- Fetch ${ STYLE_SCHEMA_FULL_URI } to get the allowed values for each property, then use them to build the props object.
185
+ IMPORTANT: style must contain plaintext CSS rules per state — never pass empty strings.
186
+ CSS is converted server-side in one bulk request; any declaration that cannot be converted is stored as the class custom CSS.
242
187
 
243
188
  Example — creating a flex column class:
244
- props.default = {
245
- "display": { "$$type": "string", "value": "flex" },
246
- "flex-direction": { "$$type": "string", "value": "column" }
247
- }
189
+ style.default = "display: flex; flex-direction: column; gap: 1rem;"
248
190
 
249
- The style schema returns a JSON Schema. Extract the "value" enum to pick the right value, then construct { "$$type": "string", "value": "<picked value>" }.`,
191
+ Example hover state:
192
+ style.hover = "opacity: 0.85;"`,
250
193
  schema,
251
194
  outputSchema,
252
195
  handler,
@@ -259,11 +202,12 @@ type Opts = {
259
202
  classId?: string;
260
203
  breakpoint: BreakpointId;
261
204
  props: Props;
205
+ customCss: CustomCss | null;
262
206
  state: StyleDefinitionState;
263
207
  };
264
208
 
265
209
  async function attemptCreate( opts: Opts ) {
266
- const { props, breakpoint, className, stylesProvider, state } = opts;
210
+ const { props, customCss, breakpoint, className, stylesProvider, state } = opts;
267
211
  const { create, delete: deleteClass } = stylesProvider.actions;
268
212
  if ( ! className ) {
269
213
  throw new Error( 'Global class name is a required for creation' );
@@ -277,7 +221,7 @@ async function attemptCreate( opts: Opts ) {
277
221
  breakpoint,
278
222
  state: ( state as string ) === 'default' ? null : state,
279
223
  },
280
- custom_css: null,
224
+ custom_css: customCss,
281
225
  props,
282
226
  },
283
227
  ] );
@@ -291,7 +235,7 @@ async function attemptCreate( opts: Opts ) {
291
235
  }
292
236
 
293
237
  async function attemptUpdate( opts: Opts ) {
294
- const { props, breakpoint, classId, stylesProvider, state } = opts;
238
+ const { props, customCss, breakpoint, classId, stylesProvider, state } = opts;
295
239
  const { updateProps, update } = stylesProvider.actions;
296
240
  if ( ! classId ) {
297
241
  throw new Error( 'Class ID is required for modification' );
@@ -305,6 +249,7 @@ async function attemptUpdate( opts: Opts ) {
305
249
  updateProps( {
306
250
  id: classId,
307
251
  props,
252
+ custom_css: customCss,
308
253
  meta: {
309
254
  breakpoint,
310
255
  state: ( state as string ) === 'default' ? null : state,
@@ -342,3 +287,20 @@ async function attemptDelete( opts: Pick< Opts, 'classId' | 'stylesProvider' > )
342
287
  await saveGlobalClasses( { context: 'frontend' } );
343
288
  return true;
344
289
  }
290
+
291
+ function collectNonEmptyStyleBlocks( style: InputSchema[ 'style' ] ): Record< string, StyleBlock > {
292
+ const blocks: Record< string, StyleBlock > = {};
293
+ Object.entries( style ).forEach( ( [ state, cssText ] ) => {
294
+ if ( cssText?.trim() ) {
295
+ blocks[ state ] = cssText.trim();
296
+ }
297
+ } );
298
+ return blocks;
299
+ }
300
+
301
+ function toStoredCustomCss( customCss: string ): CustomCss | null {
302
+ if ( ! customCss?.trim() ) {
303
+ return null;
304
+ }
305
+ return { raw: btoa( customCss ) };
306
+ }