bricks-builder-mcp 3.6.3 → 3.6.5
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 +1 -1
- package/server.js +103 -12
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "bricks-builder-mcp",
|
|
4
|
-
"version": "3.6.
|
|
4
|
+
"version": "3.6.5",
|
|
5
5
|
"description": "Serveur MCP pour piloter Bricks Builder (WordPress) depuis Claude — édition de pages, gestion d'éléments, réordonnancement des sections, vérification visuelle + technique (verify_element).",
|
|
6
6
|
"main": "server.js",
|
|
7
7
|
"bin": {
|
package/server.js
CHANGED
|
@@ -83,12 +83,66 @@ process.on('SIGINT', () => {
|
|
|
83
83
|
process.exit(0);
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
//
|
|
87
|
-
function
|
|
86
|
+
// Hex → rgb/rgba pour matcher getComputedStyle qui renvoie toujours rgb()
|
|
87
|
+
function hexToRgb(hex) {
|
|
88
|
+
hex = hex.replace('#', '');
|
|
89
|
+
if (hex.length === 3) {
|
|
90
|
+
hex = hex.split('').map(c => c + c).join('');
|
|
91
|
+
}
|
|
92
|
+
if (hex.length !== 6 && hex.length !== 8) return null;
|
|
93
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
94
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
95
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
96
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
|
|
97
|
+
if (hex.length === 8) {
|
|
98
|
+
const a = parseInt(hex.substring(6, 8), 16) / 255;
|
|
99
|
+
return `rgba(${r},${g},${b},${a.toFixed(2)})`;
|
|
100
|
+
}
|
|
101
|
+
return `rgb(${r},${g},${b})`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Normaliser une valeur rgba/rgb (supprime espaces, arrondit alpha)
|
|
105
|
+
function normaliseColor(s) {
|
|
106
|
+
s = s.replace(/\s+/g, '');
|
|
107
|
+
// rgba(0,0,0,0) → transparent
|
|
108
|
+
if (s === 'rgba(0,0,0,0)' || s === 'transparent') return 'transparent';
|
|
109
|
+
return s;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Normaliser les valeurs CSS pour comparaison (rgba/spaces/units/hex/vh)
|
|
113
|
+
function normaliseCssValue(val, viewportContext) {
|
|
88
114
|
if (val == null) return '';
|
|
89
115
|
let s = String(val).trim().toLowerCase();
|
|
90
|
-
// Supprime espaces internes (rgba(0, 0, 0) → rgba(0,0,0))
|
|
91
116
|
s = s.replace(/\s+/g, '');
|
|
117
|
+
|
|
118
|
+
// Hex → rgb pour matcher getComputedStyle
|
|
119
|
+
if (/^#[0-9a-f]{3,8}$/.test(s)) {
|
|
120
|
+
const rgb = hexToRgb(s);
|
|
121
|
+
if (rgb) return normaliseColor(rgb);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Couleurs rgb/rgba : normaliser pour comparer
|
|
125
|
+
if (s.startsWith('rgb')) {
|
|
126
|
+
return normaliseColor(s);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// vh / vw → px en fonction du viewport actuel (getComputedStyle renvoie en px)
|
|
130
|
+
if (viewportContext) {
|
|
131
|
+
const vhMatch = s.match(/^(\d+(?:\.\d+)?)vh$/);
|
|
132
|
+
if (vhMatch && viewportContext.height) {
|
|
133
|
+
return Math.round(parseFloat(vhMatch[1]) * viewportContext.height / 100) + 'px';
|
|
134
|
+
}
|
|
135
|
+
const vwMatch = s.match(/^(\d+(?:\.\d+)?)vw$/);
|
|
136
|
+
if (vwMatch && viewportContext.width) {
|
|
137
|
+
return Math.round(parseFloat(vwMatch[1]) * viewportContext.width / 100) + 'px';
|
|
138
|
+
}
|
|
139
|
+
// svh, dvh, lvh : on assimile à vh pour l'instant (approximatif mais OK pour la plupart des cas)
|
|
140
|
+
const svhMatch = s.match(/^(\d+(?:\.\d+)?)(?:svh|dvh|lvh)$/);
|
|
141
|
+
if (svhMatch && viewportContext.height) {
|
|
142
|
+
return Math.round(parseFloat(svhMatch[1]) * viewportContext.height / 100) + 'px';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
92
146
|
// Normalise "0px" et "0" et "0%"
|
|
93
147
|
if (s === '0' || s === '0px' || s === '0%') return '0';
|
|
94
148
|
// Si valeur sans unité fournie (ex "32") et getComputedStyle renvoie "32px"
|
|
@@ -1399,6 +1453,13 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1399
1453
|
const pageId = args.pageId;
|
|
1400
1454
|
const elementId = args.elementId;
|
|
1401
1455
|
const viewport = args.viewport || "desktop";
|
|
1456
|
+
const viewportSizes = {
|
|
1457
|
+
desktop: { width: 1920, height: 1080 },
|
|
1458
|
+
tablet: { width: 991, height: 1200 },
|
|
1459
|
+
mobile_landscape: { width: 767, height: 600 },
|
|
1460
|
+
mobile_portrait: { width: 478, height: 800 },
|
|
1461
|
+
};
|
|
1462
|
+
const viewportContext = viewportSizes[viewport] || viewportSizes.desktop;
|
|
1402
1463
|
|
|
1403
1464
|
// 1) Récupérer infos plugin (URL, sélecteur, expected styles)
|
|
1404
1465
|
const info = await callWordPressAPI("/verify-element-info", "POST", { pageId, elementId });
|
|
@@ -1468,6 +1529,16 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1468
1529
|
computed = await element.evaluate(el => {
|
|
1469
1530
|
const cs = getComputedStyle(el);
|
|
1470
1531
|
const rect = el.getBoundingClientRect();
|
|
1532
|
+
// Enfants "réels" = ceux qui ont une classe brxe-{id} ou id brxe-*
|
|
1533
|
+
// Exclut les <video>, <picture> et autres ajoutés par Bricks (bg vidéo, overlay auto, etc.)
|
|
1534
|
+
const realChildren = Array.from(el.children).filter(child => {
|
|
1535
|
+
if (child.id && child.id.startsWith('brxe-')) return true;
|
|
1536
|
+
return Array.from(child.classList).some(cls => cls.startsWith('brxe-'));
|
|
1537
|
+
});
|
|
1538
|
+
const bricksInternalChildren = Array.from(el.children).filter(child => {
|
|
1539
|
+
return !((child.id && child.id.startsWith('brxe-')) ||
|
|
1540
|
+
Array.from(child.classList).some(cls => cls.startsWith('brxe-')));
|
|
1541
|
+
}).map(c => c.tagName.toLowerCase() + (c.className ? '.' + c.className.split(' ').join('.') : ''));
|
|
1471
1542
|
return {
|
|
1472
1543
|
display: cs.display,
|
|
1473
1544
|
'flex-direction': cs.flexDirection,
|
|
@@ -1500,7 +1571,9 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1500
1571
|
'border-bottom-left-radius': cs.borderBottomLeftRadius,
|
|
1501
1572
|
visibility: cs.visibility,
|
|
1502
1573
|
opacity: cs.opacity,
|
|
1503
|
-
childrenInDom:
|
|
1574
|
+
childrenInDom: realChildren.length,
|
|
1575
|
+
childrenTotalDom: el.children.length,
|
|
1576
|
+
bricksInternalChildren: bricksInternalChildren,
|
|
1504
1577
|
isVisible: rect.width > 0 && rect.height > 0 && cs.visibility !== 'hidden' && cs.opacity !== '0',
|
|
1505
1578
|
hasOverflowX: el.scrollWidth > el.clientWidth,
|
|
1506
1579
|
};
|
|
@@ -1532,12 +1605,12 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1532
1605
|
});
|
|
1533
1606
|
}
|
|
1534
1607
|
|
|
1535
|
-
// Compare chaque expected
|
|
1608
|
+
// Compare chaque expected (viewport-aware pour vh/vw)
|
|
1536
1609
|
const expected = info.expected || {};
|
|
1537
1610
|
for (const [prop, expectedVal] of Object.entries(expected)) {
|
|
1538
1611
|
const got = computed[prop];
|
|
1539
1612
|
if (got === undefined) continue;
|
|
1540
|
-
const ok = normaliseCssValue(got) === normaliseCssValue(expectedVal);
|
|
1613
|
+
const ok = normaliseCssValue(got, viewportContext) === normaliseCssValue(expectedVal, viewportContext);
|
|
1541
1614
|
const check = { ok, label: `${prop} = ${expectedVal}` };
|
|
1542
1615
|
if (!ok) {
|
|
1543
1616
|
check.expected = expectedVal;
|
|
@@ -1548,16 +1621,34 @@ mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1548
1621
|
checks.push(check);
|
|
1549
1622
|
}
|
|
1550
1623
|
|
|
1551
|
-
// Console errors
|
|
1552
|
-
|
|
1624
|
+
// Console errors : séparer les vrais bugs JS des erreurs réseau (429/timeout)
|
|
1625
|
+
// qui dépendent du serveur, pas du code de l'élément.
|
|
1626
|
+
const realErrors = consoleErrors.filter(e =>
|
|
1627
|
+
!e.includes('429') &&
|
|
1628
|
+
!e.includes('net::ERR_') &&
|
|
1629
|
+
!e.includes('Failed to load resource')
|
|
1630
|
+
);
|
|
1631
|
+
const networkErrors = consoleErrors.filter(e =>
|
|
1632
|
+
e.includes('429') || e.includes('net::ERR_') || e.includes('Failed to load resource')
|
|
1633
|
+
);
|
|
1634
|
+
|
|
1635
|
+
if (realErrors.length > 0) {
|
|
1553
1636
|
checks.push({
|
|
1554
1637
|
ok: false,
|
|
1555
|
-
label: `${
|
|
1556
|
-
got:
|
|
1557
|
-
hint: "
|
|
1638
|
+
label: `${realErrors.length} erreur(s) JS dans la console`,
|
|
1639
|
+
got: realErrors.slice(0, 3),
|
|
1640
|
+
hint: "Erreur JavaScript détectée — pas lié au serveur",
|
|
1558
1641
|
});
|
|
1559
1642
|
} else {
|
|
1560
|
-
checks.push({ ok: true, label: "Aucune erreur console" });
|
|
1643
|
+
checks.push({ ok: true, label: "Aucune erreur JS console" });
|
|
1644
|
+
}
|
|
1645
|
+
// Erreurs réseau : info seulement, pas un échec
|
|
1646
|
+
if (networkErrors.length > 0) {
|
|
1647
|
+
checks.push({
|
|
1648
|
+
ok: true,
|
|
1649
|
+
label: `${networkErrors.length} erreur(s) réseau (429/load) — non bloquant`,
|
|
1650
|
+
got: networkErrors.slice(0, 2),
|
|
1651
|
+
});
|
|
1561
1652
|
}
|
|
1562
1653
|
|
|
1563
1654
|
// Overflow horizontal
|