safari-mcp 2.8.8 → 2.9.0

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.
@@ -1287,7 +1287,53 @@ async function handleCommand(type, payload) {
1287
1287
  }
1288
1288
  }
1289
1289
  if (!root) return "Element not found: " + rootSelector;
1290
- let tree = walk(root, 0);
1290
+
1291
+ // TOP-LAYER MODAL DETECTION: when a modal/dialog is open, user intent is almost always
1292
+ // to interact with it, not the page behind. Pages like Google Business Profile, Drive,
1293
+ // Airtable rich dialogs open top-layer content that standard DOM walks miss or bury.
1294
+ // Detect visible modals and, if found, walk them FIRST so their refs appear at the top.
1295
+ let topLayerTree = "";
1296
+ if (!rootSelector) {
1297
+ const seenModals = new Set();
1298
+ const modalSelectors = [
1299
+ 'dialog[open]',
1300
+ '[role="dialog"]:not([aria-hidden="true"])',
1301
+ '[role="alertdialog"]:not([aria-hidden="true"])',
1302
+ '[aria-modal="true"]',
1303
+ '[data-radix-dialog-content]',
1304
+ '[data-headlessui-state*="open"][role="dialog"]',
1305
+ '.MuiDialog-container',
1306
+ // Google overlays: Search/GBP/Drive editors use these markers
1307
+ '[jscontroller][aria-modal]',
1308
+ '[jscontroller][role="dialog"]',
1309
+ 'c-wiz[role="dialog"]',
1310
+ 'c-wiz[aria-modal]'
1311
+ ];
1312
+ for (const sel of modalSelectors) {
1313
+ let nodes;
1314
+ try { nodes = document.querySelectorAll(sel); } catch (_) { continue; }
1315
+ for (const m of nodes) {
1316
+ if (seenModals.has(m)) continue;
1317
+ seenModals.add(m);
1318
+ const cs = window.getComputedStyle(m);
1319
+ if (cs.display === "none" || cs.visibility === "hidden" || cs.opacity === "0") continue;
1320
+ const r = m.getBoundingClientRect();
1321
+ // A real modal covers a meaningful area
1322
+ if (r.width < 150 || r.height < 100) continue;
1323
+ // Tombstones: modals at 0,0 with 0 size are placeholders
1324
+ if (r.width === 0 || r.height === 0) continue;
1325
+ // Nested modals: skip if we already included a parent
1326
+ let isNested = false;
1327
+ for (const other of seenModals) {
1328
+ if (other !== m && other.contains(m)) { isNested = true; break; }
1329
+ }
1330
+ if (isNested) continue;
1331
+ topLayerTree += walk(m, 0);
1332
+ }
1333
+ }
1334
+ }
1335
+
1336
+ let tree = topLayerTree + walk(root, 0);
1291
1337
  // Shadow roots are now walked INLINE inside walk() — no separate walkShadows needed.
1292
1338
  // Walk same-origin iframes
1293
1339
  const iframes = document.querySelectorAll("iframe");
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "name": "Safari MCP Bridge",
4
4
  "description": "Connects AI agents to Safari — fast JS execution, screenshots, clicks with your real cookies/logins",
5
- "version": "2.7.2",
5
+ "version": "2.9.0",
6
6
  "icons": {
7
7
  "48": "images/icon-48.png",
8
8
  "96": "images/icon-96.png",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safari-mcp",
3
- "version": "2.8.8",
3
+ "version": "2.9.0",
4
4
  "mcpName": "io.github.achiya-automation/safari-mcp",
5
5
  "description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 80 tools via AppleScript + JavaScript.",
6
6
  "type": "module",
@@ -76,7 +76,7 @@
76
76
  "ws": "^8.19.0"
77
77
  },
78
78
  "overrides": {
79
- "hono": "^4.12.12",
79
+ "hono": "^4.12.14",
80
80
  "@hono/node-server": "^1.19.13"
81
81
  }
82
82
  }