@elementor/editor-global-classes 4.3.0-999 → 4.4.0-1065
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 +137 -322
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +123 -308
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -21
- package/src/capabilities.ts +6 -12
- package/src/mcp-integration/apply-global-class-guide-prompt.ts +3 -3
- package/src/mcp-integration/index.ts +2 -2
- package/src/mcp-integration/manage-classes-tool.ts +99 -0
- package/src/store.ts +13 -2
- package/src/mcp-integration/mcp-manage-global-classes.ts +0 -314
package/dist/index.mjs
CHANGED
|
@@ -144,12 +144,16 @@ var slice = createSlice({
|
|
|
144
144
|
state.isDirty = true;
|
|
145
145
|
},
|
|
146
146
|
update(state, { payload }) {
|
|
147
|
+
const oldLabel = state.classLabels[payload.style.id];
|
|
147
148
|
localHistory.next(state.data);
|
|
148
149
|
const style = state.data.items[payload.style.id];
|
|
149
150
|
const mergedData = {
|
|
150
151
|
...style,
|
|
151
152
|
...payload.style
|
|
152
153
|
};
|
|
154
|
+
if (oldLabel && payload.style.label && payload.style.label !== oldLabel) {
|
|
155
|
+
state.classLabels[payload.style.id] = payload.style.label;
|
|
156
|
+
}
|
|
153
157
|
state.data.items[payload.style.id] = mergedData;
|
|
154
158
|
state.isDirty = true;
|
|
155
159
|
},
|
|
@@ -251,9 +255,15 @@ var slice = createSlice({
|
|
|
251
255
|
updateAfterTemplateImport(state, {
|
|
252
256
|
payload
|
|
253
257
|
}) {
|
|
254
|
-
state.initialData.frontend.items = {
|
|
258
|
+
state.initialData.frontend.items = {
|
|
259
|
+
...state.initialData.frontend.items,
|
|
260
|
+
...payload.addedItems
|
|
261
|
+
};
|
|
255
262
|
state.initialData.frontend.order = [...state.initialData.frontend.order, ...payload.addedIdsOrder];
|
|
256
|
-
state.initialData.preview.items = {
|
|
263
|
+
state.initialData.preview.items = {
|
|
264
|
+
...state.initialData.preview.items,
|
|
265
|
+
...payload.addedItems
|
|
266
|
+
};
|
|
257
267
|
state.initialData.preview.order = [...state.initialData.preview.order, ...payload.addedIdsOrder];
|
|
258
268
|
state.data.items = { ...state.data.items, ...payload.addedItems };
|
|
259
269
|
state.data.order = [...state.data.order, ...payload.addedIdsOrder];
|
|
@@ -1988,7 +1998,7 @@ var GlobalClassesList = ({
|
|
|
1988
1998
|
search: { debouncedValue: searchValue }
|
|
1989
1999
|
} = useSearchAndFilters();
|
|
1990
2000
|
const cssClasses = useOrderedClasses();
|
|
1991
|
-
const
|
|
2001
|
+
const dispatch8 = useDispatch();
|
|
1992
2002
|
const filters = useFilters();
|
|
1993
2003
|
const [draggedItemId, setDraggedItemId] = useState5(null);
|
|
1994
2004
|
const [loading, setLoading] = useState5({});
|
|
@@ -2021,22 +2031,22 @@ var GlobalClassesList = ({
|
|
|
2021
2031
|
}
|
|
2022
2032
|
});
|
|
2023
2033
|
useEffect2(() => {
|
|
2024
|
-
const
|
|
2034
|
+
const handler = (event) => {
|
|
2025
2035
|
if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
|
|
2026
2036
|
event.stopImmediatePropagation();
|
|
2027
2037
|
event.preventDefault();
|
|
2028
2038
|
if (event.shiftKey) {
|
|
2029
|
-
|
|
2039
|
+
dispatch8(slice.actions.redo());
|
|
2030
2040
|
return;
|
|
2031
2041
|
}
|
|
2032
|
-
|
|
2042
|
+
dispatch8(slice.actions.undo());
|
|
2033
2043
|
}
|
|
2034
2044
|
};
|
|
2035
|
-
window.addEventListener("keydown",
|
|
2045
|
+
window.addEventListener("keydown", handler, {
|
|
2036
2046
|
capture: true
|
|
2037
2047
|
});
|
|
2038
|
-
return () => window.removeEventListener("keydown",
|
|
2039
|
-
}, [
|
|
2048
|
+
return () => window.removeEventListener("keydown", handler);
|
|
2049
|
+
}, [dispatch8]);
|
|
2040
2050
|
if (!cssClasses?.length) {
|
|
2041
2051
|
return /* @__PURE__ */ React17.createElement(EmptyState, null);
|
|
2042
2052
|
}
|
|
@@ -2096,7 +2106,7 @@ var GlobalClassesList = ({
|
|
|
2096
2106
|
source: "class-manager"
|
|
2097
2107
|
});
|
|
2098
2108
|
void await loadExistingClasses([cssClass.id]);
|
|
2099
|
-
|
|
2109
|
+
dispatch8(
|
|
2100
2110
|
slice.actions.update({
|
|
2101
2111
|
style: {
|
|
2102
2112
|
id: cssClass.id,
|
|
@@ -2122,7 +2132,7 @@ var GlobalClassesList = ({
|
|
|
2122
2132
|
} else if (newValue && onStartSyncRequest) {
|
|
2123
2133
|
onStartSyncRequest(id);
|
|
2124
2134
|
} else {
|
|
2125
|
-
|
|
2135
|
+
dispatch8(
|
|
2126
2136
|
slice.actions.update({
|
|
2127
2137
|
style: {
|
|
2128
2138
|
id,
|
|
@@ -2149,10 +2159,10 @@ var StyledHeader = styled6(Typography8)(({ theme, variant }) => ({
|
|
|
2149
2159
|
}
|
|
2150
2160
|
}));
|
|
2151
2161
|
var useReorder = (draggedItemId, setDraggedItemId, draggedItemLabel) => {
|
|
2152
|
-
const
|
|
2162
|
+
const dispatch8 = useDispatch();
|
|
2153
2163
|
const order = useClassesOrder();
|
|
2154
2164
|
const reorder = (newIds) => {
|
|
2155
|
-
|
|
2165
|
+
dispatch8(slice.actions.setOrder(newIds));
|
|
2156
2166
|
if (draggedItemId) {
|
|
2157
2167
|
trackGlobalClasses({
|
|
2158
2168
|
event: "classManagerReorder",
|
|
@@ -2523,19 +2533,14 @@ import {
|
|
|
2523
2533
|
import { __ as __15 } from "@wordpress/i18n";
|
|
2524
2534
|
|
|
2525
2535
|
// src/capabilities.ts
|
|
2526
|
-
import { isExperimentActive } from "@elementor/editor-v1-adapters";
|
|
2527
|
-
var EXPERIMENT_KEY = "global_classes_should_enforce_capabilities";
|
|
2528
2536
|
var UPDATE_CLASS_CAPABILITY_KEY = "elementor_global_classes_update_class";
|
|
2529
2537
|
var getCapabilities = () => {
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
updateProps: UPDATE_CLASS_CAPABILITY_KEY
|
|
2537
|
-
};
|
|
2538
|
-
}
|
|
2538
|
+
return {
|
|
2539
|
+
update: UPDATE_CLASS_CAPABILITY_KEY,
|
|
2540
|
+
create: UPDATE_CLASS_CAPABILITY_KEY,
|
|
2541
|
+
delete: UPDATE_CLASS_CAPABILITY_KEY,
|
|
2542
|
+
updateProps: UPDATE_CLASS_CAPABILITY_KEY
|
|
2543
|
+
};
|
|
2539
2544
|
};
|
|
2540
2545
|
|
|
2541
2546
|
// src/global-classes-styles-provider.ts
|
|
@@ -2788,7 +2793,7 @@ import { useEffect as useEffect5 } from "react";
|
|
|
2788
2793
|
import { GLOBAL_STYLES_IMPORTED_EVENT } from "@elementor/editor-canvas";
|
|
2789
2794
|
import { __useDispatch as useDispatch2 } from "@elementor/store";
|
|
2790
2795
|
function GlobalStylesImportListener() {
|
|
2791
|
-
const
|
|
2796
|
+
const dispatch8 = useDispatch2();
|
|
2792
2797
|
useEffect5(() => {
|
|
2793
2798
|
const handleGlobalStylesImported = async (event) => {
|
|
2794
2799
|
const customEvent = event;
|
|
@@ -2797,7 +2802,7 @@ function GlobalStylesImportListener() {
|
|
|
2797
2802
|
loadCurrentDocumentClasses();
|
|
2798
2803
|
return;
|
|
2799
2804
|
}
|
|
2800
|
-
|
|
2805
|
+
dispatch8(
|
|
2801
2806
|
slice.actions.updateAfterTemplateImport({
|
|
2802
2807
|
addedItems: globalClasses.added_items,
|
|
2803
2808
|
addedIdsOrder: globalClasses.added_items_order,
|
|
@@ -2809,7 +2814,7 @@ function GlobalStylesImportListener() {
|
|
|
2809
2814
|
return () => {
|
|
2810
2815
|
window.removeEventListener(GLOBAL_STYLES_IMPORTED_EVENT, handleGlobalStylesImported);
|
|
2811
2816
|
};
|
|
2812
|
-
}, [
|
|
2817
|
+
}, [dispatch8]);
|
|
2813
2818
|
return null;
|
|
2814
2819
|
}
|
|
2815
2820
|
|
|
@@ -2826,9 +2831,78 @@ function PopulateStore() {
|
|
|
2826
2831
|
return null;
|
|
2827
2832
|
}
|
|
2828
2833
|
|
|
2834
|
+
// src/mcp-integration/manage-classes-tool.ts
|
|
2835
|
+
import { httpService as httpService2 } from "@elementor/http-client";
|
|
2836
|
+
import { z } from "@elementor/schema";
|
|
2837
|
+
import { __dispatch as dispatch7 } from "@elementor/store";
|
|
2838
|
+
var MCP_PROXY_URL = "elementor/v1/mcp-proxy";
|
|
2839
|
+
var TOOL_NAME = "manage-classes";
|
|
2840
|
+
var initManageClassesTool = (reg) => {
|
|
2841
|
+
const { addTool } = reg;
|
|
2842
|
+
addTool({
|
|
2843
|
+
name: TOOL_NAME,
|
|
2844
|
+
description: "Manage V4 global CSS classes on the active kit. Bulk create, update, or delete using raw CSS declarations (up to 50 operations). Duplicate labels are auto-renamed with a DUP_ prefix.",
|
|
2845
|
+
schema: {
|
|
2846
|
+
action: z.enum(["create", "update", "delete"]),
|
|
2847
|
+
id: z.string().optional().describe("Class id \u2014 required for update/delete. Get from the global-classes resource."),
|
|
2848
|
+
label: z.string().optional().describe("Class label (lowercase, dash-separated) \u2014 required for create/update."),
|
|
2849
|
+
css: z.string().optional().describe(
|
|
2850
|
+
'Plain CSS string. Supports &:hover/&:focus/&:active nesting and @media(--breakpoint) blocks. font-family must be a single Google Font name (no fallback stacks). In patch mode: "prop: null" removes that prop; "all: null" wipes the variant.'
|
|
2851
|
+
),
|
|
2852
|
+
mode: z.enum(["patch", "replace"]).optional().describe(
|
|
2853
|
+
"Merge strategy for update \u2014 patch (default): merge incoming props with existing; replace: discard all existing variants for the affected breakpoints."
|
|
2854
|
+
)
|
|
2855
|
+
},
|
|
2856
|
+
outputSchema: {
|
|
2857
|
+
status: z.enum(["ok"]).describe("Operation status"),
|
|
2858
|
+
id: z.string().optional().describe("ID of the affected class \u2014 use for subsequent update/delete calls."),
|
|
2859
|
+
label: z.string().optional().describe("Final label of the class after any auto-rename.")
|
|
2860
|
+
},
|
|
2861
|
+
requiredResources: [
|
|
2862
|
+
{
|
|
2863
|
+
uri: GLOBAL_CLASSES_URI,
|
|
2864
|
+
description: "Current global classes \u2014 check before creating to avoid duplicates"
|
|
2865
|
+
}
|
|
2866
|
+
],
|
|
2867
|
+
isDestructive: true,
|
|
2868
|
+
handler: async (params) => {
|
|
2869
|
+
const { data } = await httpService2().post(MCP_PROXY_URL, {
|
|
2870
|
+
tool: TOOL_NAME,
|
|
2871
|
+
input: { operations: [params] }
|
|
2872
|
+
});
|
|
2873
|
+
const result = data.data.results?.[0];
|
|
2874
|
+
const { create, update, delete: del } = globalClassesStylesProvider.actions;
|
|
2875
|
+
switch (params.action) {
|
|
2876
|
+
case "create":
|
|
2877
|
+
if (result && create) {
|
|
2878
|
+
create(result.label, result.variants, result.id);
|
|
2879
|
+
}
|
|
2880
|
+
break;
|
|
2881
|
+
case "update":
|
|
2882
|
+
if (result && update) {
|
|
2883
|
+
update(result);
|
|
2884
|
+
}
|
|
2885
|
+
break;
|
|
2886
|
+
case "delete":
|
|
2887
|
+
if (params.id && del) {
|
|
2888
|
+
del(params.id);
|
|
2889
|
+
}
|
|
2890
|
+
break;
|
|
2891
|
+
}
|
|
2892
|
+
dispatch7(slice.actions.reset({ context: "frontend" }));
|
|
2893
|
+
window.dispatchEvent(new CustomEvent("classes:updated", { detail: { context: "frontend" } }));
|
|
2894
|
+
return {
|
|
2895
|
+
status: "ok",
|
|
2896
|
+
id: result?.id,
|
|
2897
|
+
label: result?.label
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
});
|
|
2901
|
+
};
|
|
2902
|
+
|
|
2829
2903
|
// src/mcp-integration/mcp-apply-unapply-global-classes.ts
|
|
2830
2904
|
import { doApplyClasses, doGetAppliedClasses, doUnapplyClass } from "@elementor/editor-editing-panel";
|
|
2831
|
-
import { z } from "@elementor/schema";
|
|
2905
|
+
import { z as z2 } from "@elementor/schema";
|
|
2832
2906
|
|
|
2833
2907
|
// src/mcp-integration/apply-global-class-guide-prompt.ts
|
|
2834
2908
|
import { toolPrompts } from "@elementor/editor-mcp";
|
|
@@ -2838,8 +2912,8 @@ var generateApplyGlobalClassGuidePrompt = () => {
|
|
|
2838
2912
|
prompt.description("Apply a global class to an element, enabling consistent styling through your design system.");
|
|
2839
2913
|
prompt.instruction(
|
|
2840
2914
|
`## When to use this tool:
|
|
2841
|
-
**ALWAYS use this IMMEDIATELY AFTER
|
|
2842
|
-
- After
|
|
2915
|
+
**ALWAYS use this IMMEDIATELY AFTER creating or updating elements** to apply the global classes you created beforehand:
|
|
2916
|
+
- After adding elements to the canvas, apply semantic classes to them
|
|
2843
2917
|
- When applying consistent typography styles (heading-primary, text-body, etc.)
|
|
2844
2918
|
- When applying theme colors or brand styles (bg-brand, button-cta, etc.)
|
|
2845
2919
|
- When ensuring spacing consistency (spacing-section-large, etc.)
|
|
@@ -2851,7 +2925,7 @@ var generateApplyGlobalClassGuidePrompt = () => {
|
|
|
2851
2925
|
prompt.instruction(
|
|
2852
2926
|
`## Prerequisites:
|
|
2853
2927
|
- **REQUIRED**: Get the list of available global classes from 'elementor://global-classes' resource
|
|
2854
|
-
- **REQUIRED**: Get element IDs from the
|
|
2928
|
+
- **REQUIRED**: Get element IDs from the page structure or selected element resources
|
|
2855
2929
|
- Ensure you have the most up-to-date list of classes applied to the element to avoid duplicates
|
|
2856
2930
|
- Make sure you have the correct class ID that you want to apply`
|
|
2857
2931
|
);
|
|
@@ -2882,12 +2956,12 @@ function initMcpApplyUnapplyGlobalClasses(server) {
|
|
|
2882
2956
|
);
|
|
2883
2957
|
addTool({
|
|
2884
2958
|
schema: {
|
|
2885
|
-
classId:
|
|
2886
|
-
elementId:
|
|
2959
|
+
classId: z2.string().describe("The ID of the class to apply"),
|
|
2960
|
+
elementId: z2.string().describe("The ID of the element to which the class will be applied")
|
|
2887
2961
|
},
|
|
2888
2962
|
outputSchema: {
|
|
2889
|
-
result:
|
|
2890
|
-
llm_instructions:
|
|
2963
|
+
result: z2.string().describe("Result message indicating the success of the apply operation"),
|
|
2964
|
+
llm_instructions: z2.string().describe("Instructions what to do next, Important to follow these instructions!")
|
|
2891
2965
|
},
|
|
2892
2966
|
name: "apply-global-class",
|
|
2893
2967
|
description: `Apply a global class to an element for shared design-system styling. Read the full guide at [${APPLY_GLOBAL_CLASS_GUIDE_URI}].`,
|
|
@@ -2913,11 +2987,11 @@ function initMcpApplyUnapplyGlobalClasses(server) {
|
|
|
2913
2987
|
addTool({
|
|
2914
2988
|
name: "unapply-global-class",
|
|
2915
2989
|
schema: {
|
|
2916
|
-
classId:
|
|
2917
|
-
elementId:
|
|
2990
|
+
classId: z2.string().describe("The ID of the class to unapply"),
|
|
2991
|
+
elementId: z2.string().describe("The ID of the element from which the class will be unapplied")
|
|
2918
2992
|
},
|
|
2919
2993
|
outputSchema: {
|
|
2920
|
-
result:
|
|
2994
|
+
result: z2.string().describe("Result message indicating the success of the unapply operation")
|
|
2921
2995
|
},
|
|
2922
2996
|
description: `Unapply a global class from an element by class ID. Resolve class names to IDs via [${GLOBAL_CLASSES_URI}].`,
|
|
2923
2997
|
requiredResources: [{ description: "Global classes list", uri: GLOBAL_CLASSES_URI }],
|
|
@@ -2935,21 +3009,21 @@ function initMcpApplyUnapplyGlobalClasses(server) {
|
|
|
2935
3009
|
}
|
|
2936
3010
|
|
|
2937
3011
|
// src/mcp-integration/mcp-get-global-class-usages.ts
|
|
2938
|
-
import { z as
|
|
3012
|
+
import { z as z3 } from "@elementor/schema";
|
|
2939
3013
|
function initMcpApplyGetGlobalClassUsages(reg) {
|
|
2940
3014
|
const { addTool } = reg;
|
|
2941
3015
|
const globalClassesUsageSchema = {
|
|
2942
|
-
usages:
|
|
2943
|
-
|
|
2944
|
-
classId:
|
|
3016
|
+
usages: z3.array(
|
|
3017
|
+
z3.object({
|
|
3018
|
+
classId: z3.string().describe(
|
|
2945
3019
|
'The ID of the class, not visible to the user. To retrieve the name of the class, use the "list-global-classes" tool'
|
|
2946
3020
|
),
|
|
2947
|
-
usages:
|
|
2948
|
-
|
|
2949
|
-
pageId:
|
|
2950
|
-
title:
|
|
2951
|
-
total:
|
|
2952
|
-
elements:
|
|
3021
|
+
usages: z3.array(
|
|
3022
|
+
z3.object({
|
|
3023
|
+
pageId: z3.string().describe("The ID of the page where the class is used"),
|
|
3024
|
+
title: z3.string().describe("The title of the page where the class is used"),
|
|
3025
|
+
total: z3.number().describe("The number of times the class is used on this page"),
|
|
3026
|
+
elements: z3.array(z3.string()).describe("List of element IDs using this class on the page")
|
|
2953
3027
|
})
|
|
2954
3028
|
)
|
|
2955
3029
|
})
|
|
@@ -2997,270 +3071,11 @@ function initMcpApplyGetGlobalClassUsages(reg) {
|
|
|
2997
3071
|
});
|
|
2998
3072
|
}
|
|
2999
3073
|
|
|
3000
|
-
// src/mcp-integration/mcp-manage-global-classes.ts
|
|
3001
|
-
import { BREAKPOINTS_SCHEMA_FULL_URI, convertStyleBlocksToAtomic } from "@elementor/editor-canvas";
|
|
3002
|
-
import { dispatchMcpStylesAppliedEvent } from "@elementor/editor-mcp";
|
|
3003
|
-
import { z as z3 } from "@elementor/schema";
|
|
3004
|
-
var schema = {
|
|
3005
|
-
action: z3.enum(["create", "modify", "delete"]).describe("Operation to perform"),
|
|
3006
|
-
classId: z3.string().optional().describe("Global class ID (required for modify). Get from elementor://global-classes resource."),
|
|
3007
|
-
globalClassName: z3.string().optional().describe("Global class name (required for create)"),
|
|
3008
|
-
style: z3.object({
|
|
3009
|
-
default: z3.string().describe("Plaintext CSS for the default state. MUST be non-empty \u2014 blank strings are rejected."),
|
|
3010
|
-
hover: z3.string().describe("Plaintext CSS for the :hover state. optional").optional(),
|
|
3011
|
-
focus: z3.string().describe("Plaintext CSS for the :focus state. optional").optional(),
|
|
3012
|
-
active: z3.string().describe("Plaintext CSS for the :active state. optional").optional()
|
|
3013
|
-
}).describe(
|
|
3014
|
-
"Plaintext CSS per pseudo-state. All states are converted in one bulk request; unconvertible declarations are stored as custom CSS."
|
|
3015
|
-
),
|
|
3016
|
-
breakpoint: z3.nullable(z3.string().describe("Responsive breakpoint name for styles. Defaults to desktop (null).")).default(null).describe("Responsive breakpoint name for styles. Defaults to desktop (null).")
|
|
3017
|
-
};
|
|
3018
|
-
var outputSchema = {
|
|
3019
|
-
status: z3.enum(["ok", "error"]).describe("Operation status"),
|
|
3020
|
-
classId: z3.string().optional().describe("Class ID (returned on create success)"),
|
|
3021
|
-
message: z3.string().optional().describe("Error details if status is error")
|
|
3022
|
-
};
|
|
3023
|
-
var handler = async (input) => {
|
|
3024
|
-
const { action, classId: rawClassId, globalClassName, style: rawStyle, breakpoint } = input;
|
|
3025
|
-
let classId = rawClassId;
|
|
3026
|
-
if (action === "create" && !globalClassName) {
|
|
3027
|
-
return {
|
|
3028
|
-
status: "error",
|
|
3029
|
-
message: "Create requires globalClassName"
|
|
3030
|
-
};
|
|
3031
|
-
}
|
|
3032
|
-
if (action === "modify" && !classId) {
|
|
3033
|
-
return {
|
|
3034
|
-
status: "error",
|
|
3035
|
-
message: "Modify requires classId"
|
|
3036
|
-
};
|
|
3037
|
-
}
|
|
3038
|
-
if (action === "delete" && !classId) {
|
|
3039
|
-
return {
|
|
3040
|
-
status: "error",
|
|
3041
|
-
message: "Delete requires classId"
|
|
3042
|
-
};
|
|
3043
|
-
}
|
|
3044
|
-
const { create, update, delete: deleteClass2 } = globalClassesStylesProvider.actions;
|
|
3045
|
-
if (!create || !update || !deleteClass2) {
|
|
3046
|
-
return {
|
|
3047
|
-
status: "error",
|
|
3048
|
-
message: "Required actions not available"
|
|
3049
|
-
};
|
|
3050
|
-
}
|
|
3051
|
-
const styleBlocks = collectNonEmptyStyleBlocks(rawStyle);
|
|
3052
|
-
if (action !== "delete" && Object.keys(styleBlocks).length === 0) {
|
|
3053
|
-
throw new Error(
|
|
3054
|
-
'Style must not be empty. Provide plaintext CSS per state.\n\nExample: style.default = "display: flex; flex-direction: column; gap: 1rem;"'
|
|
3055
|
-
);
|
|
3056
|
-
}
|
|
3057
|
-
let convertedByState = {};
|
|
3058
|
-
if (action !== "delete") {
|
|
3059
|
-
const conversionResults = await convertStyleBlocksToAtomic(styleBlocks);
|
|
3060
|
-
convertedByState = Object.fromEntries(
|
|
3061
|
-
Object.entries(conversionResults).map(([state, { props, customCss }]) => [
|
|
3062
|
-
state,
|
|
3063
|
-
{ props, customCss: toStoredCustomCss(customCss) }
|
|
3064
|
-
])
|
|
3065
|
-
);
|
|
3066
|
-
}
|
|
3067
|
-
const breakpointValue = breakpoint ?? "desktop";
|
|
3068
|
-
let result = {
|
|
3069
|
-
status: "error",
|
|
3070
|
-
classId: "",
|
|
3071
|
-
message: "unknown error"
|
|
3072
|
-
};
|
|
3073
|
-
try {
|
|
3074
|
-
if (action === "delete") {
|
|
3075
|
-
const deleted = await attemptDelete({
|
|
3076
|
-
classId,
|
|
3077
|
-
stylesProvider: globalClassesStylesProvider
|
|
3078
|
-
});
|
|
3079
|
-
if (deleted) {
|
|
3080
|
-
return { status: "ok", message: `deleted global class with ID ${classId}` };
|
|
3081
|
-
}
|
|
3082
|
-
throw new Error("error deleting class");
|
|
3083
|
-
}
|
|
3084
|
-
let currentAction = action;
|
|
3085
|
-
for await (const [state, { props, customCss }] of Object.entries(convertedByState)) {
|
|
3086
|
-
switch (currentAction) {
|
|
3087
|
-
case "create":
|
|
3088
|
-
const newClassId = await attemptCreate({
|
|
3089
|
-
props,
|
|
3090
|
-
customCss,
|
|
3091
|
-
className: globalClassName,
|
|
3092
|
-
stylesProvider: globalClassesStylesProvider,
|
|
3093
|
-
breakpoint: breakpointValue,
|
|
3094
|
-
state
|
|
3095
|
-
});
|
|
3096
|
-
if (newClassId && currentAction === "create") {
|
|
3097
|
-
currentAction = "modify";
|
|
3098
|
-
classId = newClassId;
|
|
3099
|
-
result = {
|
|
3100
|
-
status: "ok",
|
|
3101
|
-
message: `created global class with ID ${newClassId}`
|
|
3102
|
-
};
|
|
3103
|
-
globalClassesStylesProvider.actions.tracking?.({
|
|
3104
|
-
event: "classCreated",
|
|
3105
|
-
executedBy: "mcp_tool",
|
|
3106
|
-
classId: newClassId
|
|
3107
|
-
});
|
|
3108
|
-
dispatchMcpStylesAppliedEvent({ styleValue: props });
|
|
3109
|
-
} else {
|
|
3110
|
-
throw new Error("error creating class");
|
|
3111
|
-
}
|
|
3112
|
-
break;
|
|
3113
|
-
case "modify":
|
|
3114
|
-
const updated = await attemptUpdate({
|
|
3115
|
-
classId,
|
|
3116
|
-
props,
|
|
3117
|
-
customCss,
|
|
3118
|
-
stylesProvider: globalClassesStylesProvider,
|
|
3119
|
-
breakpoint: breakpointValue,
|
|
3120
|
-
state
|
|
3121
|
-
});
|
|
3122
|
-
if (updated) {
|
|
3123
|
-
result = { status: "ok", classId };
|
|
3124
|
-
dispatchMcpStylesAppliedEvent({ styleValue: props });
|
|
3125
|
-
} else {
|
|
3126
|
-
throw new Error("error modifying class");
|
|
3127
|
-
}
|
|
3128
|
-
break;
|
|
3129
|
-
default:
|
|
3130
|
-
throw new Error(`Unsupported action ${action}`);
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
} catch (error) {
|
|
3134
|
-
return {
|
|
3135
|
-
status: "error",
|
|
3136
|
-
message: `${action} failed: ${error.message || "Unknown error"}`
|
|
3137
|
-
};
|
|
3138
|
-
}
|
|
3139
|
-
return result;
|
|
3140
|
-
};
|
|
3141
|
-
var initManageGlobalClasses = (reg) => {
|
|
3142
|
-
const { addTool } = reg;
|
|
3143
|
-
addTool({
|
|
3144
|
-
name: "manage-global-classes",
|
|
3145
|
-
requiredResources: [
|
|
3146
|
-
{ uri: GLOBAL_CLASSES_URI, description: "Global classes list" },
|
|
3147
|
-
{ uri: BREAKPOINTS_SCHEMA_FULL_URI, description: "Breakpoints list" }
|
|
3148
|
-
],
|
|
3149
|
-
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.
|
|
3150
|
-
|
|
3151
|
-
IMPORTANT: style must contain plaintext CSS rules per state \u2014 never pass empty strings.
|
|
3152
|
-
CSS is converted server-side in one bulk request; any declaration that cannot be converted is stored as the class custom CSS.
|
|
3153
|
-
|
|
3154
|
-
Example \u2014 creating a flex column class:
|
|
3155
|
-
style.default = "display: flex; flex-direction: column; gap: 1rem;"
|
|
3156
|
-
|
|
3157
|
-
Example \u2014 hover state:
|
|
3158
|
-
style.hover = "opacity: 0.85;"`,
|
|
3159
|
-
schema,
|
|
3160
|
-
outputSchema,
|
|
3161
|
-
handler
|
|
3162
|
-
});
|
|
3163
|
-
};
|
|
3164
|
-
async function attemptCreate(opts) {
|
|
3165
|
-
const { props, customCss, breakpoint, className, stylesProvider, state } = opts;
|
|
3166
|
-
const { create, delete: deleteClass2 } = stylesProvider.actions;
|
|
3167
|
-
if (!className) {
|
|
3168
|
-
throw new Error("Global class name is a required for creation");
|
|
3169
|
-
}
|
|
3170
|
-
if (!create || !deleteClass2) {
|
|
3171
|
-
throw new Error("User is unable to create global classes");
|
|
3172
|
-
}
|
|
3173
|
-
const newClassId = create(className, [
|
|
3174
|
-
{
|
|
3175
|
-
meta: {
|
|
3176
|
-
breakpoint,
|
|
3177
|
-
state: state === "default" ? null : state
|
|
3178
|
-
},
|
|
3179
|
-
custom_css: customCss,
|
|
3180
|
-
props
|
|
3181
|
-
}
|
|
3182
|
-
]);
|
|
3183
|
-
try {
|
|
3184
|
-
await saveGlobalClasses2({ context: "frontend" });
|
|
3185
|
-
return newClassId;
|
|
3186
|
-
} catch {
|
|
3187
|
-
deleteClass2(newClassId);
|
|
3188
|
-
throw new Error("error creating class");
|
|
3189
|
-
}
|
|
3190
|
-
}
|
|
3191
|
-
async function attemptUpdate(opts) {
|
|
3192
|
-
const { props, customCss, breakpoint, classId, stylesProvider, state } = opts;
|
|
3193
|
-
const { updateProps, update } = stylesProvider.actions;
|
|
3194
|
-
if (!classId) {
|
|
3195
|
-
throw new Error("Class ID is required for modification");
|
|
3196
|
-
}
|
|
3197
|
-
if (!updateProps || !update) {
|
|
3198
|
-
throw new Error("User is unable to update global classes");
|
|
3199
|
-
}
|
|
3200
|
-
await loadExistingClasses([classId]);
|
|
3201
|
-
const snapshot = structuredClone(stylesProvider.actions.all());
|
|
3202
|
-
try {
|
|
3203
|
-
updateProps({
|
|
3204
|
-
id: classId,
|
|
3205
|
-
props,
|
|
3206
|
-
custom_css: customCss,
|
|
3207
|
-
meta: {
|
|
3208
|
-
breakpoint,
|
|
3209
|
-
state: state === "default" ? null : state
|
|
3210
|
-
}
|
|
3211
|
-
});
|
|
3212
|
-
await saveGlobalClasses2({ context: "frontend" });
|
|
3213
|
-
return true;
|
|
3214
|
-
} catch {
|
|
3215
|
-
snapshot.forEach((style) => {
|
|
3216
|
-
update({
|
|
3217
|
-
id: style.id,
|
|
3218
|
-
variants: style.variants
|
|
3219
|
-
});
|
|
3220
|
-
});
|
|
3221
|
-
await saveGlobalClasses2({ context: "frontend" });
|
|
3222
|
-
throw new Error("error updating class");
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
async function attemptDelete(opts) {
|
|
3226
|
-
const { classId, stylesProvider } = opts;
|
|
3227
|
-
const { delete: deleteClass2, create } = stylesProvider.actions;
|
|
3228
|
-
if (!classId) {
|
|
3229
|
-
throw new Error("Class ID is required for deletion");
|
|
3230
|
-
}
|
|
3231
|
-
if (!deleteClass2 || !create) {
|
|
3232
|
-
throw new Error("User is unable to delete global classes");
|
|
3233
|
-
}
|
|
3234
|
-
const snapshot = structuredClone(stylesProvider.actions.all());
|
|
3235
|
-
const targetClass = snapshot.find((style) => style.id === classId);
|
|
3236
|
-
if (!targetClass) {
|
|
3237
|
-
throw new Error(`Class with ID "${classId}" not found`);
|
|
3238
|
-
}
|
|
3239
|
-
deleteClass2(classId);
|
|
3240
|
-
await saveGlobalClasses2({ context: "frontend" });
|
|
3241
|
-
return true;
|
|
3242
|
-
}
|
|
3243
|
-
function collectNonEmptyStyleBlocks(style) {
|
|
3244
|
-
const blocks = {};
|
|
3245
|
-
Object.entries(style).forEach(([state, cssText]) => {
|
|
3246
|
-
if (cssText?.trim()) {
|
|
3247
|
-
blocks[state] = cssText.trim();
|
|
3248
|
-
}
|
|
3249
|
-
});
|
|
3250
|
-
return blocks;
|
|
3251
|
-
}
|
|
3252
|
-
function toStoredCustomCss(customCss) {
|
|
3253
|
-
if (!customCss?.trim()) {
|
|
3254
|
-
return null;
|
|
3255
|
-
}
|
|
3256
|
-
return { raw: btoa(customCss) };
|
|
3257
|
-
}
|
|
3258
|
-
|
|
3259
3074
|
// src/mcp-integration/index.ts
|
|
3260
3075
|
var initMcpIntegration = (reg, canvasMcpEntry) => {
|
|
3261
3076
|
initMcpApplyUnapplyGlobalClasses(reg);
|
|
3262
3077
|
initMcpApplyGetGlobalClassUsages(reg);
|
|
3263
|
-
|
|
3078
|
+
initManageClassesTool(reg);
|
|
3264
3079
|
initClassesResource(reg, canvasMcpEntry);
|
|
3265
3080
|
};
|
|
3266
3081
|
|