@elementor/editor-canvas 4.1.0-761 → 4.1.0-763
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 +140 -108
- package/dist/index.mjs +140 -108
- package/package.json +18 -18
- package/src/composition-builder/composition-builder.ts +150 -107
- package/src/mcp/resources/widgets-schema-resource.ts +14 -0
- package/src/mcp/tools/build-composition/prompt.ts +6 -0
- package/src/mcp/tools/build-composition/tool.ts +2 -10
package/dist/index.js
CHANGED
|
@@ -221,6 +221,15 @@ Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
|
|
|
221
221
|
llmGuidance.instructions = "These are the default styles applied to the widget. Override only when necessary.";
|
|
222
222
|
llmGuidance.default_styles = defaultStyles;
|
|
223
223
|
}
|
|
224
|
+
const allowedChildTypes = widgetData.allowed_child_types;
|
|
225
|
+
const allWidgets = (0, import_editor_elements.getWidgetsCache)() || {};
|
|
226
|
+
const allowedParents = Object.entries(allWidgets).filter(([, parentConfig]) => parentConfig.allowed_child_types?.includes(widgetType)).map(([parentType]) => parentType);
|
|
227
|
+
if (allowedChildTypes?.length || allowedParents.length) {
|
|
228
|
+
llmGuidance.nesting = {
|
|
229
|
+
...allowedChildTypes?.length ? { allowed_child_types: allowedChildTypes } : {},
|
|
230
|
+
...allowedParents.length ? { allowed_parents: allowedParents } : {}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
224
233
|
return {
|
|
225
234
|
contents: [
|
|
226
235
|
{
|
|
@@ -3502,7 +3511,6 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3502
3511
|
elementStylesConfig = {};
|
|
3503
3512
|
elementCusomCSS = {};
|
|
3504
3513
|
rootContainers = [];
|
|
3505
|
-
containerElements = [];
|
|
3506
3514
|
api = {
|
|
3507
3515
|
createElement: import_editor_elements9.createElement,
|
|
3508
3516
|
getWidgetsCache: import_editor_elements9.getWidgetsCache,
|
|
@@ -3543,45 +3551,49 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3543
3551
|
getXML() {
|
|
3544
3552
|
return this.xml;
|
|
3545
3553
|
}
|
|
3546
|
-
|
|
3554
|
+
buildModelTree(node, widgetsCache) {
|
|
3547
3555
|
const elementTag = node.tagName;
|
|
3548
|
-
const
|
|
3549
|
-
const
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3556
|
+
const isWidget = widgetsCache[elementTag]?.elType === "widget";
|
|
3557
|
+
const id = this.api.generateElementId();
|
|
3558
|
+
const children = Array.from(node.children).map((child) => this.buildModelTree(child, widgetsCache));
|
|
3559
|
+
node.setAttribute("id", id);
|
|
3560
|
+
const base = {
|
|
3561
|
+
id,
|
|
3562
|
+
skipDefaultChildren: true,
|
|
3563
|
+
elements: children,
|
|
3564
|
+
editor_settings: {
|
|
3565
|
+
title: node.getAttribute("configuration-id") ?? void 0
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
if (isWidget) {
|
|
3569
|
+
return { ...base, elType: "widget", widgetType: elementTag };
|
|
3570
|
+
}
|
|
3571
|
+
return { ...base, elType: elementTag };
|
|
3572
|
+
}
|
|
3573
|
+
async awaitViewRender(element) {
|
|
3574
|
+
const view = element.view;
|
|
3575
|
+
if (view?._currentRenderPromise instanceof Promise) {
|
|
3576
|
+
await view._currentRenderPromise;
|
|
3577
|
+
} else {
|
|
3578
|
+
await Promise.resolve();
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
validateChildTypes(node, widgetsCache) {
|
|
3582
|
+
const errors = [];
|
|
3583
|
+
const allowedChildTypes = widgetsCache[node.tagName]?.allowed_child_types;
|
|
3584
|
+
if (allowedChildTypes?.length) {
|
|
3585
|
+
for (const child of Array.from(node.children)) {
|
|
3586
|
+
if (!allowedChildTypes.includes(child.tagName)) {
|
|
3587
|
+
errors.push(
|
|
3588
|
+
`"${child.tagName}" is not allowed as a child of "${node.tagName}". Allowed: ${allowedChildTypes.join(", ")}`
|
|
3589
|
+
);
|
|
3572
3590
|
}
|
|
3573
|
-
}
|
|
3574
|
-
options: { useHistory: false }
|
|
3575
|
-
});
|
|
3576
|
-
if (containerElement.id === "document") {
|
|
3577
|
-
this.rootContainers.push(newElement);
|
|
3591
|
+
}
|
|
3578
3592
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
for (const childNode of Array.from(node.children)) {
|
|
3582
|
-
this.iterateBuild(childNode, newElement, currentChild);
|
|
3583
|
-
currentChild++;
|
|
3593
|
+
for (const child of Array.from(node.children)) {
|
|
3594
|
+
errors.push(...this.validateChildTypes(child, widgetsCache));
|
|
3584
3595
|
}
|
|
3596
|
+
return errors;
|
|
3585
3597
|
}
|
|
3586
3598
|
findSchemaForNode(node) {
|
|
3587
3599
|
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
@@ -3607,63 +3619,31 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3607
3619
|
node
|
|
3608
3620
|
};
|
|
3609
3621
|
}
|
|
3610
|
-
|
|
3611
|
-
const
|
|
3622
|
+
async applyProperties() {
|
|
3623
|
+
const configErrors = [];
|
|
3624
|
+
const styleErrors = [];
|
|
3612
3625
|
const invalidStyles = {};
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
}
|
|
3627
|
-
|
|
3626
|
+
const allConfigIds = /* @__PURE__ */ new Set([
|
|
3627
|
+
...Object.keys(this.elementConfig),
|
|
3628
|
+
...Object.keys(this.elementStylesConfig),
|
|
3629
|
+
...Object.keys(this.elementCusomCSS)
|
|
3630
|
+
]);
|
|
3631
|
+
for (const configId of allConfigIds) {
|
|
3632
|
+
let element, node;
|
|
3633
|
+
try {
|
|
3634
|
+
({ element, node } = this.matchNodeByConfigId(configId));
|
|
3635
|
+
} catch (matchErr) {
|
|
3636
|
+
const msg = matchErr.message;
|
|
3637
|
+
if (this.elementConfig[configId]) {
|
|
3638
|
+
configErrors.push(msg);
|
|
3639
|
+
}
|
|
3640
|
+
if (this.elementStylesConfig[configId] || this.elementCusomCSS[configId]) {
|
|
3641
|
+
styleErrors.push(msg);
|
|
3628
3642
|
}
|
|
3629
|
-
}
|
|
3630
|
-
if (Object.keys(validStylesPropValues).length === 0) {
|
|
3631
3643
|
continue;
|
|
3632
3644
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
elementId: element.id,
|
|
3636
|
-
propertyName: "_styles",
|
|
3637
|
-
propertyValue: validStylesPropValues,
|
|
3638
|
-
elementType: node.tagName
|
|
3639
|
-
});
|
|
3640
|
-
} catch (error) {
|
|
3641
|
-
errors.push(String(error));
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
for (const [customCSSId, customCSS] of Object.entries(this.elementCusomCSS)) {
|
|
3645
|
-
const { element, node } = this.matchNodeByConfigId(customCSSId);
|
|
3646
|
-
this.api.doUpdateElementProperty({
|
|
3647
|
-
elementId: element.id,
|
|
3648
|
-
propertyName: "_styles",
|
|
3649
|
-
propertyValue: { custom_css: customCSS },
|
|
3650
|
-
elementType: node.tagName
|
|
3651
|
-
});
|
|
3652
|
-
}
|
|
3653
|
-
return {
|
|
3654
|
-
errors,
|
|
3655
|
-
invalidStyles
|
|
3656
|
-
};
|
|
3657
|
-
}
|
|
3658
|
-
applyConfigs() {
|
|
3659
|
-
const errors = [];
|
|
3660
|
-
for (const [configId, config] of Object.entries(this.elementConfig)) {
|
|
3661
|
-
const { element, node } = this.matchNodeByConfigId(configId);
|
|
3662
|
-
const propSchema = this.findSchemaForNode(node);
|
|
3663
|
-
const result = validateInput.validateProps(propSchema, config);
|
|
3664
|
-
if (!result.valid && result.errors?.length) {
|
|
3665
|
-
errors.push(...result.errors);
|
|
3666
|
-
} else {
|
|
3645
|
+
const config = this.elementConfig[configId];
|
|
3646
|
+
if (config) {
|
|
3667
3647
|
for (const [propertyName, propertyValue] of Object.entries(config)) {
|
|
3668
3648
|
try {
|
|
3669
3649
|
this.api.doUpdateElementProperty({
|
|
@@ -3673,30 +3653,84 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3673
3653
|
elementType: node.tagName
|
|
3674
3654
|
});
|
|
3675
3655
|
} catch (error) {
|
|
3676
|
-
|
|
3656
|
+
configErrors.push(error.message);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
const styleConfig = this.elementStylesConfig[configId];
|
|
3661
|
+
if (styleConfig) {
|
|
3662
|
+
const validStylesPropValues = {};
|
|
3663
|
+
for (const [styleName, stylePropValue] of Object.entries(styleConfig)) {
|
|
3664
|
+
const { valid, errors: validationErrors } = validateInput.validateStyles({
|
|
3665
|
+
[styleName]: stylePropValue
|
|
3666
|
+
});
|
|
3667
|
+
if (!valid) {
|
|
3668
|
+
if (styleConfig.$intention) {
|
|
3669
|
+
invalidStyles[element.id] = invalidStyles[element.id] || [];
|
|
3670
|
+
invalidStyles[element.id].push(styleName);
|
|
3671
|
+
}
|
|
3672
|
+
styleErrors.push(...validationErrors || []);
|
|
3673
|
+
} else {
|
|
3674
|
+
validStylesPropValues[styleName] = stylePropValue;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
if (Object.keys(validStylesPropValues).length > 0) {
|
|
3678
|
+
try {
|
|
3679
|
+
this.api.doUpdateElementProperty({
|
|
3680
|
+
elementId: element.id,
|
|
3681
|
+
propertyName: "_styles",
|
|
3682
|
+
propertyValue: validStylesPropValues,
|
|
3683
|
+
elementType: node.tagName
|
|
3684
|
+
});
|
|
3685
|
+
} catch (error) {
|
|
3686
|
+
styleErrors.push(String(error));
|
|
3677
3687
|
}
|
|
3678
3688
|
}
|
|
3679
3689
|
}
|
|
3690
|
+
const customCSS = this.elementCusomCSS[configId];
|
|
3691
|
+
if (customCSS) {
|
|
3692
|
+
try {
|
|
3693
|
+
this.api.doUpdateElementProperty({
|
|
3694
|
+
elementId: element.id,
|
|
3695
|
+
propertyName: "_styles",
|
|
3696
|
+
propertyValue: { custom_css: customCSS },
|
|
3697
|
+
elementType: node.tagName
|
|
3698
|
+
});
|
|
3699
|
+
} catch (cssErr) {
|
|
3700
|
+
styleErrors.push(String(cssErr));
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
await this.awaitViewRender(element);
|
|
3680
3704
|
}
|
|
3681
|
-
return
|
|
3705
|
+
return { configErrors, styleErrors, invalidStyles };
|
|
3682
3706
|
}
|
|
3683
|
-
build(rootContainer) {
|
|
3707
|
+
async build(rootContainer) {
|
|
3684
3708
|
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
3685
|
-
const CONTAINER_ELEMENTS = Object.values(widgetsCache).filter((widget) => widget.meta?.is_container).map((widget) => widget.elType).filter((x) => typeof x === "string");
|
|
3686
|
-
this.containerElements = CONTAINER_ELEMENTS;
|
|
3687
3709
|
new Set(this.xml.querySelectorAll("*")).forEach((node) => {
|
|
3688
3710
|
if (!widgetsCache[node.tagName]) {
|
|
3689
3711
|
throw new Error(`Unknown widget type: ${node.tagName}`);
|
|
3690
3712
|
}
|
|
3691
3713
|
});
|
|
3714
|
+
const childTypeErrors = [];
|
|
3715
|
+
for (const rootChild of Array.from(this.xml.children)) {
|
|
3716
|
+
childTypeErrors.push(...this.validateChildTypes(rootChild, widgetsCache));
|
|
3717
|
+
}
|
|
3718
|
+
if (childTypeErrors.length) {
|
|
3719
|
+
throw new Error(`Invalid element structure:
|
|
3720
|
+
${childTypeErrors.join("\n")}`);
|
|
3721
|
+
}
|
|
3692
3722
|
const children = Array.from(this.xml.children);
|
|
3693
|
-
let currentChild = 0;
|
|
3694
3723
|
for (const childNode of children) {
|
|
3695
|
-
this.
|
|
3696
|
-
|
|
3724
|
+
const modelTree = this.buildModelTree(childNode, widgetsCache);
|
|
3725
|
+
const newElement = this.api.createElement({
|
|
3726
|
+
container: rootContainer,
|
|
3727
|
+
model: modelTree,
|
|
3728
|
+
options: { useHistory: false }
|
|
3729
|
+
});
|
|
3730
|
+
this.rootContainers.push(newElement);
|
|
3731
|
+
await this.awaitViewRender(newElement);
|
|
3697
3732
|
}
|
|
3698
|
-
const {
|
|
3699
|
-
const configErrors = this.applyConfigs();
|
|
3733
|
+
const { configErrors, styleErrors, invalidStyles } = await this.applyProperties();
|
|
3700
3734
|
return {
|
|
3701
3735
|
configErrors,
|
|
3702
3736
|
styleErrors,
|
|
@@ -3736,6 +3770,12 @@ var generatePrompt = () => {
|
|
|
3736
3770
|
- Every element needs unique "configuration-id"
|
|
3737
3771
|
- No attributes, classes, IDs, or text nodes in XML
|
|
3738
3772
|
|
|
3773
|
+
## NESTED ELEMENTS
|
|
3774
|
+
Some elements have internal tree structures (nesting). When using these elements, you MUST build the FULL tree in XML.
|
|
3775
|
+
- Check \`llm_guidance.nesting\` in widget schemas for structure requirements
|
|
3776
|
+
- \`allowed_child_types\` lists which element types can be nested inside
|
|
3777
|
+
- \`allowed_parents\` lists which element types this element can be placed inside
|
|
3778
|
+
|
|
3739
3779
|
# CONFIGURATION
|
|
3740
3780
|
- Map configuration-id \u2192 elementConfig (props) + stylesConfig (layout only)
|
|
3741
3781
|
- All PropValues require \`$$type\` matching schema
|
|
@@ -3921,17 +3961,9 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
3921
3961
|
compositionBuilder.setElementConfig(elementConfig);
|
|
3922
3962
|
compositionBuilder.setStylesConfig(stylesConfig);
|
|
3923
3963
|
compositionBuilder.setCustomCSS(customCSS);
|
|
3924
|
-
const {
|
|
3925
|
-
configErrors,
|
|
3926
|
-
invalidStyles,
|
|
3927
|
-
rootContainers: generatedRootContainers
|
|
3928
|
-
} = compositionBuilder.build(targetContainer);
|
|
3964
|
+
const { invalidStyles, rootContainers: generatedRootContainers } = await compositionBuilder.build(targetContainer);
|
|
3929
3965
|
rootContainers.push(...generatedRootContainers);
|
|
3930
3966
|
generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
|
|
3931
|
-
if (configErrors.length) {
|
|
3932
|
-
errors.push(...configErrors.map((e) => new Error(e)));
|
|
3933
|
-
throw new Error("Configuration errors occurred during composition building.");
|
|
3934
|
-
}
|
|
3935
3967
|
Object.entries(invalidStyles).forEach(([elementId, rawCssRules]) => {
|
|
3936
3968
|
const customCss = {
|
|
3937
3969
|
value: rawCssRules.join(";\n")
|
package/dist/index.mjs
CHANGED
|
@@ -166,6 +166,15 @@ Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
|
|
|
166
166
|
llmGuidance.instructions = "These are the default styles applied to the widget. Override only when necessary.";
|
|
167
167
|
llmGuidance.default_styles = defaultStyles;
|
|
168
168
|
}
|
|
169
|
+
const allowedChildTypes = widgetData.allowed_child_types;
|
|
170
|
+
const allWidgets = getWidgetsCache() || {};
|
|
171
|
+
const allowedParents = Object.entries(allWidgets).filter(([, parentConfig]) => parentConfig.allowed_child_types?.includes(widgetType)).map(([parentType]) => parentType);
|
|
172
|
+
if (allowedChildTypes?.length || allowedParents.length) {
|
|
173
|
+
llmGuidance.nesting = {
|
|
174
|
+
...allowedChildTypes?.length ? { allowed_child_types: allowedChildTypes } : {},
|
|
175
|
+
...allowedParents.length ? { allowed_parents: allowedParents } : {}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
169
178
|
return {
|
|
170
179
|
contents: [
|
|
171
180
|
{
|
|
@@ -3488,7 +3497,6 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3488
3497
|
elementStylesConfig = {};
|
|
3489
3498
|
elementCusomCSS = {};
|
|
3490
3499
|
rootContainers = [];
|
|
3491
|
-
containerElements = [];
|
|
3492
3500
|
api = {
|
|
3493
3501
|
createElement: createElement7,
|
|
3494
3502
|
getWidgetsCache: getWidgetsCache5,
|
|
@@ -3529,45 +3537,49 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3529
3537
|
getXML() {
|
|
3530
3538
|
return this.xml;
|
|
3531
3539
|
}
|
|
3532
|
-
|
|
3540
|
+
buildModelTree(node, widgetsCache) {
|
|
3533
3541
|
const elementTag = node.tagName;
|
|
3534
|
-
const
|
|
3535
|
-
const
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3542
|
+
const isWidget = widgetsCache[elementTag]?.elType === "widget";
|
|
3543
|
+
const id = this.api.generateElementId();
|
|
3544
|
+
const children = Array.from(node.children).map((child) => this.buildModelTree(child, widgetsCache));
|
|
3545
|
+
node.setAttribute("id", id);
|
|
3546
|
+
const base = {
|
|
3547
|
+
id,
|
|
3548
|
+
skipDefaultChildren: true,
|
|
3549
|
+
elements: children,
|
|
3550
|
+
editor_settings: {
|
|
3551
|
+
title: node.getAttribute("configuration-id") ?? void 0
|
|
3552
|
+
}
|
|
3553
|
+
};
|
|
3554
|
+
if (isWidget) {
|
|
3555
|
+
return { ...base, elType: "widget", widgetType: elementTag };
|
|
3556
|
+
}
|
|
3557
|
+
return { ...base, elType: elementTag };
|
|
3558
|
+
}
|
|
3559
|
+
async awaitViewRender(element) {
|
|
3560
|
+
const view = element.view;
|
|
3561
|
+
if (view?._currentRenderPromise instanceof Promise) {
|
|
3562
|
+
await view._currentRenderPromise;
|
|
3563
|
+
} else {
|
|
3564
|
+
await Promise.resolve();
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
validateChildTypes(node, widgetsCache) {
|
|
3568
|
+
const errors = [];
|
|
3569
|
+
const allowedChildTypes = widgetsCache[node.tagName]?.allowed_child_types;
|
|
3570
|
+
if (allowedChildTypes?.length) {
|
|
3571
|
+
for (const child of Array.from(node.children)) {
|
|
3572
|
+
if (!allowedChildTypes.includes(child.tagName)) {
|
|
3573
|
+
errors.push(
|
|
3574
|
+
`"${child.tagName}" is not allowed as a child of "${node.tagName}". Allowed: ${allowedChildTypes.join(", ")}`
|
|
3575
|
+
);
|
|
3558
3576
|
}
|
|
3559
|
-
}
|
|
3560
|
-
options: { useHistory: false }
|
|
3561
|
-
});
|
|
3562
|
-
if (containerElement.id === "document") {
|
|
3563
|
-
this.rootContainers.push(newElement);
|
|
3577
|
+
}
|
|
3564
3578
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
for (const childNode of Array.from(node.children)) {
|
|
3568
|
-
this.iterateBuild(childNode, newElement, currentChild);
|
|
3569
|
-
currentChild++;
|
|
3579
|
+
for (const child of Array.from(node.children)) {
|
|
3580
|
+
errors.push(...this.validateChildTypes(child, widgetsCache));
|
|
3570
3581
|
}
|
|
3582
|
+
return errors;
|
|
3571
3583
|
}
|
|
3572
3584
|
findSchemaForNode(node) {
|
|
3573
3585
|
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
@@ -3593,63 +3605,31 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3593
3605
|
node
|
|
3594
3606
|
};
|
|
3595
3607
|
}
|
|
3596
|
-
|
|
3597
|
-
const
|
|
3608
|
+
async applyProperties() {
|
|
3609
|
+
const configErrors = [];
|
|
3610
|
+
const styleErrors = [];
|
|
3598
3611
|
const invalidStyles = {};
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
}
|
|
3613
|
-
|
|
3612
|
+
const allConfigIds = /* @__PURE__ */ new Set([
|
|
3613
|
+
...Object.keys(this.elementConfig),
|
|
3614
|
+
...Object.keys(this.elementStylesConfig),
|
|
3615
|
+
...Object.keys(this.elementCusomCSS)
|
|
3616
|
+
]);
|
|
3617
|
+
for (const configId of allConfigIds) {
|
|
3618
|
+
let element, node;
|
|
3619
|
+
try {
|
|
3620
|
+
({ element, node } = this.matchNodeByConfigId(configId));
|
|
3621
|
+
} catch (matchErr) {
|
|
3622
|
+
const msg = matchErr.message;
|
|
3623
|
+
if (this.elementConfig[configId]) {
|
|
3624
|
+
configErrors.push(msg);
|
|
3625
|
+
}
|
|
3626
|
+
if (this.elementStylesConfig[configId] || this.elementCusomCSS[configId]) {
|
|
3627
|
+
styleErrors.push(msg);
|
|
3614
3628
|
}
|
|
3615
|
-
}
|
|
3616
|
-
if (Object.keys(validStylesPropValues).length === 0) {
|
|
3617
3629
|
continue;
|
|
3618
3630
|
}
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
elementId: element.id,
|
|
3622
|
-
propertyName: "_styles",
|
|
3623
|
-
propertyValue: validStylesPropValues,
|
|
3624
|
-
elementType: node.tagName
|
|
3625
|
-
});
|
|
3626
|
-
} catch (error) {
|
|
3627
|
-
errors.push(String(error));
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
for (const [customCSSId, customCSS] of Object.entries(this.elementCusomCSS)) {
|
|
3631
|
-
const { element, node } = this.matchNodeByConfigId(customCSSId);
|
|
3632
|
-
this.api.doUpdateElementProperty({
|
|
3633
|
-
elementId: element.id,
|
|
3634
|
-
propertyName: "_styles",
|
|
3635
|
-
propertyValue: { custom_css: customCSS },
|
|
3636
|
-
elementType: node.tagName
|
|
3637
|
-
});
|
|
3638
|
-
}
|
|
3639
|
-
return {
|
|
3640
|
-
errors,
|
|
3641
|
-
invalidStyles
|
|
3642
|
-
};
|
|
3643
|
-
}
|
|
3644
|
-
applyConfigs() {
|
|
3645
|
-
const errors = [];
|
|
3646
|
-
for (const [configId, config] of Object.entries(this.elementConfig)) {
|
|
3647
|
-
const { element, node } = this.matchNodeByConfigId(configId);
|
|
3648
|
-
const propSchema = this.findSchemaForNode(node);
|
|
3649
|
-
const result = validateInput.validateProps(propSchema, config);
|
|
3650
|
-
if (!result.valid && result.errors?.length) {
|
|
3651
|
-
errors.push(...result.errors);
|
|
3652
|
-
} else {
|
|
3631
|
+
const config = this.elementConfig[configId];
|
|
3632
|
+
if (config) {
|
|
3653
3633
|
for (const [propertyName, propertyValue] of Object.entries(config)) {
|
|
3654
3634
|
try {
|
|
3655
3635
|
this.api.doUpdateElementProperty({
|
|
@@ -3659,30 +3639,84 @@ var CompositionBuilder = class _CompositionBuilder {
|
|
|
3659
3639
|
elementType: node.tagName
|
|
3660
3640
|
});
|
|
3661
3641
|
} catch (error) {
|
|
3662
|
-
|
|
3642
|
+
configErrors.push(error.message);
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
const styleConfig = this.elementStylesConfig[configId];
|
|
3647
|
+
if (styleConfig) {
|
|
3648
|
+
const validStylesPropValues = {};
|
|
3649
|
+
for (const [styleName, stylePropValue] of Object.entries(styleConfig)) {
|
|
3650
|
+
const { valid, errors: validationErrors } = validateInput.validateStyles({
|
|
3651
|
+
[styleName]: stylePropValue
|
|
3652
|
+
});
|
|
3653
|
+
if (!valid) {
|
|
3654
|
+
if (styleConfig.$intention) {
|
|
3655
|
+
invalidStyles[element.id] = invalidStyles[element.id] || [];
|
|
3656
|
+
invalidStyles[element.id].push(styleName);
|
|
3657
|
+
}
|
|
3658
|
+
styleErrors.push(...validationErrors || []);
|
|
3659
|
+
} else {
|
|
3660
|
+
validStylesPropValues[styleName] = stylePropValue;
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
if (Object.keys(validStylesPropValues).length > 0) {
|
|
3664
|
+
try {
|
|
3665
|
+
this.api.doUpdateElementProperty({
|
|
3666
|
+
elementId: element.id,
|
|
3667
|
+
propertyName: "_styles",
|
|
3668
|
+
propertyValue: validStylesPropValues,
|
|
3669
|
+
elementType: node.tagName
|
|
3670
|
+
});
|
|
3671
|
+
} catch (error) {
|
|
3672
|
+
styleErrors.push(String(error));
|
|
3663
3673
|
}
|
|
3664
3674
|
}
|
|
3665
3675
|
}
|
|
3676
|
+
const customCSS = this.elementCusomCSS[configId];
|
|
3677
|
+
if (customCSS) {
|
|
3678
|
+
try {
|
|
3679
|
+
this.api.doUpdateElementProperty({
|
|
3680
|
+
elementId: element.id,
|
|
3681
|
+
propertyName: "_styles",
|
|
3682
|
+
propertyValue: { custom_css: customCSS },
|
|
3683
|
+
elementType: node.tagName
|
|
3684
|
+
});
|
|
3685
|
+
} catch (cssErr) {
|
|
3686
|
+
styleErrors.push(String(cssErr));
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
await this.awaitViewRender(element);
|
|
3666
3690
|
}
|
|
3667
|
-
return
|
|
3691
|
+
return { configErrors, styleErrors, invalidStyles };
|
|
3668
3692
|
}
|
|
3669
|
-
build(rootContainer) {
|
|
3693
|
+
async build(rootContainer) {
|
|
3670
3694
|
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
3671
|
-
const CONTAINER_ELEMENTS = Object.values(widgetsCache).filter((widget) => widget.meta?.is_container).map((widget) => widget.elType).filter((x) => typeof x === "string");
|
|
3672
|
-
this.containerElements = CONTAINER_ELEMENTS;
|
|
3673
3695
|
new Set(this.xml.querySelectorAll("*")).forEach((node) => {
|
|
3674
3696
|
if (!widgetsCache[node.tagName]) {
|
|
3675
3697
|
throw new Error(`Unknown widget type: ${node.tagName}`);
|
|
3676
3698
|
}
|
|
3677
3699
|
});
|
|
3700
|
+
const childTypeErrors = [];
|
|
3701
|
+
for (const rootChild of Array.from(this.xml.children)) {
|
|
3702
|
+
childTypeErrors.push(...this.validateChildTypes(rootChild, widgetsCache));
|
|
3703
|
+
}
|
|
3704
|
+
if (childTypeErrors.length) {
|
|
3705
|
+
throw new Error(`Invalid element structure:
|
|
3706
|
+
${childTypeErrors.join("\n")}`);
|
|
3707
|
+
}
|
|
3678
3708
|
const children = Array.from(this.xml.children);
|
|
3679
|
-
let currentChild = 0;
|
|
3680
3709
|
for (const childNode of children) {
|
|
3681
|
-
this.
|
|
3682
|
-
|
|
3710
|
+
const modelTree = this.buildModelTree(childNode, widgetsCache);
|
|
3711
|
+
const newElement = this.api.createElement({
|
|
3712
|
+
container: rootContainer,
|
|
3713
|
+
model: modelTree,
|
|
3714
|
+
options: { useHistory: false }
|
|
3715
|
+
});
|
|
3716
|
+
this.rootContainers.push(newElement);
|
|
3717
|
+
await this.awaitViewRender(newElement);
|
|
3683
3718
|
}
|
|
3684
|
-
const {
|
|
3685
|
-
const configErrors = this.applyConfigs();
|
|
3719
|
+
const { configErrors, styleErrors, invalidStyles } = await this.applyProperties();
|
|
3686
3720
|
return {
|
|
3687
3721
|
configErrors,
|
|
3688
3722
|
styleErrors,
|
|
@@ -3722,6 +3756,12 @@ var generatePrompt = () => {
|
|
|
3722
3756
|
- Every element needs unique "configuration-id"
|
|
3723
3757
|
- No attributes, classes, IDs, or text nodes in XML
|
|
3724
3758
|
|
|
3759
|
+
## NESTED ELEMENTS
|
|
3760
|
+
Some elements have internal tree structures (nesting). When using these elements, you MUST build the FULL tree in XML.
|
|
3761
|
+
- Check \`llm_guidance.nesting\` in widget schemas for structure requirements
|
|
3762
|
+
- \`allowed_child_types\` lists which element types can be nested inside
|
|
3763
|
+
- \`allowed_parents\` lists which element types this element can be placed inside
|
|
3764
|
+
|
|
3725
3765
|
# CONFIGURATION
|
|
3726
3766
|
- Map configuration-id \u2192 elementConfig (props) + stylesConfig (layout only)
|
|
3727
3767
|
- All PropValues require \`$$type\` matching schema
|
|
@@ -3907,17 +3947,9 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
3907
3947
|
compositionBuilder.setElementConfig(elementConfig);
|
|
3908
3948
|
compositionBuilder.setStylesConfig(stylesConfig);
|
|
3909
3949
|
compositionBuilder.setCustomCSS(customCSS);
|
|
3910
|
-
const {
|
|
3911
|
-
configErrors,
|
|
3912
|
-
invalidStyles,
|
|
3913
|
-
rootContainers: generatedRootContainers
|
|
3914
|
-
} = compositionBuilder.build(targetContainer);
|
|
3950
|
+
const { invalidStyles, rootContainers: generatedRootContainers } = await compositionBuilder.build(targetContainer);
|
|
3915
3951
|
rootContainers.push(...generatedRootContainers);
|
|
3916
3952
|
generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
|
|
3917
|
-
if (configErrors.length) {
|
|
3918
|
-
errors.push(...configErrors.map((e) => new Error(e)));
|
|
3919
|
-
throw new Error("Configuration errors occurred during composition building.");
|
|
3920
|
-
}
|
|
3921
3953
|
Object.entries(invalidStyles).forEach(([elementId, rawCssRules]) => {
|
|
3922
3954
|
const customCss = {
|
|
3923
3955
|
value: rawCssRules.join(";\n")
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "4.1.0-
|
|
4
|
+
"version": "4.1.0-763",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,25 +37,25 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor": "4.1.0-
|
|
40
|
+
"@elementor/editor": "4.1.0-763",
|
|
41
41
|
"dompurify": "^3.2.6",
|
|
42
|
-
"@elementor/editor-controls": "4.1.0-
|
|
43
|
-
"@elementor/editor-documents": "4.1.0-
|
|
44
|
-
"@elementor/editor-elements": "4.1.0-
|
|
45
|
-
"@elementor/editor-interactions": "4.1.0-
|
|
46
|
-
"@elementor/editor-mcp": "4.1.0-
|
|
47
|
-
"@elementor/editor-notifications": "4.1.0-
|
|
48
|
-
"@elementor/editor-props": "4.1.0-
|
|
49
|
-
"@elementor/editor-responsive": "4.1.0-
|
|
50
|
-
"@elementor/editor-styles": "4.1.0-
|
|
51
|
-
"@elementor/editor-styles-repository": "4.1.0-
|
|
52
|
-
"@elementor/editor-ui": "4.1.0-
|
|
53
|
-
"@elementor/editor-v1-adapters": "4.1.0-
|
|
54
|
-
"@elementor/schema": "4.1.0-
|
|
55
|
-
"@elementor/twing": "4.1.0-
|
|
42
|
+
"@elementor/editor-controls": "4.1.0-763",
|
|
43
|
+
"@elementor/editor-documents": "4.1.0-763",
|
|
44
|
+
"@elementor/editor-elements": "4.1.0-763",
|
|
45
|
+
"@elementor/editor-interactions": "4.1.0-763",
|
|
46
|
+
"@elementor/editor-mcp": "4.1.0-763",
|
|
47
|
+
"@elementor/editor-notifications": "4.1.0-763",
|
|
48
|
+
"@elementor/editor-props": "4.1.0-763",
|
|
49
|
+
"@elementor/editor-responsive": "4.1.0-763",
|
|
50
|
+
"@elementor/editor-styles": "4.1.0-763",
|
|
51
|
+
"@elementor/editor-styles-repository": "4.1.0-763",
|
|
52
|
+
"@elementor/editor-ui": "4.1.0-763",
|
|
53
|
+
"@elementor/editor-v1-adapters": "4.1.0-763",
|
|
54
|
+
"@elementor/schema": "4.1.0-763",
|
|
55
|
+
"@elementor/twing": "4.1.0-763",
|
|
56
56
|
"@elementor/ui": "1.36.17",
|
|
57
|
-
"@elementor/utils": "4.1.0-
|
|
58
|
-
"@elementor/wp-media": "4.1.0-
|
|
57
|
+
"@elementor/utils": "4.1.0-763",
|
|
58
|
+
"@elementor/wp-media": "4.1.0-763",
|
|
59
59
|
"@floating-ui/react": "^0.27.5",
|
|
60
60
|
"@wordpress/i18n": "^5.13.0"
|
|
61
61
|
},
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createElement,
|
|
3
|
+
type CreateElementParams,
|
|
3
4
|
generateElementId,
|
|
4
5
|
getContainer,
|
|
5
6
|
getWidgetsCache,
|
|
6
7
|
type V1Element,
|
|
8
|
+
type V1ElementConfig,
|
|
7
9
|
} from '@elementor/editor-elements';
|
|
8
10
|
import { type z } from '@elementor/schema';
|
|
9
11
|
|
|
@@ -34,7 +36,6 @@ export class CompositionBuilder {
|
|
|
34
36
|
private elementStylesConfig: Record< string, Record< string, AnyValue > > = {};
|
|
35
37
|
private elementCusomCSS: Record< string, string > = {};
|
|
36
38
|
private rootContainers: V1Element[] = [];
|
|
37
|
-
private containerElements: string[] = [];
|
|
38
39
|
private api: API = {
|
|
39
40
|
createElement,
|
|
40
41
|
getWidgetsCache,
|
|
@@ -82,50 +83,63 @@ export class CompositionBuilder {
|
|
|
82
83
|
return this.xml;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
private
|
|
86
|
+
private buildModelTree(
|
|
87
|
+
node: Element,
|
|
88
|
+
widgetsCache: Record< string, V1ElementConfig >
|
|
89
|
+
): Record< string, unknown > {
|
|
86
90
|
const elementTag = node.tagName;
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
const isWidget = widgetsCache[ elementTag ]?.elType === 'widget';
|
|
92
|
+
const id = this.api.generateElementId();
|
|
93
|
+
const children = Array.from( node.children ).map( ( child ) => this.buildModelTree( child, widgetsCache ) );
|
|
94
|
+
|
|
95
|
+
node.setAttribute( 'id', id );
|
|
96
|
+
|
|
97
|
+
const base = {
|
|
98
|
+
id,
|
|
99
|
+
skipDefaultChildren: true,
|
|
100
|
+
elements: children,
|
|
101
|
+
editor_settings: {
|
|
102
|
+
title: node.getAttribute( 'configuration-id' ) ?? undefined,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if ( isWidget ) {
|
|
107
|
+
return { ...base, elType: 'widget' as const, widgetType: elementTag };
|
|
95
108
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
options: { useHistory: false },
|
|
107
|
-
} )
|
|
108
|
-
: this.api.createElement( {
|
|
109
|
-
container: targetContainer,
|
|
110
|
-
model: {
|
|
111
|
-
elType: 'widget',
|
|
112
|
-
widgetType: elementTag,
|
|
113
|
-
id: generateElementId(),
|
|
114
|
-
editor_settings: {
|
|
115
|
-
title: node.getAttribute( 'configuration-id' ) ?? undefined,
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
options: { useHistory: false },
|
|
119
|
-
} );
|
|
120
|
-
if ( containerElement.id === 'document' ) {
|
|
121
|
-
this.rootContainers.push( newElement );
|
|
109
|
+
|
|
110
|
+
return { ...base, elType: elementTag };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async awaitViewRender( element: V1Element ) {
|
|
114
|
+
const view = element.view as Record< string, unknown > | undefined;
|
|
115
|
+
if ( view?._currentRenderPromise instanceof Promise ) {
|
|
116
|
+
await view._currentRenderPromise;
|
|
117
|
+
} else {
|
|
118
|
+
await Promise.resolve();
|
|
122
119
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private validateChildTypes( node: Element, widgetsCache: Record< string, V1ElementConfig > ): string[] {
|
|
123
|
+
const errors: string[] = [];
|
|
124
|
+
const allowedChildTypes = widgetsCache[ node.tagName ]?.allowed_child_types;
|
|
125
|
+
|
|
126
|
+
if ( allowedChildTypes?.length ) {
|
|
127
|
+
for ( const child of Array.from( node.children ) ) {
|
|
128
|
+
if ( ! allowedChildTypes.includes( child.tagName ) ) {
|
|
129
|
+
errors.push(
|
|
130
|
+
`"${ child.tagName }" is not allowed as a child of "${
|
|
131
|
+
node.tagName
|
|
132
|
+
}". Allowed: ${ allowedChildTypes.join( ', ' ) }`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for ( const child of Array.from( node.children ) ) {
|
|
139
|
+
errors.push( ...this.validateChildTypes( child, widgetsCache ) );
|
|
128
140
|
}
|
|
141
|
+
|
|
142
|
+
return errors;
|
|
129
143
|
}
|
|
130
144
|
|
|
131
145
|
private findSchemaForNode( node: Element ) {
|
|
@@ -154,64 +168,34 @@ export class CompositionBuilder {
|
|
|
154
168
|
};
|
|
155
169
|
}
|
|
156
170
|
|
|
157
|
-
|
|
158
|
-
const
|
|
171
|
+
private async applyProperties() {
|
|
172
|
+
const configErrors: string[] = [];
|
|
173
|
+
const styleErrors: string[] = [];
|
|
159
174
|
const invalidStyles: Record< string, string[] > = {};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
|
|
176
|
+
const allConfigIds = new Set( [
|
|
177
|
+
...Object.keys( this.elementConfig ),
|
|
178
|
+
...Object.keys( this.elementStylesConfig ),
|
|
179
|
+
...Object.keys( this.elementCusomCSS ),
|
|
180
|
+
] );
|
|
181
|
+
|
|
182
|
+
for ( const configId of allConfigIds ) {
|
|
183
|
+
let element, node;
|
|
184
|
+
try {
|
|
185
|
+
( { element, node } = this.matchNodeByConfigId( configId ) );
|
|
186
|
+
} catch ( matchErr ) {
|
|
187
|
+
const msg = ( matchErr as Error ).message;
|
|
188
|
+
if ( this.elementConfig[ configId ] ) {
|
|
189
|
+
configErrors.push( msg );
|
|
190
|
+
}
|
|
191
|
+
if ( this.elementStylesConfig[ configId ] || this.elementCusomCSS[ configId ] ) {
|
|
192
|
+
styleErrors.push( msg );
|
|
175
193
|
}
|
|
176
|
-
}
|
|
177
|
-
if ( Object.keys( validStylesPropValues ).length === 0 ) {
|
|
178
194
|
continue;
|
|
179
195
|
}
|
|
180
|
-
try {
|
|
181
|
-
this.api.doUpdateElementProperty( {
|
|
182
|
-
elementId: element.id,
|
|
183
|
-
propertyName: '_styles',
|
|
184
|
-
propertyValue: validStylesPropValues,
|
|
185
|
-
elementType: node.tagName,
|
|
186
|
-
} );
|
|
187
|
-
} catch ( error ) {
|
|
188
|
-
errors.push( String( error ) );
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
for ( const [ customCSSId, customCSS ] of Object.entries( this.elementCusomCSS ) ) {
|
|
192
|
-
const { element, node } = this.matchNodeByConfigId( customCSSId );
|
|
193
|
-
this.api.doUpdateElementProperty( {
|
|
194
|
-
elementId: element.id,
|
|
195
|
-
propertyName: '_styles',
|
|
196
|
-
propertyValue: { custom_css: customCSS },
|
|
197
|
-
elementType: node.tagName,
|
|
198
|
-
} );
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
errors,
|
|
202
|
-
invalidStyles,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
196
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
for ( const [ configId, config ] of Object.entries( this.elementConfig ) ) {
|
|
209
|
-
const { element, node } = this.matchNodeByConfigId( configId );
|
|
210
|
-
const propSchema = this.findSchemaForNode( node );
|
|
211
|
-
const result = validateInput.validateProps( propSchema, config );
|
|
212
|
-
if ( ! result.valid && result.errors?.length ) {
|
|
213
|
-
errors.push( ...result.errors );
|
|
214
|
-
} else {
|
|
197
|
+
const config = this.elementConfig[ configId ];
|
|
198
|
+
if ( config ) {
|
|
215
199
|
for ( const [ propertyName, propertyValue ] of Object.entries( config ) ) {
|
|
216
200
|
try {
|
|
217
201
|
this.api.doUpdateElementProperty( {
|
|
@@ -221,36 +205,95 @@ export class CompositionBuilder {
|
|
|
221
205
|
elementType: node.tagName,
|
|
222
206
|
} );
|
|
223
207
|
} catch ( error ) {
|
|
224
|
-
|
|
208
|
+
configErrors.push( ( error as Error ).message );
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const styleConfig = this.elementStylesConfig[ configId ];
|
|
214
|
+
if ( styleConfig ) {
|
|
215
|
+
const validStylesPropValues: Record< string, AnyValue > = {};
|
|
216
|
+
for ( const [ styleName, stylePropValue ] of Object.entries( styleConfig ) ) {
|
|
217
|
+
const { valid, errors: validationErrors } = validateInput.validateStyles( {
|
|
218
|
+
[ styleName ]: stylePropValue,
|
|
219
|
+
} );
|
|
220
|
+
if ( ! valid ) {
|
|
221
|
+
if ( styleConfig.$intention ) {
|
|
222
|
+
invalidStyles[ element.id ] = invalidStyles[ element.id ] || [];
|
|
223
|
+
invalidStyles[ element.id ].push( styleName );
|
|
224
|
+
}
|
|
225
|
+
styleErrors.push( ...( validationErrors || [] ) );
|
|
226
|
+
} else {
|
|
227
|
+
validStylesPropValues[ styleName ] = stylePropValue;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if ( Object.keys( validStylesPropValues ).length > 0 ) {
|
|
231
|
+
try {
|
|
232
|
+
this.api.doUpdateElementProperty( {
|
|
233
|
+
elementId: element.id,
|
|
234
|
+
propertyName: '_styles',
|
|
235
|
+
propertyValue: validStylesPropValues,
|
|
236
|
+
elementType: node.tagName,
|
|
237
|
+
} );
|
|
238
|
+
} catch ( error ) {
|
|
239
|
+
styleErrors.push( String( error ) );
|
|
225
240
|
}
|
|
226
241
|
}
|
|
227
242
|
}
|
|
243
|
+
|
|
244
|
+
const customCSS = this.elementCusomCSS[ configId ];
|
|
245
|
+
if ( customCSS ) {
|
|
246
|
+
try {
|
|
247
|
+
this.api.doUpdateElementProperty( {
|
|
248
|
+
elementId: element.id,
|
|
249
|
+
propertyName: '_styles',
|
|
250
|
+
propertyValue: { custom_css: customCSS },
|
|
251
|
+
elementType: node.tagName,
|
|
252
|
+
} );
|
|
253
|
+
} catch ( cssErr ) {
|
|
254
|
+
styleErrors.push( String( cssErr ) );
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await this.awaitViewRender( element );
|
|
228
259
|
}
|
|
229
|
-
|
|
260
|
+
|
|
261
|
+
return { configErrors, styleErrors, invalidStyles };
|
|
230
262
|
}
|
|
231
263
|
|
|
232
|
-
build( rootContainer: V1Element ) {
|
|
264
|
+
async build( rootContainer: V1Element ) {
|
|
233
265
|
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
234
|
-
|
|
235
|
-
.filter( ( widget ) => widget.meta?.is_container )
|
|
236
|
-
.map( ( widget ) => widget.elType )
|
|
237
|
-
.filter( ( x ) => typeof x === 'string' );
|
|
238
|
-
this.containerElements = CONTAINER_ELEMENTS;
|
|
266
|
+
|
|
239
267
|
new Set( this.xml.querySelectorAll( '*' ) ).forEach( ( node ) => {
|
|
240
268
|
if ( ! widgetsCache[ node.tagName ] ) {
|
|
241
269
|
throw new Error( `Unknown widget type: ${ node.tagName }` );
|
|
242
270
|
}
|
|
243
271
|
} );
|
|
244
272
|
|
|
273
|
+
const childTypeErrors: string[] = [];
|
|
274
|
+
for ( const rootChild of Array.from( this.xml.children ) ) {
|
|
275
|
+
childTypeErrors.push( ...this.validateChildTypes( rootChild, widgetsCache ) );
|
|
276
|
+
}
|
|
277
|
+
if ( childTypeErrors.length ) {
|
|
278
|
+
throw new Error( `Invalid element structure:\n${ childTypeErrors.join( '\n' ) }` );
|
|
279
|
+
}
|
|
280
|
+
|
|
245
281
|
const children = Array.from( this.xml.children );
|
|
246
|
-
let currentChild = 0;
|
|
247
282
|
for ( const childNode of children ) {
|
|
248
|
-
this.
|
|
249
|
-
|
|
283
|
+
const modelTree = this.buildModelTree( childNode, widgetsCache );
|
|
284
|
+
|
|
285
|
+
const newElement = this.api.createElement( {
|
|
286
|
+
container: rootContainer,
|
|
287
|
+
model: modelTree as CreateElementParams[ 'model' ],
|
|
288
|
+
options: { useHistory: false },
|
|
289
|
+
} );
|
|
290
|
+
|
|
291
|
+
this.rootContainers.push( newElement );
|
|
292
|
+
|
|
293
|
+
await this.awaitViewRender( newElement );
|
|
250
294
|
}
|
|
251
295
|
|
|
252
|
-
const {
|
|
253
|
-
const configErrors = this.applyConfigs();
|
|
296
|
+
const { configErrors, styleErrors, invalidStyles } = await this.applyProperties();
|
|
254
297
|
|
|
255
298
|
return {
|
|
256
299
|
configErrors,
|
|
@@ -141,6 +141,20 @@ Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
|
|
|
141
141
|
llmGuidance.default_styles = defaultStyles;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
const allowedChildTypes = widgetData.allowed_child_types;
|
|
145
|
+
|
|
146
|
+
const allWidgets = getWidgetsCache() || {};
|
|
147
|
+
const allowedParents = Object.entries( allWidgets )
|
|
148
|
+
.filter( ( [ , parentConfig ] ) => parentConfig.allowed_child_types?.includes( widgetType ) )
|
|
149
|
+
.map( ( [ parentType ] ) => parentType );
|
|
150
|
+
|
|
151
|
+
if ( allowedChildTypes?.length || allowedParents.length ) {
|
|
152
|
+
llmGuidance.nesting = {
|
|
153
|
+
...( allowedChildTypes?.length ? { allowed_child_types: allowedChildTypes } : {} ),
|
|
154
|
+
...( allowedParents.length ? { allowed_parents: allowedParents } : {} ),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
144
158
|
return {
|
|
145
159
|
contents: [
|
|
146
160
|
{
|
|
@@ -19,6 +19,12 @@ export const generatePrompt = () => {
|
|
|
19
19
|
- Every element needs unique "configuration-id"
|
|
20
20
|
- No attributes, classes, IDs, or text nodes in XML
|
|
21
21
|
|
|
22
|
+
## NESTED ELEMENTS
|
|
23
|
+
Some elements have internal tree structures (nesting). When using these elements, you MUST build the FULL tree in XML.
|
|
24
|
+
- Check \`llm_guidance.nesting\` in widget schemas for structure requirements
|
|
25
|
+
- \`allowed_child_types\` lists which element types can be nested inside
|
|
26
|
+
- \`allowed_parents\` lists which element types this element can be placed inside
|
|
27
|
+
|
|
22
28
|
# CONFIGURATION
|
|
23
29
|
- Map configuration-id → elementConfig (props) + stylesConfig (layout only)
|
|
24
30
|
- All PropValues require \`$$type\` matching schema
|
|
@@ -50,20 +50,12 @@ export const initBuildCompositionsTool = ( reg: MCPRegistryEntry ) => {
|
|
|
50
50
|
compositionBuilder.setStylesConfig( stylesConfig );
|
|
51
51
|
compositionBuilder.setCustomCSS( customCSS );
|
|
52
52
|
|
|
53
|
-
const {
|
|
54
|
-
|
|
55
|
-
invalidStyles,
|
|
56
|
-
rootContainers: generatedRootContainers,
|
|
57
|
-
} = compositionBuilder.build( targetContainer );
|
|
53
|
+
const { invalidStyles, rootContainers: generatedRootContainers } =
|
|
54
|
+
await compositionBuilder.build( targetContainer );
|
|
58
55
|
|
|
59
56
|
rootContainers.push( ...generatedRootContainers );
|
|
60
57
|
generatedXML = new XMLSerializer().serializeToString( compositionBuilder.getXML() );
|
|
61
58
|
|
|
62
|
-
if ( configErrors.length ) {
|
|
63
|
-
errors.push( ...configErrors.map( ( e ) => new Error( e ) ) );
|
|
64
|
-
throw new Error( 'Configuration errors occurred during composition building.' );
|
|
65
|
-
}
|
|
66
|
-
|
|
67
59
|
Object.entries( invalidStyles ).forEach( ( [ elementId, rawCssRules ] ) => {
|
|
68
60
|
const customCss = {
|
|
69
61
|
value: rawCssRules.join( ';\n' ),
|