@elementor/editor-global-classes 4.2.0-931 → 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-
|
|
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-
|
|
43
|
-
"@elementor/editor-current-user": "4.2.0-
|
|
44
|
-
"@elementor/editor-documents": "4.2.0-
|
|
45
|
-
"@elementor/editor-editing-panel": "4.2.0-
|
|
46
|
-
"@elementor/editor-mcp": "4.2.0-
|
|
47
|
-
"@elementor/editor-panels": "4.2.0-
|
|
48
|
-
"@elementor/editor-props": "4.2.0-
|
|
49
|
-
"@elementor/editor-variables": "4.2.0-
|
|
50
|
-
"@elementor/editor-styles": "4.2.0-
|
|
51
|
-
"@elementor/editor-canvas": "4.2.0-
|
|
52
|
-
"@elementor/editor-styles-repository": "4.2.0-
|
|
53
|
-
"@elementor/editor-ui": "4.2.0-
|
|
54
|
-
"@elementor/editor-v1-adapters": "4.2.0-
|
|
55
|
-
"@elementor/http-client": "4.2.0-
|
|
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-
|
|
58
|
-
"@elementor/schema": "4.2.0-
|
|
59
|
-
"@elementor/store": "4.2.0-
|
|
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-
|
|
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-
|
|
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
|
|
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,
|
|
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
|
|
3
|
+
import { type Props } from '@elementor/editor-props';
|
|
4
4
|
import { type BreakpointId } from '@elementor/editor-responsive';
|
|
5
|
-
import {
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)
|
|
31
|
-
.describe(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
129
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
) }
|
|
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(
|
|
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:
|
|
241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
+
}
|