@midscene/web 0.5.1 → 0.5.2-beta-20240929094445.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.
@@ -595,17 +595,60 @@ var midscene_element_inspector = (() => {
595
595
 
596
596
  // src/extractor/dom-util.ts
597
597
  function isFormElement(node) {
598
- return node instanceof HTMLElement && (node.tagName.toLowerCase() === "input" || node.tagName.toLowerCase() === "textarea" || node.tagName.toLowerCase() === "label" || node.tagName.toLowerCase() === "select" || node.tagName.toLowerCase() === "option");
598
+ return node instanceof HTMLElement && (node.tagName.toLowerCase() === "input" || node.tagName.toLowerCase() === "textarea" || node.tagName.toLowerCase() === "select" || node.tagName.toLowerCase() === "option");
599
599
  }
600
600
  function isButtonElement(node) {
601
601
  return node instanceof HTMLElement && node.tagName.toLowerCase() === "button";
602
602
  }
603
603
  function isImgElement(node) {
604
+ if (!includeBaseElement(node) && node instanceof Element) {
605
+ const computedStyle = window.getComputedStyle(node);
606
+ const backgroundImage = computedStyle.getPropertyValue("background-image");
607
+ if (backgroundImage !== "none") {
608
+ return true;
609
+ }
610
+ }
604
611
  return node instanceof HTMLElement && node.tagName.toLowerCase() === "img" || node instanceof SVGElement && node.tagName.toLowerCase() === "svg";
605
612
  }
606
613
  function isTextElement(node) {
607
614
  return node.nodeName.toLowerCase() === "#text";
608
615
  }
616
+ function isContainerElement(node) {
617
+ if (!(node instanceof HTMLElement))
618
+ return false;
619
+ if (includeBaseElement(node)) {
620
+ return false;
621
+ }
622
+ const computedStyle = window.getComputedStyle(node);
623
+ const backgroundColor = computedStyle.getPropertyValue("background-color");
624
+ if (backgroundColor) {
625
+ return true;
626
+ }
627
+ return false;
628
+ }
629
+ function includeBaseElement(node) {
630
+ if (!(node instanceof HTMLElement))
631
+ return false;
632
+ if (node.innerText) {
633
+ return true;
634
+ }
635
+ const includeList = [
636
+ "svg",
637
+ "button",
638
+ "input",
639
+ "textarea",
640
+ "select",
641
+ "option",
642
+ "img"
643
+ ];
644
+ for (const tagName of includeList) {
645
+ const element = node.querySelectorAll(tagName);
646
+ if (element.length > 0) {
647
+ return true;
648
+ }
649
+ }
650
+ return false;
651
+ }
609
652
 
610
653
  // src/extractor/util.ts
611
654
  var import_js_sha256 = __toESM(require_sha256());
@@ -633,9 +676,9 @@ var midscene_element_inspector = (() => {
633
676
  function selectorForValue(val) {
634
677
  return `[${taskIdKey}='${val}']`;
635
678
  }
636
- function setDataForNode(node, nodeHash) {
679
+ function setDataForNode(node, nodeHash, setToParentNode = false) {
637
680
  const taskId = taskIdKey;
638
- if (!(node instanceof HTMLElement)) {
681
+ if (!(node instanceof Element)) {
639
682
  return "";
640
683
  }
641
684
  if (!taskId) {
@@ -644,7 +687,13 @@ var midscene_element_inspector = (() => {
644
687
  }
645
688
  const selector = selectorForValue(nodeHash);
646
689
  if (getDebugMode()) {
647
- node.setAttribute(taskIdKey, nodeHash.toString());
690
+ if (setToParentNode) {
691
+ if (node.parentNode instanceof HTMLElement) {
692
+ node.parentNode.setAttribute(taskIdKey, nodeHash.toString());
693
+ }
694
+ } else {
695
+ node.setAttribute(taskIdKey, nodeHash.toString());
696
+ }
648
697
  }
649
698
  return selector;
650
699
  }
@@ -702,6 +751,28 @@ var midscene_element_inspector = (() => {
702
751
  zoom
703
752
  };
704
753
  }
754
+ var isElementCovered = (el, rect) => {
755
+ const x = rect.left + rect.width / 2;
756
+ const y = rect.top + rect.height / 2;
757
+ const topElement = document.elementFromPoint(x, y);
758
+ if (topElement === el) {
759
+ return false;
760
+ }
761
+ if (el == null ? void 0 : el.contains(topElement)) {
762
+ return false;
763
+ }
764
+ if (topElement == null ? void 0 : topElement.contains(el)) {
765
+ return false;
766
+ }
767
+ logger(el, "Element is covered by another element", {
768
+ topElement,
769
+ el,
770
+ rect,
771
+ x,
772
+ y
773
+ });
774
+ return true;
775
+ };
705
776
  function visibleRect(el, baseZoom = 1) {
706
777
  if (!el) {
707
778
  logger(el, "Element is not in the DOM hierarchy");
@@ -723,6 +794,9 @@ var midscene_element_inspector = (() => {
723
794
  logger(el, "Element has no size");
724
795
  return false;
725
796
  }
797
+ if (baseZoom === 1 && isElementCovered(el, rect)) {
798
+ return false;
799
+ }
726
800
  const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
727
801
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
728
802
  const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
@@ -815,12 +889,11 @@ var midscene_element_inspector = (() => {
815
889
  logger("Element is not visible", node);
816
890
  return null;
817
891
  }
818
- const debugMode2 = getDebugMode();
819
892
  if (isFormElement(node)) {
820
- const attributes2 = getNodeAttributes(node);
821
- const nodeHashId2 = midsceneGenerateHash(attributes2.placeholder, rect);
822
- const selector2 = setDataForNode(node, nodeHashId2);
823
- let valueContent = attributes2.value || attributes2.placeholder || node.textContent || "";
893
+ const attributes = getNodeAttributes(node);
894
+ const nodeHashId = midsceneGenerateHash(attributes.placeholder, rect);
895
+ const selector = setDataForNode(node, nodeHashId);
896
+ let valueContent = attributes.value || attributes.placeholder || node.textContent || "";
824
897
  const tagName = node.tagName.toLowerCase();
825
898
  if (node.tagName.toLowerCase() === "select") {
826
899
  const selectedOption = node.options[node.selectedIndex];
@@ -829,13 +902,13 @@ var midscene_element_inspector = (() => {
829
902
  if ((node.tagName.toLowerCase() === "input" || node.tagName.toLowerCase() === "textarea") && node.value) {
830
903
  valueContent = node.value;
831
904
  }
832
- const elementInfo2 = {
833
- id: nodeHashId2,
905
+ const elementInfo = {
906
+ id: nodeHashId,
834
907
  nodePath,
835
- nodeHashId: nodeHashId2,
836
- locator: selector2,
908
+ nodeHashId,
909
+ locator: selector,
837
910
  nodeType: NodeType.FORM_ITEM,
838
- attributes: __spreadProps(__spreadValues({}, attributes2), {
911
+ attributes: __spreadProps(__spreadValues({}, attributes), {
839
912
  htmlTagName: `<${tagName}>`,
840
913
  nodeType: NodeType.FORM_ITEM
841
914
  }),
@@ -845,24 +918,25 @@ var midscene_element_inspector = (() => {
845
918
  Math.round(rect.left + rect.width / 2),
846
919
  Math.round(rect.top + rect.height / 2)
847
920
  ],
848
- htmlNode: debugMode2 ? node : null,
849
- zoom: rect.zoom
921
+ zoom: rect.zoom,
922
+ screenWidth: window.innerWidth,
923
+ screenHeight: window.innerHeight
850
924
  };
851
- return elementInfo2;
925
+ return elementInfo;
852
926
  }
853
927
  if (isButtonElement(node)) {
854
- const attributes2 = getNodeAttributes(node);
928
+ const attributes = getNodeAttributes(node);
855
929
  const pseudo = getPseudoElementContent(node);
856
930
  const content = node.innerText || pseudo.before || pseudo.after || "";
857
- const nodeHashId2 = midsceneGenerateHash(content, rect);
858
- const selector2 = setDataForNode(node, nodeHashId2);
859
- const elementInfo2 = {
860
- id: nodeHashId2,
931
+ const nodeHashId = midsceneGenerateHash(content, rect);
932
+ const selector = setDataForNode(node, nodeHashId);
933
+ const elementInfo = {
934
+ id: nodeHashId,
861
935
  nodePath,
862
- nodeHashId: nodeHashId2,
936
+ nodeHashId,
863
937
  nodeType: NodeType.BUTTON,
864
- locator: selector2,
865
- attributes: __spreadProps(__spreadValues({}, attributes2), {
938
+ locator: selector,
939
+ attributes: __spreadProps(__spreadValues({}, attributes), {
866
940
  nodeType: NodeType.BUTTON
867
941
  }),
868
942
  content,
@@ -871,21 +945,22 @@ var midscene_element_inspector = (() => {
871
945
  Math.round(rect.left + rect.width / 2),
872
946
  Math.round(rect.top + rect.height / 2)
873
947
  ],
874
- htmlNode: debugMode2 ? node : null,
875
- zoom: rect.zoom
948
+ zoom: rect.zoom,
949
+ screenWidth: window.innerWidth,
950
+ screenHeight: window.innerHeight
876
951
  };
877
- return elementInfo2;
952
+ return elementInfo;
878
953
  }
879
954
  if (isImgElement(node)) {
880
- const attributes2 = getNodeAttributes(node);
881
- const nodeHashId2 = midsceneGenerateHash("", rect);
882
- const selector2 = setDataForNode(node, nodeHashId2);
883
- const elementInfo2 = {
884
- id: nodeHashId2,
955
+ const attributes = getNodeAttributes(node);
956
+ const nodeHashId = midsceneGenerateHash("", rect);
957
+ const selector = setDataForNode(node, nodeHashId);
958
+ const elementInfo = {
959
+ id: nodeHashId,
885
960
  nodePath,
886
- nodeHashId: nodeHashId2,
887
- locator: selector2,
888
- attributes: __spreadProps(__spreadValues(__spreadValues({}, attributes2), node.nodeName.toLowerCase() === "svg" ? {
961
+ nodeHashId,
962
+ locator: selector,
963
+ attributes: __spreadProps(__spreadValues(__spreadValues({}, attributes), node.nodeName.toLowerCase() === "svg" ? {
889
964
  svgContent: "true"
890
965
  } : {}), {
891
966
  nodeType: NodeType.IMG
@@ -897,30 +972,31 @@ var midscene_element_inspector = (() => {
897
972
  Math.round(rect.left + rect.width / 2),
898
973
  Math.round(rect.top + rect.height / 2)
899
974
  ],
900
- htmlNode: debugMode2 ? node : null,
901
- zoom: rect.zoom
975
+ zoom: rect.zoom,
976
+ screenWidth: window.innerWidth,
977
+ screenHeight: window.innerHeight
902
978
  };
903
- return elementInfo2;
979
+ return elementInfo;
904
980
  }
905
981
  if (isTextElement(node)) {
906
982
  const text = (_a = node.textContent) == null ? void 0 : _a.trim().replace(/\n+/g, " ");
907
983
  if (!text) {
908
984
  return null;
909
985
  }
910
- const attributes2 = getNodeAttributes(node);
911
- const attributeKeys = Object.keys(attributes2);
986
+ const attributes = getNodeAttributes(node);
987
+ const attributeKeys = Object.keys(attributes);
912
988
  if (!text.trim() && attributeKeys.length === 0) {
913
989
  return null;
914
990
  }
915
- const nodeHashId2 = midsceneGenerateHash(text, rect);
916
- const selector2 = setDataForNode(node, nodeHashId2);
917
- const elementInfo2 = {
918
- id: nodeHashId2,
991
+ const nodeHashId = midsceneGenerateHash(text, rect);
992
+ const selector = setDataForNode(node, nodeHashId, true);
993
+ const elementInfo = {
994
+ id: nodeHashId,
919
995
  nodePath,
920
- nodeHashId: nodeHashId2,
996
+ nodeHashId,
921
997
  nodeType: NodeType.TEXT,
922
- locator: selector2,
923
- attributes: __spreadProps(__spreadValues({}, attributes2), {
998
+ locator: selector,
999
+ attributes: __spreadProps(__spreadValues({}, attributes), {
924
1000
  nodeType: NodeType.TEXT
925
1001
  }),
926
1002
  center: [
@@ -930,33 +1006,38 @@ var midscene_element_inspector = (() => {
930
1006
  // attributes,
931
1007
  content: text,
932
1008
  rect,
933
- htmlNode: debugMode2 ? node : null,
934
- zoom: rect.zoom
1009
+ zoom: rect.zoom,
1010
+ screenWidth: window.innerWidth,
1011
+ screenHeight: window.innerHeight
935
1012
  };
936
- return elementInfo2;
1013
+ return elementInfo;
937
1014
  }
938
- const attributes = getNodeAttributes(node);
939
- const nodeHashId = midsceneGenerateHash("", rect);
940
- const selector = setDataForNode(node, nodeHashId);
941
- const elementInfo = {
942
- id: nodeHashId,
943
- nodePath,
944
- nodeHashId,
945
- nodeType: NodeType.CONTAINER,
946
- locator: selector,
947
- attributes: __spreadProps(__spreadValues({}, attributes), {
948
- nodeType: NodeType.CONTAINER
949
- }),
950
- content: "",
951
- rect,
952
- center: [
953
- Math.round(rect.left + rect.width / 2),
954
- Math.round(rect.top + rect.height / 2)
955
- ],
956
- htmlNode: debugMode2 ? node : null,
957
- zoom: rect.zoom
958
- };
959
- return elementInfo;
1015
+ if (isContainerElement(node)) {
1016
+ const attributes = getNodeAttributes(node);
1017
+ const nodeHashId = midsceneGenerateHash("", rect);
1018
+ const selector = setDataForNode(node, nodeHashId);
1019
+ const elementInfo = {
1020
+ id: nodeHashId,
1021
+ nodePath,
1022
+ nodeHashId,
1023
+ nodeType: NodeType.CONTAINER,
1024
+ locator: selector,
1025
+ attributes: __spreadProps(__spreadValues({}, attributes), {
1026
+ nodeType: NodeType.CONTAINER
1027
+ }),
1028
+ content: "",
1029
+ rect,
1030
+ center: [
1031
+ Math.round(rect.left + rect.width / 2),
1032
+ Math.round(rect.top + rect.height / 2)
1033
+ ],
1034
+ zoom: rect.zoom,
1035
+ screenWidth: window.innerWidth,
1036
+ screenHeight: window.innerHeight
1037
+ };
1038
+ return elementInfo;
1039
+ }
1040
+ return null;
960
1041
  }
961
1042
  function extractTextWithPosition(initNode, debugMode2 = false, currentFrame = { id: 0, left: 0, top: 0 }) {
962
1043
  setDebugMode(debugMode2);
@@ -967,34 +1048,14 @@ var midscene_element_inspector = (() => {
967
1048
  return null;
968
1049
  }
969
1050
  const elementInfo = collectElementInfo(node, nodePath, baseZoom);
970
- if ((elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.BUTTON || (elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.IMG) {
1051
+ if ((elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.BUTTON || (elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.IMG || (elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.TEXT || (elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.FORM_ITEM || (elementInfo == null ? void 0 : elementInfo.nodeType) === NodeType.CONTAINER) {
1052
+ elementInfoArray.push(elementInfo);
971
1053
  return elementInfo;
972
1054
  }
973
- let hasNonContainerChildren = false;
974
- const childrenPureContainers = [];
1055
+ const rect = getRect(node, baseZoom);
975
1056
  for (let i = 0; i < node.childNodes.length; i++) {
976
1057
  logger("will dfs", node.childNodes[i]);
977
- const resultLengthBeforeDfs = elementInfoArray.length;
978
- const result = dfs(
979
- node.childNodes[i],
980
- `${nodePath}-${i}`,
981
- elementInfo == null ? void 0 : elementInfo.zoom
982
- );
983
- if (!result)
984
- continue;
985
- if ((result == null ? void 0 : result.nodeType) === NodeType.CONTAINER && elementInfoArray.length > resultLengthBeforeDfs) {
986
- hasNonContainerChildren = true;
987
- continue;
988
- }
989
- if ((result == null ? void 0 : result.nodeType) === NodeType.CONTAINER) {
990
- childrenPureContainers.push(result);
991
- } else {
992
- hasNonContainerChildren = true;
993
- elementInfoArray.push(result);
994
- }
995
- }
996
- if (hasNonContainerChildren) {
997
- elementInfoArray.push(...childrenPureContainers);
1058
+ dfs(node.childNodes[i], `${nodePath}-${i}`, rect.zoom);
998
1059
  }
999
1060
  if (!elementInfo) {
1000
1061
  logger("should NOT continue for node", node);
@@ -1002,10 +1063,7 @@ var midscene_element_inspector = (() => {
1002
1063
  }
1003
1064
  return elementInfo;
1004
1065
  }
1005
- const outerMostElementInfo = dfs(initNode || getDocument(), "0");
1006
- if (outerMostElementInfo && !elementInfoArray.length) {
1007
- elementInfoArray.push(outerMostElementInfo);
1008
- }
1066
+ dfs(initNode || getDocument(), "0");
1009
1067
  for (let i = 0; i < elementInfoArray.length; i++) {
1010
1068
  elementInfoArray[i].indexId = (i + 1).toString();
1011
1069
  }
@@ -1169,7 +1227,6 @@ var midscene_element_inspector = (() => {
1169
1227
  Math.round(rect.top + rect.height / 2)
1170
1228
  ],
1171
1229
  nodeType,
1172
- htmlNode: null,
1173
1230
  nodePath: ""
1174
1231
  };
1175
1232
  if (elementInfo.nodeType !== NodeType.CONTAINER) {