@yashwant.dharmdas/elementor-mcp 3.2.4 → 3.2.6
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 +78 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1090,28 +1090,56 @@ function createMcpServer(sites) {
|
|
|
1090
1090
|
await page.setViewport({ width: vpWidth, height: vpHeight, deviceScaleFactor: 1 });
|
|
1091
1091
|
// Navigate and wait for network to go idle
|
|
1092
1092
|
await page.goto(pageUrl, { waitUntil: "networkidle2", timeout: 45_000 });
|
|
1093
|
-
// ── Auto-scroll
|
|
1093
|
+
// ── Auto-scroll with multi-pass stabilization ────────────────────
|
|
1094
|
+
// Many Elementor pages lazy-load content on scroll (images, Swiper
|
|
1095
|
+
// carousels, reveal animations). A single scroll pass is not enough —
|
|
1096
|
+
// the page can grow as content loads. We keep scrolling until the
|
|
1097
|
+
// total page height stops increasing between passes.
|
|
1094
1098
|
if (auto_scroll !== false) {
|
|
1095
1099
|
await page.evaluate(async () => {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1100
|
+
const getHeight = () => Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight);
|
|
1101
|
+
const scrollOnce = () => new Promise((resolve) => {
|
|
1102
|
+
let y = 0;
|
|
1103
|
+
const step = 300;
|
|
1099
1104
|
const timer = setInterval(() => {
|
|
1100
|
-
const
|
|
1101
|
-
window.scrollBy(0,
|
|
1102
|
-
|
|
1103
|
-
if (
|
|
1105
|
+
const h = getHeight();
|
|
1106
|
+
window.scrollBy(0, step);
|
|
1107
|
+
y += step;
|
|
1108
|
+
if (y >= h) {
|
|
1104
1109
|
clearInterval(timer);
|
|
1105
1110
|
resolve();
|
|
1106
1111
|
}
|
|
1107
|
-
},
|
|
1112
|
+
}, 60);
|
|
1108
1113
|
});
|
|
1114
|
+
// Repeat until height stabilizes (max 4 passes)
|
|
1115
|
+
let lastHeight = 0;
|
|
1116
|
+
for (let i = 0; i < 4; i++) {
|
|
1117
|
+
window.scrollTo(0, 0);
|
|
1118
|
+
await new Promise(r => setTimeout(r, 150));
|
|
1119
|
+
await scrollOnce();
|
|
1120
|
+
// Wait at the bottom for lazy content to trigger
|
|
1121
|
+
await new Promise(r => setTimeout(r, 800));
|
|
1122
|
+
const h = getHeight();
|
|
1123
|
+
if (h === lastHeight)
|
|
1124
|
+
break;
|
|
1125
|
+
lastHeight = h;
|
|
1126
|
+
}
|
|
1127
|
+
// Back to top for the capture
|
|
1128
|
+
window.scrollTo(0, 0);
|
|
1109
1129
|
});
|
|
1110
|
-
// Scroll back to top for the capture
|
|
1111
|
-
await page.evaluate(() => window.scrollTo(0, 0));
|
|
1112
1130
|
}
|
|
1113
|
-
// ──
|
|
1131
|
+
// ── Force-load all <img> tags (including data-src / loading=lazy) ─
|
|
1114
1132
|
await page.evaluate(async () => {
|
|
1133
|
+
// Strip loading="lazy" so the browser loads everything eagerly
|
|
1134
|
+
document.querySelectorAll('img[loading="lazy"]').forEach((img) => {
|
|
1135
|
+
img.setAttribute("loading", "eager");
|
|
1136
|
+
});
|
|
1137
|
+
// Resolve data-src → src for libraries that defer loading
|
|
1138
|
+
document.querySelectorAll("img[data-src]").forEach((img) => {
|
|
1139
|
+
const ds = img.getAttribute("data-src");
|
|
1140
|
+
if (ds && !img.getAttribute("src"))
|
|
1141
|
+
img.setAttribute("src", ds);
|
|
1142
|
+
});
|
|
1115
1143
|
const images = Array.from(document.querySelectorAll("img"));
|
|
1116
1144
|
await Promise.all(images.map((img) => {
|
|
1117
1145
|
if (img.complete && img.naturalWidth > 0)
|
|
@@ -1119,39 +1147,65 @@ function createMcpServer(sites) {
|
|
|
1119
1147
|
return new Promise((resolve) => {
|
|
1120
1148
|
img.addEventListener("load", () => resolve(), { once: true });
|
|
1121
1149
|
img.addEventListener("error", () => resolve(), { once: true });
|
|
1122
|
-
// Safety timeout per image
|
|
1123
1150
|
setTimeout(() => resolve(), 5000);
|
|
1124
1151
|
});
|
|
1125
1152
|
}));
|
|
1126
1153
|
});
|
|
1127
|
-
// Extra settle time for fonts, animations,
|
|
1128
|
-
await new Promise(resolve => setTimeout(resolve, wait ??
|
|
1154
|
+
// Extra settle time for fonts, CSS animations, video posters
|
|
1155
|
+
await new Promise(resolve => setTimeout(resolve, wait ?? 2500));
|
|
1129
1156
|
const fmt = format ?? "jpeg";
|
|
1130
1157
|
const baseOptions = { type: fmt, fullPage: !max_height && (full_page ?? true) };
|
|
1131
1158
|
if (max_height)
|
|
1132
1159
|
baseOptions.clip = { x: 0, y: 0, width: vpWidth, height: vpHeight };
|
|
1133
|
-
// ── Adaptive
|
|
1160
|
+
// ── Adaptive capture: always fit under the MCP 1MB tool-result limit ──
|
|
1134
1161
|
// (base64 overhead ~33%, so raw buffer must be ≤ ~780KB)
|
|
1135
1162
|
const PREVIEW_LIMIT = 750 * 1024;
|
|
1136
|
-
let buf;
|
|
1163
|
+
let buf = Buffer.alloc(0);
|
|
1137
1164
|
let usedQuality = undefined;
|
|
1165
|
+
let usedScale = 1;
|
|
1166
|
+
const takeOne = async (q, scale) => {
|
|
1167
|
+
if (scale !== 1) {
|
|
1168
|
+
// Re-apply viewport with scale factor to shrink pixel dimensions
|
|
1169
|
+
await page.setViewport({ width: Math.round(vpWidth * scale), height: Math.round(vpHeight * scale), deviceScaleFactor: 1 });
|
|
1170
|
+
// Allow layout to reflow
|
|
1171
|
+
await new Promise(r => setTimeout(r, 500));
|
|
1172
|
+
}
|
|
1173
|
+
const opts = { ...baseOptions };
|
|
1174
|
+
if (q !== undefined)
|
|
1175
|
+
opts.quality = q;
|
|
1176
|
+
const raw = await page.screenshot(opts);
|
|
1177
|
+
return Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
1178
|
+
};
|
|
1138
1179
|
if (fmt === "jpeg") {
|
|
1139
|
-
//
|
|
1180
|
+
// Stage 1: try full resolution, progressive quality
|
|
1140
1181
|
const q0 = quality ?? 82;
|
|
1141
1182
|
const qualitySteps = Array.from(new Set([q0, 70, 55, 40, 25, 15])).filter(q => q <= q0);
|
|
1142
1183
|
for (const q of qualitySteps) {
|
|
1143
|
-
|
|
1144
|
-
buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
1184
|
+
buf = await takeOne(q, 1);
|
|
1145
1185
|
usedQuality = q;
|
|
1146
1186
|
if (buf.length <= PREVIEW_LIMIT)
|
|
1147
1187
|
break;
|
|
1148
1188
|
}
|
|
1189
|
+
// Stage 2: if still too big, shrink viewport width progressively
|
|
1190
|
+
const scaleSteps = [0.7, 0.5, 0.35, 0.25];
|
|
1191
|
+
for (const s of scaleSteps) {
|
|
1192
|
+
if (buf.length <= PREVIEW_LIMIT)
|
|
1193
|
+
break;
|
|
1194
|
+
buf = await takeOne(40, s);
|
|
1195
|
+
usedQuality = 40;
|
|
1196
|
+
usedScale = s;
|
|
1197
|
+
if (buf.length <= PREVIEW_LIMIT)
|
|
1198
|
+
break;
|
|
1199
|
+
// Inner quality drop at this scale
|
|
1200
|
+
buf = await takeOne(20, s);
|
|
1201
|
+
usedQuality = 20;
|
|
1202
|
+
if (buf.length <= PREVIEW_LIMIT)
|
|
1203
|
+
break;
|
|
1204
|
+
}
|
|
1149
1205
|
}
|
|
1150
1206
|
else {
|
|
1151
|
-
|
|
1152
|
-
buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
1207
|
+
buf = await takeOne(undefined, 1);
|
|
1153
1208
|
}
|
|
1154
|
-
buf = buf;
|
|
1155
1209
|
// ── 5. Save to disk ─────────────────────────────────────────────
|
|
1156
1210
|
const ext = fmt === "jpeg" ? "jpg" : "png";
|
|
1157
1211
|
const idPart = page_id ?? (pageUrl.replace(/https?:\/\//, "").replace(/[^a-z0-9]/gi, "_").slice(0, 40));
|
|
@@ -1161,7 +1215,7 @@ function createMcpServer(sites) {
|
|
|
1161
1215
|
const sizeKB = Math.round(buf.length / 1024);
|
|
1162
1216
|
const mimeType = `image/${fmt}`;
|
|
1163
1217
|
const qualityNote = usedQuality !== undefined && usedQuality !== (quality ?? 82)
|
|
1164
|
-
? ` | Auto-downscaled to quality ${usedQuality} to fit MCP limit`
|
|
1218
|
+
? ` | Auto-downscaled to quality ${usedQuality}${usedScale !== 1 ? ` @ ${Math.round(usedScale * 100)}% width` : ""} to fit MCP limit`
|
|
1165
1219
|
: "";
|
|
1166
1220
|
// ── 6. Always return the image so Claude can analyse it ──────
|
|
1167
1221
|
const content = [];
|
package/package.json
CHANGED