@yashwant.dharmdas/elementor-mcp 3.2.6 → 3.2.8

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.
Files changed (2) hide show
  1. package/dist/index.js +133 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1092,15 +1092,16 @@ function createMcpServer(sites) {
1092
1092
  await page.goto(pageUrl, { waitUntil: "networkidle2", timeout: 45_000 });
1093
1093
  // ── Auto-scroll with multi-pass stabilization ────────────────────
1094
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.
1095
+ // carousels, reveal animations). We scroll slowly so IntersectionObservers
1096
+ // have time to fire, then wait at the bottom for content to settle, and
1097
+ // repeat passes until the total page height stops increasing.
1098
1098
  if (auto_scroll !== false) {
1099
1099
  await page.evaluate(async () => {
1100
1100
  const getHeight = () => Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight);
1101
1101
  const scrollOnce = () => new Promise((resolve) => {
1102
1102
  let y = 0;
1103
- const step = 300;
1103
+ const step = 180; // smaller step
1104
+ const delay = 110; // slower interval — gives observers time to fire
1104
1105
  const timer = setInterval(() => {
1105
1106
  const h = getHeight();
1106
1107
  window.scrollBy(0, step);
@@ -1109,50 +1110,166 @@ function createMcpServer(sites) {
1109
1110
  clearInterval(timer);
1110
1111
  resolve();
1111
1112
  }
1112
- }, 60);
1113
+ }, delay);
1113
1114
  });
1114
1115
  // Repeat until height stabilizes (max 4 passes)
1115
1116
  let lastHeight = 0;
1116
1117
  for (let i = 0; i < 4; i++) {
1117
1118
  window.scrollTo(0, 0);
1118
- await new Promise(r => setTimeout(r, 150));
1119
+ await new Promise(r => setTimeout(r, 200));
1119
1120
  await scrollOnce();
1120
- // Wait at the bottom for lazy content to trigger
1121
- await new Promise(r => setTimeout(r, 800));
1121
+ // Long wait at the bottom bg images / Swipers need time
1122
+ await new Promise(r => setTimeout(r, 1500));
1122
1123
  const h = getHeight();
1123
- if (h === lastHeight)
1124
+ if (h === lastHeight && i > 0)
1124
1125
  break;
1125
1126
  lastHeight = h;
1126
1127
  }
1128
+ // One more slow scroll pass at half speed to nail any stragglers
1129
+ await new Promise((resolve) => {
1130
+ window.scrollTo(0, 0);
1131
+ let y = 0;
1132
+ const step = 120;
1133
+ const timer = setInterval(() => {
1134
+ const h = getHeight();
1135
+ window.scrollBy(0, step);
1136
+ y += step;
1137
+ if (y >= h) {
1138
+ clearInterval(timer);
1139
+ resolve();
1140
+ }
1141
+ }, 130);
1142
+ });
1143
+ await new Promise(r => setTimeout(r, 1000));
1127
1144
  // Back to top for the capture
1128
1145
  window.scrollTo(0, 0);
1129
1146
  });
1130
1147
  }
1131
- // ── Force-load all <img> tags (including data-src / loading=lazy)
1148
+ // ── Force-load all images, Elementor backgrounds, and reveal sections
1132
1149
  await page.evaluate(async () => {
1133
- // Strip loading="lazy" so the browser loads everything eagerly
1150
+ // 1. Inject CSS that disables animations AND forces opacity:1
1151
+ // on Elementor's hidden reveal states. This MUST be first so
1152
+ // subsequent layout / render passes don't re-hide content.
1153
+ const style = document.createElement("style");
1154
+ style.id = "__mcp_screenshot_overrides__";
1155
+ style.textContent = `
1156
+ *, *::before, *::after {
1157
+ animation-duration: 0s !important;
1158
+ animation-delay: 0s !important;
1159
+ transition-duration: 0s !important;
1160
+ transition-delay: 0s !important;
1161
+ }
1162
+ .elementor-invisible,
1163
+ .elementor-element[data-settings*="animation"] {
1164
+ opacity: 1 !important;
1165
+ visibility: visible !important;
1166
+ transform: none !important;
1167
+ }
1168
+ /* Defeat any lazy-load opacity tricks */
1169
+ .elementor-element { opacity: 1 !important; }
1170
+ `;
1171
+ document.head.appendChild(style);
1172
+ // 2. Strip loading="lazy" on images
1134
1173
  document.querySelectorAll('img[loading="lazy"]').forEach((img) => {
1135
1174
  img.setAttribute("loading", "eager");
1136
1175
  });
1137
- // Resolve data-src → src for libraries that defer loading
1176
+ // 3. Resolve data-src → src for any deferred-load libraries
1138
1177
  document.querySelectorAll("img[data-src]").forEach((img) => {
1139
1178
  const ds = img.getAttribute("data-src");
1140
1179
  if (ds && !img.getAttribute("src"))
1141
1180
  img.setAttribute("src", ds);
1142
1181
  });
1182
+ // 4. Resolve <source srcset> inside <picture> (just in case)
1183
+ document.querySelectorAll("source[data-srcset]").forEach((src) => {
1184
+ const ds = src.getAttribute("data-srcset");
1185
+ if (ds && !src.getAttribute("srcset"))
1186
+ src.setAttribute("srcset", ds);
1187
+ });
1188
+ // 5. Force-load Elementor's lazy background images.
1189
+ // Elementor uses MULTIPLE patterns over the years:
1190
+ // - class .elementor-element-bg-lazyload + CSS var --e-bg-lazyload
1191
+ // - data-bg / data-background-image
1192
+ // - data-settings JSON containing background_image
1193
+ // We try all of them.
1194
+ document.querySelectorAll(".elementor-element-bg-lazyload, [data-bg], [data-background-image], [data-settings]").forEach((el) => {
1195
+ let url = el.getAttribute("data-bg") ||
1196
+ el.getAttribute("data-background-image");
1197
+ // Try the CSS variable
1198
+ if (!url) {
1199
+ const v = getComputedStyle(el).getPropertyValue("--e-bg-lazyload").trim();
1200
+ if (v && v !== "none") {
1201
+ url = v.replace(/^url\(["']?|["']?\)$/g, "");
1202
+ }
1203
+ }
1204
+ // Try parsing data-settings JSON
1205
+ if (!url) {
1206
+ try {
1207
+ const ds = el.getAttribute("data-settings");
1208
+ if (ds) {
1209
+ const obj = JSON.parse(ds);
1210
+ url = obj?.background_image?.url
1211
+ || obj?._background_image?.url
1212
+ || obj?.background_overlay_image?.url
1213
+ || null;
1214
+ }
1215
+ }
1216
+ catch { /* ignore */ }
1217
+ }
1218
+ if (url && url !== "none") {
1219
+ // Set inline so it overrides any external lazy CSS
1220
+ el.style.setProperty("background-image", `url("${url}")`, "important");
1221
+ el.style.setProperty("--e-bg-lazyload", "none");
1222
+ }
1223
+ el.classList.remove("elementor-element-bg-lazyload");
1224
+ });
1225
+ // 6. Force-show Elementor reveal-animation elements
1226
+ document.querySelectorAll(".elementor-invisible").forEach((el) => {
1227
+ el.classList.remove("elementor-invisible");
1228
+ el.style.opacity = "1";
1229
+ el.style.visibility = "visible";
1230
+ el.style.transform = "none";
1231
+ });
1232
+ // 7. Re-trigger Elementor frontend lazy-load if available.
1233
+ // Elementor exposes the global `elementorFrontend`; calling its
1234
+ // elementsHandler reinitialises lazy modules.
1235
+ try {
1236
+ const ef = window.elementorFrontend;
1237
+ if (ef?.elementsHandler?.runReadyTrigger) {
1238
+ document.querySelectorAll(".elementor-element").forEach((el) => {
1239
+ try {
1240
+ ef.elementsHandler.runReadyTrigger(el);
1241
+ }
1242
+ catch { /* ignore */ }
1243
+ });
1244
+ }
1245
+ }
1246
+ catch { /* ignore */ }
1247
+ // 8. Dispatch scroll/resize to wake any remaining IntersectionObservers
1248
+ window.dispatchEvent(new Event("scroll"));
1249
+ window.dispatchEvent(new Event("resize"));
1250
+ document.dispatchEvent(new Event("DOMContentLoaded"));
1251
+ // 9. Wait for ALL <img> tags (now eager) to finish loading + decode
1143
1252
  const images = Array.from(document.querySelectorAll("img"));
1144
1253
  await Promise.all(images.map((img) => {
1145
- if (img.complete && img.naturalWidth > 0)
1146
- return Promise.resolve();
1254
+ if (img.complete && img.naturalWidth > 0) {
1255
+ return img.decode?.().catch(() => { }) ?? Promise.resolve();
1256
+ }
1147
1257
  return new Promise((resolve) => {
1148
1258
  img.addEventListener("load", () => resolve(), { once: true });
1149
1259
  img.addEventListener("error", () => resolve(), { once: true });
1150
1260
  setTimeout(() => resolve(), 5000);
1151
1261
  });
1152
1262
  }));
1263
+ // 10. Wait for fonts to be fully ready (avoids FOUT in screenshot)
1264
+ if (document.fonts?.ready) {
1265
+ try {
1266
+ await document.fonts.ready;
1267
+ }
1268
+ catch { /* ignore */ }
1269
+ }
1153
1270
  });
1154
- // Extra settle time for fonts, CSS animations, video posters
1155
- await new Promise(resolve => setTimeout(resolve, wait ?? 2500));
1271
+ // Extra settle time bumped to 3500ms default to handle slow CDNs / videos
1272
+ await new Promise(resolve => setTimeout(resolve, wait ?? 3500));
1156
1273
  const fmt = format ?? "jpeg";
1157
1274
  const baseOptions = { type: fmt, fullPage: !max_height && (full_page ?? true) };
1158
1275
  if (max_height)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yashwant.dharmdas/elementor-mcp",
3
- "version": "3.2.6",
3
+ "version": "3.2.8",
4
4
  "description": "MCP server for controlling Elementor via Claude — supports multiple WordPress sites",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",