@midscene/shared 0.17.2 → 0.17.3-beta-20250526042111.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.
@@ -197,6 +197,9 @@ function isFormElement(node) {
197
197
  function isButtonElement(node) {
198
198
  return node instanceof HTMLElement && node.tagName.toLowerCase() === "button";
199
199
  }
200
+ function isAElement(node) {
201
+ return node instanceof HTMLElement && node.tagName.toLowerCase() === "a";
202
+ }
200
203
  function isImgElement(node) {
201
204
  if (!includeBaseElement(node) && node instanceof Element) {
202
205
  const computedStyle = window.getComputedStyle(node);
@@ -247,7 +250,8 @@ function includeBaseElement(node) {
247
250
  "textarea",
248
251
  "select",
249
252
  "option",
250
- "img"
253
+ "img",
254
+ "a"
251
255
  ];
252
256
  for (const tagName of includeList) {
253
257
  const element = node.querySelectorAll(tagName);
@@ -636,10 +640,20 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
636
640
  return elementInfo;
637
641
  }
638
642
  if (isButtonElement(node)) {
643
+ const rect2 = mergeElementAndChildrenRects(
644
+ node,
645
+ currentWindow,
646
+ currentDocument,
647
+ baseZoom,
648
+ visibleOnly
649
+ );
650
+ if (!rect2) {
651
+ return null;
652
+ }
639
653
  const attributes = getNodeAttributes(node, currentWindow);
640
654
  const pseudo = getPseudoElementContent(node, currentWindow);
641
655
  const content = node.innerText || pseudo.before || pseudo.after || "";
642
- const nodeHashId = midsceneGenerateHash(node, content, rect);
656
+ const nodeHashId = midsceneGenerateHash(node, content, rect2);
643
657
  const selector = setDataForNode(node, nodeHashId, false, currentWindow);
644
658
  const elementInfo = {
645
659
  id: nodeHashId,
@@ -653,12 +667,12 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
653
667
  nodeType: "BUTTON Node" /* BUTTON */
654
668
  },
655
669
  content,
656
- rect,
670
+ rect: rect2,
657
671
  center: [
658
- Math.round(rect.left + rect.width / 2),
659
- Math.round(rect.top + rect.height / 2)
672
+ Math.round(rect2.left + rect2.width / 2),
673
+ Math.round(rect2.top + rect2.height / 2)
660
674
  ],
661
- zoom: rect.zoom
675
+ zoom: rect2.zoom
662
676
  };
663
677
  return elementInfo;
664
678
  }
@@ -723,6 +737,33 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
723
737
  };
724
738
  return elementInfo;
725
739
  }
740
+ if (isAElement(node)) {
741
+ const attributes = getNodeAttributes(node, currentWindow);
742
+ const pseudo = getPseudoElementContent(node, currentWindow);
743
+ const content = node.innerText || pseudo.before || pseudo.after || "";
744
+ const nodeHashId = midsceneGenerateHash(node, content, rect);
745
+ const selector = setDataForNode(node, nodeHashId, false, currentWindow);
746
+ const elementInfo = {
747
+ id: nodeHashId,
748
+ indexId: indexId++,
749
+ nodeHashId,
750
+ nodeType: "Anchor Node" /* A */,
751
+ locator: selector,
752
+ attributes: {
753
+ ...attributes,
754
+ htmlTagName: tagNameOfNode(node),
755
+ nodeType: "Anchor Node" /* A */
756
+ },
757
+ content,
758
+ rect,
759
+ center: [
760
+ Math.round(rect.left + rect.width / 2),
761
+ Math.round(rect.top + rect.height / 2)
762
+ ],
763
+ zoom: rect.zoom
764
+ };
765
+ return elementInfo;
766
+ }
726
767
  if (isContainerElement(node)) {
727
768
  const attributes = getNodeAttributes(node, currentWindow);
728
769
  const nodeHashId = midsceneGenerateHash(node, "", rect);
@@ -797,7 +838,7 @@ function extractTreeNode(initNode, debugMode2 = false) {
797
838
  node: elementInfo,
798
839
  children: []
799
840
  };
800
- if (elementInfo?.nodeType === "BUTTON Node" /* BUTTON */ || elementInfo?.nodeType === "IMG Node" /* IMG */ || elementInfo?.nodeType === "TEXT Node" /* TEXT */ || elementInfo?.nodeType === "FORM_ITEM Node" /* FORM_ITEM */ || elementInfo?.nodeType === "CONTAINER Node" /* CONTAINER */) {
841
+ if (elementInfo?.nodeType === "BUTTON Node" /* BUTTON */ || elementInfo?.nodeType === "IMG Node" /* IMG */ || elementInfo?.nodeType === "TEXT Node" /* TEXT */ || elementInfo?.nodeType === "Anchor Node" /* A */ || elementInfo?.nodeType === "FORM_ITEM Node" /* FORM_ITEM */ || elementInfo?.nodeType === "CONTAINER Node" /* CONTAINER */) {
801
842
  return nodeInfo;
802
843
  }
803
844
  const rect = getRect(node, baseZoom, currentWindow);
@@ -852,6 +893,50 @@ function extractTreeNode(initNode, debugMode2 = false) {
852
893
  children: topChildren
853
894
  };
854
895
  }
896
+ function mergeElementAndChildrenRects(node, currentWindow, currentDocument, baseZoom = 1, visibleOnly = true) {
897
+ const selfRect = elementRect(
898
+ node,
899
+ currentWindow,
900
+ currentDocument,
901
+ baseZoom,
902
+ visibleOnly
903
+ );
904
+ if (!selfRect)
905
+ return null;
906
+ let minLeft = selfRect.left;
907
+ let minTop = selfRect.top;
908
+ let maxRight = selfRect.left + selfRect.width;
909
+ let maxBottom = selfRect.top + selfRect.height;
910
+ function traverse(child) {
911
+ for (let i = 0; i < child.childNodes.length; i++) {
912
+ const sub = child.childNodes[i];
913
+ if (sub.nodeType === 1) {
914
+ const rect = elementRect(
915
+ sub,
916
+ currentWindow,
917
+ currentDocument,
918
+ baseZoom,
919
+ visibleOnly
920
+ );
921
+ if (rect) {
922
+ minLeft = Math.min(minLeft, rect.left);
923
+ minTop = Math.min(minTop, rect.top);
924
+ maxRight = Math.max(maxRight, rect.left + rect.width);
925
+ maxBottom = Math.max(maxBottom, rect.top + rect.height);
926
+ }
927
+ traverse(sub);
928
+ }
929
+ }
930
+ }
931
+ traverse(node);
932
+ return {
933
+ ...selfRect,
934
+ left: minLeft,
935
+ top: minTop,
936
+ width: maxRight - minLeft,
937
+ height: maxBottom - minTop
938
+ };
939
+ }
855
940
 
856
941
  // src/extractor/locator.ts
857
942
  var getElementIndex = (element) => {
@@ -865,16 +950,6 @@ var getElementIndex = (element) => {
865
950
  }
866
951
  return index;
867
952
  };
868
- var findFirstAncestorWithId = (element) => {
869
- let current = element;
870
- while (current?.parentElement) {
871
- if (current.id) {
872
- return current;
873
- }
874
- current = current.parentElement;
875
- }
876
- return null;
877
- };
878
953
  var getTextNodeIndex = (textNode) => {
879
954
  let index = 1;
880
955
  let current = textNode.previousSibling;
@@ -892,6 +967,10 @@ var getElementXPath = (element) => {
892
967
  if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) {
893
968
  const parentXPath = getElementXPath(parentNode);
894
969
  const textIndex = getTextNodeIndex(element);
970
+ const textContent = element.textContent?.trim();
971
+ if (textContent) {
972
+ return `${parentXPath}/text()[${textIndex}][normalize-space()="${textContent}"]`;
973
+ }
895
974
  return `${parentXPath}/text()[${textIndex}]`;
896
975
  }
897
976
  return "";
@@ -905,22 +984,6 @@ var getElementXPath = (element) => {
905
984
  if (el === document.body) {
906
985
  return "/html/body";
907
986
  }
908
- if (el.id) {
909
- return `//*[@id="${el.id}"]`;
910
- }
911
- const ancestorWithId = findFirstAncestorWithId(el);
912
- if (ancestorWithId) {
913
- const ancestorPath = `//*[@id="${ancestorWithId.id}"]`;
914
- let current = el;
915
- const pathParts = [];
916
- while (current && current !== ancestorWithId) {
917
- const index2 = getElementIndex(current);
918
- const tagName2 = current.nodeName.toLowerCase();
919
- pathParts.unshift(`${tagName2}[${index2}]`);
920
- current = current.parentElement;
921
- }
922
- return pathParts.length > 0 ? `${ancestorPath}/${pathParts.join("/")}` : ancestorPath;
923
- }
924
987
  if (!el.parentNode) {
925
988
  return `/${el.nodeName.toLowerCase()}`;
926
989
  }
@@ -935,8 +998,8 @@ var getElementXPath = (element) => {
935
998
  function generateXPaths(node) {
936
999
  if (!node)
937
1000
  return [];
938
- const xPath = getElementXPath(node);
939
- return [xPath];
1001
+ const fullXPath = getElementXPath(node);
1002
+ return [fullXPath];
940
1003
  }
941
1004
  function getXpathsById(id) {
942
1005
  const node = getNodeFromCacheList(id);
@@ -171,7 +171,6 @@ var getMidsceneRunSubDir = (subdir) => {
171
171
  }
172
172
  return logPath;
173
173
  };
174
- var logDir = isNodeEnv ? getMidsceneRunSubDir("log") : "";
175
174
 
176
175
  // src/logger.ts
177
176
  var topicPrefix = "midscene";
@@ -179,7 +178,10 @@ var logStreams = /* @__PURE__ */ new Map();
179
178
  function getLogStream(topic) {
180
179
  const topicFileName = topic.replace(/:/g, "-");
181
180
  if (!logStreams.has(topicFileName)) {
182
- const logFile = import_node_path2.default.join(logDir, `${topicFileName}.log`);
181
+ const logFile = import_node_path2.default.join(
182
+ getMidsceneRunSubDir("log"),
183
+ `${topicFileName}.log`
184
+ );
183
185
  const stream = import_node_fs2.default.createWriteStream(logFile, { flags: "a" });
184
186
  logStreams.set(topicFileName, stream);
185
187
  }
@@ -755,6 +755,9 @@ ${indentStr}${after}`;
755
755
  function isButtonElement(node) {
756
756
  return node instanceof HTMLElement && node.tagName.toLowerCase() === "button";
757
757
  }
758
+ function isAElement(node) {
759
+ return node instanceof HTMLElement && node.tagName.toLowerCase() === "a";
760
+ }
758
761
  function isImgElement(node) {
759
762
  if (!includeBaseElement(node) && node instanceof Element) {
760
763
  const computedStyle = window.getComputedStyle(node);
@@ -805,7 +808,8 @@ ${indentStr}${after}`;
805
808
  "textarea",
806
809
  "select",
807
810
  "option",
808
- "img"
811
+ "img",
812
+ "a"
809
813
  ];
810
814
  for (const tagName of includeList) {
811
815
  const element = node.querySelectorAll(tagName);
@@ -1196,10 +1200,20 @@ ${indentStr}${after}`;
1196
1200
  return elementInfo;
1197
1201
  }
1198
1202
  if (isButtonElement(node)) {
1203
+ const rect2 = mergeElementAndChildrenRects(
1204
+ node,
1205
+ currentWindow,
1206
+ currentDocument,
1207
+ baseZoom,
1208
+ visibleOnly
1209
+ );
1210
+ if (!rect2) {
1211
+ return null;
1212
+ }
1199
1213
  const attributes = getNodeAttributes(node, currentWindow);
1200
1214
  const pseudo = getPseudoElementContent(node, currentWindow);
1201
1215
  const content = node.innerText || pseudo.before || pseudo.after || "";
1202
- const nodeHashId = midsceneGenerateHash(node, content, rect);
1216
+ const nodeHashId = midsceneGenerateHash(node, content, rect2);
1203
1217
  const selector = setDataForNode(node, nodeHashId, false, currentWindow);
1204
1218
  const elementInfo = {
1205
1219
  id: nodeHashId,
@@ -1212,12 +1226,12 @@ ${indentStr}${after}`;
1212
1226
  nodeType: "BUTTON Node" /* BUTTON */
1213
1227
  }),
1214
1228
  content,
1215
- rect,
1229
+ rect: rect2,
1216
1230
  center: [
1217
- Math.round(rect.left + rect.width / 2),
1218
- Math.round(rect.top + rect.height / 2)
1231
+ Math.round(rect2.left + rect2.width / 2),
1232
+ Math.round(rect2.top + rect2.height / 2)
1219
1233
  ],
1220
- zoom: rect.zoom
1234
+ zoom: rect2.zoom
1221
1235
  };
1222
1236
  return elementInfo;
1223
1237
  }
@@ -1279,6 +1293,32 @@ ${indentStr}${after}`;
1279
1293
  };
1280
1294
  return elementInfo;
1281
1295
  }
1296
+ if (isAElement(node)) {
1297
+ const attributes = getNodeAttributes(node, currentWindow);
1298
+ const pseudo = getPseudoElementContent(node, currentWindow);
1299
+ const content = node.innerText || pseudo.before || pseudo.after || "";
1300
+ const nodeHashId = midsceneGenerateHash(node, content, rect);
1301
+ const selector = setDataForNode(node, nodeHashId, false, currentWindow);
1302
+ const elementInfo = {
1303
+ id: nodeHashId,
1304
+ indexId: indexId++,
1305
+ nodeHashId,
1306
+ nodeType: "Anchor Node" /* A */,
1307
+ locator: selector,
1308
+ attributes: __spreadProps(__spreadValues({}, attributes), {
1309
+ htmlTagName: tagNameOfNode(node),
1310
+ nodeType: "Anchor Node" /* A */
1311
+ }),
1312
+ content,
1313
+ rect,
1314
+ center: [
1315
+ Math.round(rect.left + rect.width / 2),
1316
+ Math.round(rect.top + rect.height / 2)
1317
+ ],
1318
+ zoom: rect.zoom
1319
+ };
1320
+ return elementInfo;
1321
+ }
1282
1322
  if (isContainerElement(node)) {
1283
1323
  const attributes = getNodeAttributes(node, currentWindow);
1284
1324
  const nodeHashId = midsceneGenerateHash(node, "", rect);
@@ -1352,7 +1392,7 @@ ${indentStr}${after}`;
1352
1392
  node: elementInfo,
1353
1393
  children: []
1354
1394
  };
1355
- if ((elementInfo == null ? void 0 : elementInfo.nodeType) === "BUTTON Node" /* BUTTON */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "IMG Node" /* IMG */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "TEXT Node" /* TEXT */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "FORM_ITEM Node" /* FORM_ITEM */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "CONTAINER Node" /* CONTAINER */) {
1395
+ if ((elementInfo == null ? void 0 : elementInfo.nodeType) === "BUTTON Node" /* BUTTON */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "IMG Node" /* IMG */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "TEXT Node" /* TEXT */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "Anchor Node" /* A */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "FORM_ITEM Node" /* FORM_ITEM */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "CONTAINER Node" /* CONTAINER */) {
1356
1396
  return nodeInfo;
1357
1397
  }
1358
1398
  const rect = getRect(node, baseZoom, currentWindow);
@@ -1407,6 +1447,49 @@ ${indentStr}${after}`;
1407
1447
  children: topChildren
1408
1448
  };
1409
1449
  }
1450
+ function mergeElementAndChildrenRects(node, currentWindow, currentDocument, baseZoom = 1, visibleOnly = true) {
1451
+ const selfRect = elementRect(
1452
+ node,
1453
+ currentWindow,
1454
+ currentDocument,
1455
+ baseZoom,
1456
+ visibleOnly
1457
+ );
1458
+ if (!selfRect)
1459
+ return null;
1460
+ let minLeft = selfRect.left;
1461
+ let minTop = selfRect.top;
1462
+ let maxRight = selfRect.left + selfRect.width;
1463
+ let maxBottom = selfRect.top + selfRect.height;
1464
+ function traverse(child) {
1465
+ for (let i = 0; i < child.childNodes.length; i++) {
1466
+ const sub = child.childNodes[i];
1467
+ if (sub.nodeType === 1) {
1468
+ const rect = elementRect(
1469
+ sub,
1470
+ currentWindow,
1471
+ currentDocument,
1472
+ baseZoom,
1473
+ visibleOnly
1474
+ );
1475
+ if (rect) {
1476
+ minLeft = Math.min(minLeft, rect.left);
1477
+ minTop = Math.min(minTop, rect.top);
1478
+ maxRight = Math.max(maxRight, rect.left + rect.width);
1479
+ maxBottom = Math.max(maxBottom, rect.top + rect.height);
1480
+ }
1481
+ traverse(sub);
1482
+ }
1483
+ }
1484
+ }
1485
+ traverse(node);
1486
+ return __spreadProps(__spreadValues({}, selfRect), {
1487
+ left: minLeft,
1488
+ top: minTop,
1489
+ width: maxRight - minLeft,
1490
+ height: maxBottom - minTop
1491
+ });
1492
+ }
1410
1493
 
1411
1494
  // src/extractor/locator.ts
1412
1495
  var getElementIndex = (element) => {
@@ -1420,16 +1503,6 @@ ${indentStr}${after}`;
1420
1503
  }
1421
1504
  return index;
1422
1505
  };
1423
- var findFirstAncestorWithId = (element) => {
1424
- let current = element;
1425
- while (current == null ? void 0 : current.parentElement) {
1426
- if (current.id) {
1427
- return current;
1428
- }
1429
- current = current.parentElement;
1430
- }
1431
- return null;
1432
- };
1433
1506
  var getTextNodeIndex = (textNode) => {
1434
1507
  let index = 1;
1435
1508
  let current = textNode.previousSibling;
@@ -1442,11 +1515,16 @@ ${indentStr}${after}`;
1442
1515
  return index;
1443
1516
  };
1444
1517
  var getElementXPath = (element) => {
1518
+ var _a;
1445
1519
  if (element.nodeType === Node.TEXT_NODE) {
1446
1520
  const parentNode = element.parentNode;
1447
1521
  if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) {
1448
1522
  const parentXPath = getElementXPath(parentNode);
1449
1523
  const textIndex = getTextNodeIndex(element);
1524
+ const textContent = (_a = element.textContent) == null ? void 0 : _a.trim();
1525
+ if (textContent) {
1526
+ return `${parentXPath}/text()[${textIndex}][normalize-space()="${textContent}"]`;
1527
+ }
1450
1528
  return `${parentXPath}/text()[${textIndex}]`;
1451
1529
  }
1452
1530
  return "";
@@ -1460,22 +1538,6 @@ ${indentStr}${after}`;
1460
1538
  if (el === document.body) {
1461
1539
  return "/html/body";
1462
1540
  }
1463
- if (el.id) {
1464
- return `//*[@id="${el.id}"]`;
1465
- }
1466
- const ancestorWithId = findFirstAncestorWithId(el);
1467
- if (ancestorWithId) {
1468
- const ancestorPath = `//*[@id="${ancestorWithId.id}"]`;
1469
- let current = el;
1470
- const pathParts = [];
1471
- while (current && current !== ancestorWithId) {
1472
- const index2 = getElementIndex(current);
1473
- const tagName2 = current.nodeName.toLowerCase();
1474
- pathParts.unshift(`${tagName2}[${index2}]`);
1475
- current = current.parentElement;
1476
- }
1477
- return pathParts.length > 0 ? `${ancestorPath}/${pathParts.join("/")}` : ancestorPath;
1478
- }
1479
1541
  if (!el.parentNode) {
1480
1542
  return `/${el.nodeName.toLowerCase()}`;
1481
1543
  }
@@ -1490,8 +1552,8 @@ ${indentStr}${after}`;
1490
1552
  function generateXPaths(node) {
1491
1553
  if (!node)
1492
1554
  return [];
1493
- const xPath = getElementXPath(node);
1494
- return [xPath];
1555
+ const fullXPath = getElementXPath(node);
1556
+ return [fullXPath];
1495
1557
  }
1496
1558
  function getXpathsById(id) {
1497
1559
  const node = getNodeFromCacheList(id);
@@ -611,6 +611,9 @@ var midscene_element_inspector = (() => {
611
611
  function isButtonElement(node) {
612
612
  return node instanceof HTMLElement && node.tagName.toLowerCase() === "button";
613
613
  }
614
+ function isAElement(node) {
615
+ return node instanceof HTMLElement && node.tagName.toLowerCase() === "a";
616
+ }
614
617
  function isImgElement(node) {
615
618
  if (!includeBaseElement(node) && node instanceof Element) {
616
619
  const computedStyle = window.getComputedStyle(node);
@@ -661,7 +664,8 @@ var midscene_element_inspector = (() => {
661
664
  "textarea",
662
665
  "select",
663
666
  "option",
664
- "img"
667
+ "img",
668
+ "a"
665
669
  ];
666
670
  for (const tagName of includeList) {
667
671
  const element = node.querySelectorAll(tagName);
@@ -1045,10 +1049,20 @@ var midscene_element_inspector = (() => {
1045
1049
  return elementInfo;
1046
1050
  }
1047
1051
  if (isButtonElement(node)) {
1052
+ const rect2 = mergeElementAndChildrenRects(
1053
+ node,
1054
+ currentWindow,
1055
+ currentDocument,
1056
+ baseZoom,
1057
+ visibleOnly
1058
+ );
1059
+ if (!rect2) {
1060
+ return null;
1061
+ }
1048
1062
  const attributes = getNodeAttributes(node, currentWindow);
1049
1063
  const pseudo = getPseudoElementContent(node, currentWindow);
1050
1064
  const content = node.innerText || pseudo.before || pseudo.after || "";
1051
- const nodeHashId = midsceneGenerateHash(node, content, rect);
1065
+ const nodeHashId = midsceneGenerateHash(node, content, rect2);
1052
1066
  const selector = setDataForNode(node, nodeHashId, false, currentWindow);
1053
1067
  const elementInfo = {
1054
1068
  id: nodeHashId,
@@ -1061,12 +1075,12 @@ var midscene_element_inspector = (() => {
1061
1075
  nodeType: "BUTTON Node" /* BUTTON */
1062
1076
  }),
1063
1077
  content,
1064
- rect,
1078
+ rect: rect2,
1065
1079
  center: [
1066
- Math.round(rect.left + rect.width / 2),
1067
- Math.round(rect.top + rect.height / 2)
1080
+ Math.round(rect2.left + rect2.width / 2),
1081
+ Math.round(rect2.top + rect2.height / 2)
1068
1082
  ],
1069
- zoom: rect.zoom
1083
+ zoom: rect2.zoom
1070
1084
  };
1071
1085
  return elementInfo;
1072
1086
  }
@@ -1128,6 +1142,32 @@ var midscene_element_inspector = (() => {
1128
1142
  };
1129
1143
  return elementInfo;
1130
1144
  }
1145
+ if (isAElement(node)) {
1146
+ const attributes = getNodeAttributes(node, currentWindow);
1147
+ const pseudo = getPseudoElementContent(node, currentWindow);
1148
+ const content = node.innerText || pseudo.before || pseudo.after || "";
1149
+ const nodeHashId = midsceneGenerateHash(node, content, rect);
1150
+ const selector = setDataForNode(node, nodeHashId, false, currentWindow);
1151
+ const elementInfo = {
1152
+ id: nodeHashId,
1153
+ indexId: indexId++,
1154
+ nodeHashId,
1155
+ nodeType: "Anchor Node" /* A */,
1156
+ locator: selector,
1157
+ attributes: __spreadProps(__spreadValues({}, attributes), {
1158
+ htmlTagName: tagNameOfNode(node),
1159
+ nodeType: "Anchor Node" /* A */
1160
+ }),
1161
+ content,
1162
+ rect,
1163
+ center: [
1164
+ Math.round(rect.left + rect.width / 2),
1165
+ Math.round(rect.top + rect.height / 2)
1166
+ ],
1167
+ zoom: rect.zoom
1168
+ };
1169
+ return elementInfo;
1170
+ }
1131
1171
  if (isContainerElement(node)) {
1132
1172
  const attributes = getNodeAttributes(node, currentWindow);
1133
1173
  const nodeHashId = midsceneGenerateHash(node, "", rect);
@@ -1197,7 +1237,7 @@ var midscene_element_inspector = (() => {
1197
1237
  node: elementInfo,
1198
1238
  children: []
1199
1239
  };
1200
- if ((elementInfo == null ? void 0 : elementInfo.nodeType) === "BUTTON Node" /* BUTTON */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "IMG Node" /* IMG */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "TEXT Node" /* TEXT */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "FORM_ITEM Node" /* FORM_ITEM */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "CONTAINER Node" /* CONTAINER */) {
1240
+ if ((elementInfo == null ? void 0 : elementInfo.nodeType) === "BUTTON Node" /* BUTTON */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "IMG Node" /* IMG */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "TEXT Node" /* TEXT */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "Anchor Node" /* A */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "FORM_ITEM Node" /* FORM_ITEM */ || (elementInfo == null ? void 0 : elementInfo.nodeType) === "CONTAINER Node" /* CONTAINER */) {
1201
1241
  return nodeInfo;
1202
1242
  }
1203
1243
  const rect = getRect(node, baseZoom, currentWindow);
@@ -1252,6 +1292,49 @@ var midscene_element_inspector = (() => {
1252
1292
  children: topChildren
1253
1293
  };
1254
1294
  }
1295
+ function mergeElementAndChildrenRects(node, currentWindow, currentDocument, baseZoom = 1, visibleOnly = true) {
1296
+ const selfRect = elementRect(
1297
+ node,
1298
+ currentWindow,
1299
+ currentDocument,
1300
+ baseZoom,
1301
+ visibleOnly
1302
+ );
1303
+ if (!selfRect)
1304
+ return null;
1305
+ let minLeft = selfRect.left;
1306
+ let minTop = selfRect.top;
1307
+ let maxRight = selfRect.left + selfRect.width;
1308
+ let maxBottom = selfRect.top + selfRect.height;
1309
+ function traverse(child) {
1310
+ for (let i = 0; i < child.childNodes.length; i++) {
1311
+ const sub = child.childNodes[i];
1312
+ if (sub.nodeType === 1) {
1313
+ const rect = elementRect(
1314
+ sub,
1315
+ currentWindow,
1316
+ currentDocument,
1317
+ baseZoom,
1318
+ visibleOnly
1319
+ );
1320
+ if (rect) {
1321
+ minLeft = Math.min(minLeft, rect.left);
1322
+ minTop = Math.min(minTop, rect.top);
1323
+ maxRight = Math.max(maxRight, rect.left + rect.width);
1324
+ maxBottom = Math.max(maxBottom, rect.top + rect.height);
1325
+ }
1326
+ traverse(sub);
1327
+ }
1328
+ }
1329
+ }
1330
+ traverse(node);
1331
+ return __spreadProps(__spreadValues({}, selfRect), {
1332
+ left: minLeft,
1333
+ top: minTop,
1334
+ width: maxRight - minLeft,
1335
+ height: maxBottom - minTop
1336
+ });
1337
+ }
1255
1338
 
1256
1339
  // src/extractor/debug.ts
1257
1340
  console.log(extractTextWithPosition(document.body, true));
@@ -10,6 +10,5 @@ declare const getMidsceneRunBaseDir: () => string;
10
10
  * @returns The absolute path to the requested directory
11
11
  */
12
12
  declare const getMidsceneRunSubDir: (subdir: "dump" | "cache" | "report" | "tmp" | "log" | "output") => string;
13
- declare const logDir: string;
14
13
 
15
- export { defaultRunDirName, getMidsceneRunBaseDir, getMidsceneRunDir, getMidsceneRunSubDir, isNodeEnv, logDir };
14
+ export { defaultRunDirName, getMidsceneRunBaseDir, getMidsceneRunDir, getMidsceneRunSubDir, isNodeEnv };
@@ -6,6 +6,7 @@ declare enum NodeType {
6
6
  CONTAINER = "CONTAINER Node",
7
7
  FORM_ITEM = "FORM_ITEM Node",
8
8
  BUTTON = "BUTTON Node",
9
+ A = "Anchor Node",
9
10
  IMG = "IMG Node",
10
11
  TEXT = "TEXT Node",
11
12
  POSITION = "POSITION Node"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/shared",
3
- "version": "0.17.2",
3
+ "version": "0.17.3-beta-20250526042111.0",
4
4
  "repository": "https://github.com/web-infra-dev/midscene",
5
5
  "homepage": "https://midscenejs.com/",
6
6
  "types": "./dist/types/index.d.ts",
package/src/common.ts CHANGED
@@ -62,5 +62,3 @@ export const getMidsceneRunSubDir = (
62
62
 
63
63
  return logPath;
64
64
  };
65
-
66
- export const logDir = isNodeEnv ? getMidsceneRunSubDir('log') : '';
@@ -9,6 +9,7 @@ export enum NodeType {
9
9
  CONTAINER = 'CONTAINER Node',
10
10
  FORM_ITEM = 'FORM_ITEM Node',
11
11
  BUTTON = 'BUTTON Node',
12
+ A = 'Anchor Node',
12
13
  IMG = 'IMG Node',
13
14
  TEXT = 'TEXT Node',
14
15
  POSITION = 'POSITION Node',
@@ -17,6 +17,12 @@ export function isButtonElement(
17
17
  return node instanceof HTMLElement && node.tagName.toLowerCase() === 'button';
18
18
  }
19
19
 
20
+ export function isAElement(
21
+ node: globalThis.Node,
22
+ ): node is globalThis.HTMLButtonElement {
23
+ return node instanceof HTMLElement && node.tagName.toLowerCase() === 'a';
24
+ }
25
+
20
26
  export function isImgElement(
21
27
  node: globalThis.Node,
22
28
  ): node is globalThis.HTMLImageElement {
@@ -90,6 +96,7 @@ function includeBaseElement(node: globalThis.Node) {
90
96
  'select',
91
97
  'option',
92
98
  'img',
99
+ 'a',
93
100
  ];
94
101
 
95
102
  for (const tagName of includeList) {