export declare const jsCode = "\n(\n args = {\n doHighlightElements: true,\n focusHighlightIndex: -1,\n viewportExpansion: 0,\n }\n) => {\n const { doHighlightElements, focusHighlightIndex, viewportExpansion } = args;\n let highlightIndex = 0; // Reset highlight index\n\n /**\n * Hash map of DOM nodes indexed by their highlight index.\n *\n * @type {Object}\n */\n const DOM_HASH_MAP = {};\n\n const ID = { current: 0 };\n\n // Quick check to confirm the script receives focusHighlightIndex\n console.log(\"focusHighlightIndex:\", focusHighlightIndex);\n\n const HIGHLIGHT_CONTAINER_ID = \"playwright-highlight-container\";\n\n /**\n * Highlights an element in the DOM and returns the index of the next element.\n */\n function highlightElement(element, index, parentIframe = null) {\n // Create or get highlight container\n let container = document.getElementById(HIGHLIGHT_CONTAINER_ID);\n if (!container) {\n container = document.createElement(\"div\");\n container.id = HIGHLIGHT_CONTAINER_ID;\n container.style.position = \"absolute\";\n container.style.pointerEvents = \"none\";\n container.style.top = \"0\";\n container.style.left = \"0\";\n container.style.width = \"100%\";\n container.style.height = \"100%\";\n container.style.zIndex = \"2147483647\"; // Maximum z-index value\n\n document.body.appendChild(container);\n }\n\n // Generate a color based on the index\n const colors = [\n \"#FF0000\",\n \"#00FF00\",\n \"#0000FF\",\n \"#FFA500\",\n \"#800080\",\n \"#008080\",\n \"#FF69B4\",\n \"#4B0082\",\n \"#FF4500\",\n \"#2E8B57\",\n \"#DC143C\",\n \"#4682B4\",\n ];\n const colorIndex = index % colors.length;\n const baseColor = colors[colorIndex];\n const backgroundColor = `${baseColor}1A`; // 10% opacity version of the color\n\n // Create highlight overlay\n const overlay = document.createElement(\"div\");\n overlay.style.position = \"absolute\";\n overlay.style.border = `2px solid ${baseColor}`;\n overlay.style.backgroundColor = backgroundColor;\n overlay.style.pointerEvents = \"none\";\n overlay.style.boxSizing = \"border-box\";\n\n // Position overlay based on element, including scroll position\n const rect = element.getBoundingClientRect();\n let top = rect.top + window.scrollY;\n let left = rect.left + window.scrollX;\n\n // Adjust position if element is inside an iframe\n if (parentIframe) {\n const iframeRect = parentIframe.getBoundingClientRect();\n top += iframeRect.top;\n left += iframeRect.left;\n }\n\n overlay.style.top = `${top}px`;\n overlay.style.left = `${left}px`;\n overlay.style.width = `${rect.width}px`;\n overlay.style.height = `${rect.height}px`;\n\n // Create label\n const label = document.createElement(\"div\");\n label.className = \"playwright-highlight-label\";\n label.style.position = \"absolute\";\n label.style.background = baseColor;\n label.style.color = \"white\";\n label.style.padding = \"1px 4px\";\n label.style.borderRadius = \"4px\";\n label.style.fontSize = `${Math.min(12, Math.max(8, rect.height / 2))}px`; // Responsive font size\n label.textContent = index;\n\n // Calculate label position\n const labelWidth = 20; // Approximate width\n const labelHeight = 16; // Approximate height\n\n // Default position (top-right corner inside the box)\n let labelTop = top + 2;\n let labelLeft = left + rect.width - labelWidth - 2;\n\n // Adjust if box is too small\n if (rect.width < labelWidth + 4 || rect.height < labelHeight + 4) {\n // Position outside the box if it's too small\n labelTop = top - labelHeight - 2;\n labelLeft = left + rect.width - labelWidth;\n }\n\n label.style.top = `${labelTop}px`;\n label.style.left = `${labelLeft}px`;\n\n // Add to container\n container.appendChild(overlay);\n container.appendChild(label);\n\n // Store reference for cleanup\n element.setAttribute(\n \"browser-user-highlight-id\",\n `playwright-highlight-${index}`\n );\n\n return index + 1;\n }\n\n /**\n * Returns an XPath tree string for an element.\n */\n function getXPathTree(element, stopAtBoundary = true) {\n const segments = [];\n let currentElement = element;\n\n while (currentElement && currentElement.nodeType === Node.ELEMENT_NODE) {\n // Stop if we hit a shadow root or iframe\n if (\n stopAtBoundary &&\n (currentElement.parentNode instanceof ShadowRoot ||\n currentElement.parentNode instanceof HTMLIFrameElement)\n ) {\n break;\n }\n\n let index = 0;\n let sibling = currentElement.previousSibling;\n while (sibling) {\n if (\n sibling.nodeType === Node.ELEMENT_NODE &&\n sibling.nodeName === currentElement.nodeName\n ) {\n index++;\n }\n sibling = sibling.previousSibling;\n }\n\n const tagName = currentElement.nodeName.toLowerCase();\n const xpathIndex = index > 0 ? `[${index + 1}]` : \"\";\n segments.unshift(`${tagName}${xpathIndex}`);\n\n currentElement = currentElement.parentNode;\n }\n\n return segments.join(\"/\");\n }\n\n // Helper function to check if element is accepted\n function isElementAccepted(element) {\n const leafElementDenyList = new Set([\n \"svg\",\n \"script\",\n \"style\",\n \"link\",\n \"meta\",\n ]);\n return !leafElementDenyList.has(element.tagName.toLowerCase());\n }\n\n /**\n * Checks if an element is interactive.\n */\n function isInteractiveElement(element) {\n // Immediately return false for body tag\n if (element.tagName.toLowerCase() === \"body\") {\n return false;\n }\n\n // Base interactive elements and roles\n const interactiveElements = new Set([\n \"a\",\n \"button\",\n \"details\",\n \"embed\",\n \"input\",\n \"label\",\n \"menu\",\n \"menuitem\",\n \"object\",\n \"select\",\n \"textarea\",\n \"summary\",\n ]);\n\n const interactiveRoles = new Set([\n \"button\",\n \"menu\",\n \"menuitem\",\n \"link\",\n \"checkbox\",\n \"radio\",\n \"slider\",\n \"tab\",\n \"tabpanel\",\n \"textbox\",\n \"combobox\",\n \"grid\",\n \"listbox\",\n \"option\",\n \"progressbar\",\n \"scrollbar\",\n \"searchbox\",\n \"switch\",\n \"tree\",\n \"treeitem\",\n \"spinbutton\",\n \"tooltip\",\n \"a-button-inner\",\n \"a-dropdown-button\",\n \"click\",\n \"menuitemcheckbox\",\n \"menuitemradio\",\n \"a-button-text\",\n \"button-text\",\n \"button-icon\",\n \"button-icon-only\",\n \"button-text-icon-only\",\n \"dropdown\",\n \"combobox\",\n ]);\n\n const tagName = element.tagName.toLowerCase();\n const role = element.getAttribute(\"role\");\n const ariaRole = element.getAttribute(\"aria-role\");\n const tabIndex = element.getAttribute(\"tabindex\");\n\n // Add check for specific class\n const hasAddressInputClass = element.classList.contains(\n \"address-input__container__input\"\n );\n\n // Basic role/attribute checks\n const hasInteractiveRole =\n hasAddressInputClass ||\n interactiveElements.has(tagName) ||\n interactiveRoles.has(role) ||\n interactiveRoles.has(ariaRole) ||\n (tabIndex !== null &&\n tabIndex !== \"-1\" &&\n element.parentElement?.tagName.toLowerCase() !== \"body\") ||\n element.getAttribute(\"data-action\") === \"a-dropdown-select\" ||\n element.getAttribute(\"data-action\") === \"a-dropdown-button\";\n\n if (hasInteractiveRole) return true;\n\n // Get computed style\n const style = window.getComputedStyle(element);\n\n // Check if element has click-like styling\n // const hasClickStyling = style.cursor === 'pointer' ||\n // element.style.cursor === 'pointer' ||\n // style.pointerEvents !== 'none';\n\n // Check for event listeners\n const hasClickHandler =\n element.onclick !== null ||\n element.getAttribute(\"onclick\") !== null ||\n element.hasAttribute(\"ng-click\") ||\n element.hasAttribute(\"@click\") ||\n element.hasAttribute(\"v-on:click\");\n\n // Helper function to safely get event listeners\n function getEventListeners(el) {\n try {\n // Try to get listeners using Chrome DevTools API\n return window.getEventListeners?.(el) || {};\n } catch (e) {\n // Fallback: check for common event properties\n const listeners = {};\n\n // List of common event types to check\n const eventTypes = [\n \"click\",\n \"mousedown\",\n \"mouseup\",\n \"touchstart\",\n \"touchend\",\n \"keydown\",\n \"keyup\",\n \"focus\",\n \"blur\",\n ];\n\n for (const type of eventTypes) {\n const handler = el[`on${type}`];\n if (handler) {\n listeners[type] = [\n {\n listener: handler,\n useCapture: false,\n },\n ];\n }\n }\n\n return listeners;\n }\n }\n\n // Check for click-related events on the element itself\n const listeners = getEventListeners(element);\n const hasClickListeners =\n listeners &&\n (listeners.click?.length > 0 ||\n listeners.mousedown?.length > 0 ||\n listeners.mouseup?.length > 0 ||\n listeners.touchstart?.length > 0 ||\n listeners.touchend?.length > 0);\n\n // Check for ARIA properties that suggest interactivity\n const hasAriaProps =\n element.hasAttribute(\"aria-expanded\") ||\n element.hasAttribute(\"aria-pressed\") ||\n element.hasAttribute(\"aria-selected\") ||\n element.hasAttribute(\"aria-checked\");\n\n // Check for form-related functionality\n const isFormRelated =\n element.form !== undefined ||\n element.hasAttribute(\"contenteditable\") ||\n style.userSelect !== \"none\";\n\n // Check if element is draggable\n const isDraggable =\n element.draggable || element.getAttribute(\"draggable\") === \"true\";\n\n // Additional check to prevent body from being marked as interactive\n if (\n element.tagName.toLowerCase() === \"body\" ||\n element.parentElement?.tagName.toLowerCase() === \"body\"\n ) {\n return false;\n }\n\n return (\n hasAriaProps ||\n // hasClickStyling ||\n hasClickHandler ||\n hasClickListeners ||\n // isFormRelated ||\n isDraggable\n );\n }\n\n /**\n * Checks if an element is visible.\n */\n function isElementVisible(element) {\n const style = window.getComputedStyle(element);\n return (\n element.offsetWidth > 0 &&\n element.offsetHeight > 0 &&\n style.visibility !== \"hidden\" &&\n style.display !== \"none\"\n );\n }\n\n /**\n * Checks if an element is the top element at its position.\n */\n function isTopElement(element) {\n // Find the correct document context and root element\n let doc = element.ownerDocument;\n\n // If we're in an iframe, elements are considered top by default\n if (doc !== window.document) {\n return true;\n }\n\n // For shadow DOM, we need to check within its own root context\n const shadowRoot = element.getRootNode();\n if (shadowRoot instanceof ShadowRoot) {\n const rect = element.getBoundingClientRect();\n const point = {\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n };\n\n try {\n // Use shadow root's elementFromPoint to check within shadow DOM context\n const topEl = shadowRoot.elementFromPoint(point.x, point.y);\n if (!topEl) return false;\n\n // Check if the element or any of its parents match our target element\n let current = topEl;\n while (current && current !== shadowRoot) {\n if (current === element) return true;\n current = current.parentElement;\n }\n return false;\n } catch (e) {\n return true; // If we can't determine, consider it visible\n }\n }\n\n // Regular DOM elements\n const rect = element.getBoundingClientRect();\n\n // If viewportExpansion is -1, check if element is the top one at its position\n if (viewportExpansion === -1) {\n return true; // Consider all elements as top elements when expansion is -1\n }\n\n // Calculate expanded viewport boundaries including scroll position\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n const viewportTop = -viewportExpansion + scrollY;\n const viewportLeft = -viewportExpansion + scrollX;\n const viewportBottom = window.innerHeight + viewportExpansion + scrollY;\n const viewportRight = window.innerWidth + viewportExpansion + scrollX;\n\n // Get absolute element position\n const absTop = rect.top + scrollY;\n const absLeft = rect.left + scrollX;\n const absBottom = rect.bottom + scrollY;\n const absRight = rect.right + scrollX;\n\n // Skip if element is completely outside expanded viewport\n if (\n absBottom < viewportTop ||\n absTop > viewportBottom ||\n absRight < viewportLeft ||\n absLeft > viewportRight\n ) {\n return false;\n }\n\n // For elements within expanded viewport, check if they're the top element\n try {\n const centerX = rect.left + rect.width / 2;\n const centerY = rect.top + rect.height / 2;\n\n // Only clamp the point if it's outside the actual document\n const point = {\n x: centerX,\n y: centerY,\n };\n\n if (\n point.x < 0 ||\n point.x >= window.innerWidth ||\n point.y < 0 ||\n point.y >= window.innerHeight\n ) {\n return true; // Consider elements with center outside viewport as visible\n }\n\n const topEl = document.elementFromPoint(point.x, point.y);\n if (!topEl) return false;\n\n let current = topEl;\n while (current && current !== document.documentElement) {\n if (current === element) return true;\n current = current.parentElement;\n }\n return false;\n } catch (e) {\n return true;\n }\n }\n\n /**\n * Checks if a text node is visible.\n */\n function isTextNodeVisible(textNode) {\n const range = document.createRange();\n range.selectNodeContents(textNode);\n const rect = range.getBoundingClientRect();\n\n return (\n rect.width !== 0 &&\n rect.height !== 0 &&\n rect.top >= 0 &&\n rect.top <= window.innerHeight &&\n textNode.parentElement?.checkVisibility({\n checkOpacity: true,\n checkVisibilityCSS: true,\n })\n );\n }\n\n /**\n * Creates a node data object for a given node and its descendants and returns\n * the identifier of the node in the hash map or null if the node is not accepted.\n */\n function buildDomTree(node, parentIframe = null) {\n if (!node) {\n return null;\n }\n\n // NOTE: We skip highlight container nodes from the DOM tree\n // by ignoring the container element itself and all its children.\n if (node.id === HIGHLIGHT_CONTAINER_ID) {\n return null;\n }\n\n // Special case for text nodes\n if (node.nodeType === Node.TEXT_NODE) {\n const textContent = node.textContent.trim();\n if (textContent && isTextNodeVisible(node)) {\n const id = `${ID.current++}`;\n\n DOM_HASH_MAP[id] = {\n type: \"TEXT_NODE\",\n text: textContent,\n isVisible: true,\n };\n\n return id;\n }\n return null;\n }\n\n // Check if element is accepted\n if (node.nodeType === Node.ELEMENT_NODE && !isElementAccepted(node)) {\n return null;\n }\n\n const nodeData = {\n tagName: node.tagName ? node.tagName.toLowerCase() : null,\n attributes: {},\n xpath:\n node.nodeType === Node.ELEMENT_NODE ? getXPathTree(node, true) : null,\n children: [],\n };\n\n // Add coordinates for element nodes\n if (node.nodeType === Node.ELEMENT_NODE) {\n const rect = node.getBoundingClientRect();\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n // Viewport-relative coordinates (can be negative when scrolled)\n nodeData.viewportCoordinates = {\n topLeft: {\n x: Math.round(rect.left),\n y: Math.round(rect.top),\n },\n topRight: {\n x: Math.round(rect.right),\n y: Math.round(rect.top),\n },\n bottomLeft: {\n x: Math.round(rect.left),\n y: Math.round(rect.bottom),\n },\n bottomRight: {\n x: Math.round(rect.right),\n y: Math.round(rect.bottom),\n },\n center: {\n x: Math.round(rect.left + rect.width / 2),\n y: Math.round(rect.top + rect.height / 2),\n },\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n };\n\n // Page-relative coordinates (always positive, relative to page origin)\n nodeData.pageCoordinates = {\n topLeft: {\n x: Math.round(rect.left + scrollX),\n y: Math.round(rect.top + scrollY),\n },\n topRight: {\n x: Math.round(rect.right + scrollX),\n y: Math.round(rect.top + scrollY),\n },\n bottomLeft: {\n x: Math.round(rect.left + scrollX),\n y: Math.round(rect.bottom + scrollY),\n },\n bottomRight: {\n x: Math.round(rect.right + scrollX),\n y: Math.round(rect.bottom + scrollY),\n },\n center: {\n x: Math.round(rect.left + rect.width / 2 + scrollX),\n y: Math.round(rect.top + rect.height / 2 + scrollY),\n },\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n };\n\n // Add viewport and scroll information\n nodeData.viewport = {\n scrollX: Math.round(scrollX),\n scrollY: Math.round(scrollY),\n width: window.innerWidth,\n height: window.innerHeight,\n };\n }\n\n // Copy all attributes if the node is an element\n if (node.nodeType === Node.ELEMENT_NODE && node.attributes) {\n // Use getAttributeNames() instead of directly iterating attributes\n const attributeNames = node.getAttributeNames?.() || [];\n for (const name of attributeNames) {\n nodeData.attributes[name] = node.getAttribute(name);\n }\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n const isInteractive = isInteractiveElement(node);\n const isVisible = isElementVisible(node);\n const isTop = isTopElement(node);\n\n nodeData.isInteractive = isInteractive;\n nodeData.isVisible = isVisible;\n nodeData.isTopElement = isTop;\n\n // Highlight if element meets all criteria and highlighting is enabled\n if (isInteractive && isVisible && isTop) {\n nodeData.highlightIndex = highlightIndex++;\n if (doHighlightElements) {\n if (focusHighlightIndex >= 0) {\n if (focusHighlightIndex === nodeData.highlightIndex) {\n highlightElement(node, nodeData.highlightIndex, parentIframe);\n }\n } else {\n highlightElement(node, nodeData.highlightIndex, parentIframe);\n }\n }\n }\n }\n\n // Only add iframeContext if we're inside an iframe\n // if (parentIframe) {\n // nodeData.iframeContext = `iframe[src=\"${parentIframe.src || ''}\"]`;\n // }\n\n // Only add shadowRoot field if it exists\n if (node.shadowRoot) {\n nodeData.shadowRoot = true;\n }\n\n // Handle shadow DOM\n if (node.shadowRoot) {\n for (const child of node.shadowRoot.childNodes) {\n const domElement = buildDomTree(child, parentIframe);\n if (domElement) {\n nodeData.children.push(domElement);\n }\n }\n }\n\n // Handle iframes\n if (node.tagName === \"IFRAME\") {\n try {\n const iframeDoc = node.contentDocument || node.contentWindow.document;\n if (iframeDoc) {\n for (const child of iframeDoc.body.childNodes) {\n const domElement = buildDomTree(child, node);\n if (domElement) {\n nodeData.children.push(domElement);\n }\n }\n }\n } catch (e) {\n console.warn(\"Unable to access iframe:\", node);\n }\n } else {\n for (const child of node.childNodes) {\n const domElement = buildDomTree(child, parentIframe);\n if (domElement) {\n nodeData.children.push(domElement);\n }\n }\n // If it's an element and has no visible content, return null\n if (nodeData.tagName === 'a' && nodeData.children.length === 0) {\n return null;\n }\n }\n\n // NOTE: We register the node to the hash map.\n const id = `${ID.current++}`;\n DOM_HASH_MAP[id] = nodeData;\n\n return id;\n }\n\n const rootId = buildDomTree(document.body);\n\n return { rootId, map: DOM_HASH_MAP };\n};\n";