(() => { const GLOBAL_KEY = "__spaceBrowserPageContent__"; const DOM_HELPER_KEY = "__spaceBrowserDomHelper__"; const VERSION = "12"; const REQUIRED_API_NAMES = Object.freeze([ "annotate", "boundingBoxFor", "capture", "click", "detail", "fileInputElementFor", "fileInputFor", "pointFor", "scroll", "select", "setChecked", "submit", "type", "typeSubmit" ]); function patchOpenShadowDom() { const original = Element.prototype.attachShadow; if (!original || original.__a0BrowserOpenShadowPatch) { return; } const patched = function attachShadow(options) { return original.call(this, { ...(options || {}), mode: "open" }); }; patched.__a0BrowserOpenShadowPatch = true; Element.prototype.attachShadow = patched; } patchOpenShadowDom(); const BLOCK_TAGS = new Set([ "ADDRESS", "ARTICLE", "ASIDE", "BLOCKQUOTE", "BODY", "DETAILS", "DIV", "DL", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HR", "HTML", "LI", "MAIN", "NAV", "OL", "P", "PRE", "SECTION", "TABLE", "TBODY", "TD", "TFOOT", "TH", "THEAD", "TR", "UL" ]); const SKIP_TAGS = new Set([ "HEAD", "LINK", "META", "NOSCRIPT", "SCRIPT", "STYLE", "TEMPLATE" ]); const INTERACTIVE_ROLES = new Set([ "button", "checkbox", "combobox", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "searchbox", "slider", "spinbutton", "switch", "tab", "textbox" ]); const INTERACTIVE_EVENT_NAMES = new Set([ "auxclick", "change", "click", "contextmenu", "dblclick", "input", "keydown", "keypress", "keyup", "mousedown", "mouseup", "pointerdown", "pointerup", "submit", "touchend", "touchstart" ]); const INTERACTIVE_EVENT_PROPERTIES = [...INTERACTIVE_EVENT_NAMES] .map((eventName) => `on${eventName}`); if (globalThis[GLOBAL_KEY]?.version === VERSION) { return; } const state = { backend: "live", captureId: 0, capturedAt: 0, captureOptions: { includeLabelQuotes: false, includeLinkUrls: false, includeSemanticTags: true, includeStateTags: true, includeListIndentation: true, includeListMarkers: false }, entries: new Map() }; function isElementNode(value) { return Boolean(value && value.nodeType === 1); } function isTextNode(value) { return Boolean(value && value.nodeType === 3); } function normalizeText(value) { return String(value ?? "") .replace(/\s+/gu, " ") .trim(); } function looksLikeSerializedHtmlText(value) { const normalizedValue = normalizeText(value); if (!normalizedValue || !normalizedValue.includes("<") || !normalizedValue.includes(">")) { return false; } if (//iu.test(normalizedValue)) { return true; } const tagMatches = normalizedValue.match(/<\/?[a-z][^>]*>/giu) || []; return tagMatches.length >= 3 && normalizedValue.length >= 80; } function looksLikeBrowserHelperMarkupText(value) { const normalizedValue = normalizeText(value); if (!normalizedValue) { return false; } return /space-browser-(?:frame-document|shadow-root)/iu.test(normalizedValue) || /data-space-browser-(?:frame|node|status|frame-url|frame-title|frame-src)/iu.test(normalizedValue); } function looksLikeMinifiedScriptText(value) { const normalizedValue = normalizeText(value); if (!normalizedValue || normalizedValue.length < 400) { return false; } const jsSignals = [ /\bfunction\b/u, /\breturn\b/u, /\bvar\b/u, /\bnew\b/u, /\bcase\b/u, /\bswitch\b/u, /\bwhile\b/u, /\bfor\b/u, /\b(?:localStorage|postMessage|document\.|window\.|parent\.)/u, /\bthis\./u, /(?:&&|\|\||>>>|!==|===)/u ].reduce((count, pattern) => count + (pattern.test(normalizedValue) ? 1 : 0), 0); if (jsSignals < 4) { return false; } const punctuationCount = (normalizedValue.match(/[{}[\]();=<>\\]/gu) || []).length; return punctuationCount / normalizedValue.length >= 0.12; } function shouldDropReadableText(value) { const normalizedValue = normalizeText(value); if (!normalizedValue) { return true; } return looksLikeBrowserHelperMarkupText(normalizedValue) || looksLikeSerializedHtmlText(normalizedValue) || looksLikeMinifiedScriptText(normalizedValue); } function normalizeAttributeText(value) { return normalizeText(value).slice(0, 160); } function escapeMarkdownText(value) { return String(value ?? "").replace(/([\\`*_{}\[\]()#+\-!|>])/gu, "\\$1"); } function quoteText(value) { return JSON.stringify(String(value ?? "")); } function truncateText(value, maxLength = 120) { const normalizedValue = normalizeText(value); if (normalizedValue.length <= maxLength) { return normalizedValue; } return `${normalizedValue.slice(0, Math.max(0, maxLength - 1)).trimEnd()}...`; } function delayMs(timeoutMs) { return new Promise((resolve) => { globalThis.setTimeout(resolve, Math.max(0, Number(timeoutMs) || 0)); }); } function parseCssColor(value) { const normalizedValue = normalizeText(value); if (!normalizedValue || normalizedValue === "transparent") { return null; } const rgbMatch = normalizedValue.match(/^rgba?\(([^)]+)\)$/iu); if (rgbMatch) { const parts = rgbMatch[1] .split(",") .map((part) => Number.parseFloat(String(part || "").trim())) .filter((part) => Number.isFinite(part)); if (parts.length >= 3) { return { r: Math.max(0, Math.min(255, parts[0])), g: Math.max(0, Math.min(255, parts[1])), b: Math.max(0, Math.min(255, parts[2])), a: parts.length >= 4 ? Math.max(0, Math.min(1, parts[3])) : 1 }; } } const hexMatch = normalizedValue.match(/^#([\da-f]{3,8})$/iu); if (!hexMatch) { return null; } const hex = hexMatch[1]; if (hex.length === 3 || hex.length === 4) { const [r, g, b, a = "f"] = hex.split(""); return { r: Number.parseInt(`${r}${r}`, 16), g: Number.parseInt(`${g}${g}`, 16), b: Number.parseInt(`${b}${b}`, 16), a: Number.parseInt(`${a}${a}`, 16) / 255 }; } if (hex.length === 6 || hex.length === 8) { return { r: Number.parseInt(hex.slice(0, 2), 16), g: Number.parseInt(hex.slice(2, 4), 16), b: Number.parseInt(hex.slice(4, 6), 16), a: hex.length === 8 ? Number.parseInt(hex.slice(6, 8), 16) / 255 : 1 }; } return null; } function rgbToHsl(color) { if (!color) { return null; } const r = color.r / 255; const g = color.g / 255; const b = color.b / 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); const delta = max - min; const lightness = (max + min) / 2; let hue = 0; let saturation = 0; if (delta > 0) { saturation = delta / (1 - Math.abs(2 * lightness - 1)); if (max === r) { hue = 60 * (((g - b) / delta) % 6); } else if (max === g) { hue = 60 * (((b - r) / delta) + 2); } else { hue = 60 * (((r - g) / delta) + 4); } } if (hue < 0) { hue += 360; } return { hue, lightness, saturation }; } function isTrustedHtmlRequirementError(error) { return /TrustedHTML/iu.test(String(error?.message || error || "")); } function joinBlocks(blocks) { return blocks .map((block) => String(block || "").trim()) .filter(Boolean) .join("\n\n") .trim(); } function cleanReadableMarkdown(value) { const lines = String(value || "") .replace(/[\s\S]*?<\/style\\?>/giu, "") .replace(/[\s\S]*?<\/script\\?>/giu, "") .replace(//giu, "") .split("\n"); const filteredLines = []; let insideCodeFence = false; lines.forEach((line) => { const trimmedLine = String(line || "").trim(); if (trimmedLine.startsWith("```")) { insideCodeFence = !insideCodeFence; filteredLines.push(line); return; } if (!trimmedLine || insideCodeFence) { filteredLines.push(line); return; } if (shouldDropReadableText(trimmedLine)) { return; } filteredLines.push(line); }); return filteredLines .join("\n") .replace(/\n{3,}/gu, "\n\n") .trim(); } function joinInlineParts(parts) { return String(parts .map((part) => String(part || "").trim()) .filter(Boolean) .join(" ")) .replace(/\s+([,.;!?])/gu, "$1") .replace(/([([{\u201c])\s+/gu, "$1") .replace(/\s+([\])}\u201d])/gu, "$1") .replace(/\s*\n\s*/gu, "\n") .replace(/[ \t]+\n/gu, "\n") .replace(/\n{3,}/gu, "\n\n") .trim(); } function indentBlock(text, level = 1) { const prefix = " ".repeat(Math.max(0, level)); return String(text || "") .split("\n") .map((line) => `${prefix}${line}`) .join("\n"); } function createNamedError(name, message, details = {}) { const error = new Error(message); error.name = name; Object.assign(error, details); return error; } function coerceSelectorList(payload) { if (typeof payload === "string") { return [payload]; } if (Array.isArray(payload?.selectors)) { return payload.selectors; } if (typeof payload?.selectors === "string") { return [payload.selectors]; } if (Array.isArray(payload?.selector)) { return payload.selector; } if (typeof payload?.selector === "string") { return [payload.selector]; } if (Array.isArray(payload)) { return payload; } return []; } function normalizeSelectorList(payload) { return coerceSelectorList(payload) .map((selector) => String(selector || "").trim()) .filter(Boolean); } function normalizeIncludeLinkUrls(payload) { return payload?.includeLinkUrls === true; } function normalizeIncludeLabelQuotes(payload) { return payload?.includeLabelQuotes === true; } function normalizeIncludeListIndentation(payload) { return payload?.includeListIndentation !== false; } function normalizeIncludeListMarkers(payload) { return payload?.includeListMarkers === true; } function normalizeIncludeStateTags(payload) { return payload?.includeStateTags !== false; } function normalizeIncludeSemanticTags(payload) { return payload?.includeSemanticTags !== false; } function formatSummaryValue(value, options = {}) { const normalizedValue = normalizeText(value); if (!normalizedValue) { return ""; } if (options.includeLabelQuotes === true) { return quoteText(normalizedValue); } return escapeMarkdownText(normalizedValue); } function normalizeFrameChain(value) { const rawFrameChain = Array.isArray(value) ? value : typeof value === "string" ? value.split(">") : []; return rawFrameChain .map((entry) => String(entry || "").trim()) .filter(Boolean); } function getDomHelper() { const helper = globalThis[DOM_HELPER_KEY]; if ( helper && typeof helper.captureDocument === "function" && typeof helper.detailNode === "function" && typeof helper.clickNode === "function" && typeof helper.typeNode === "function" && typeof helper.submitNode === "function" && typeof helper.typeSubmitNode === "function" && typeof helper.scrollNode === "function" ) { return helper; } return null; } function requireDomHelper(actionLabel) { const helper = getDomHelper(); if (helper) { return helper; } throw createNamedError( "BrowserPageContentHelperUnavailableError", `Browser page content cannot ${actionLabel} without the desktop DOM helper.`, { code: "browser_page_content_dom_helper_unavailable", details: { action: String(actionLabel || "resolve") } } ); } function normalizeReferenceId(value) { if (typeof value === "number" && Number.isFinite(value)) { return String(Math.trunc(value)); } if (typeof value === "string") { return value.trim(); } if (value && typeof value === "object") { return normalizeReferenceId(value.referenceId ?? value.ref ?? value.id); } return ""; } function getTagName(element) { return String(element?.tagName || "").toUpperCase(); } function expandSlotNodes(node) { if (!isElementNode(node) || getTagName(node) !== "SLOT" || typeof node.assignedNodes !== "function") { return [node]; } try { const assignedNodes = [...(node.assignedNodes({ flatten: true }) || [])].filter(Boolean); if (assignedNodes.length) { return assignedNodes.flatMap((assignedNode) => expandSlotNodes(assignedNode)); } } catch { // Fall through to the slot's fallback children. } return [...(node.childNodes || [])].flatMap((childNode) => expandSlotNodes(childNode)); } function getReadableChildNodes(element) { const shadowRoot = element?.shadowRoot; if (shadowRoot) { const shadowNodes = [...(shadowRoot.childNodes || [])].flatMap((childNode) => expandSlotNodes(childNode)); if (shadowNodes.length) { return shadowNodes; } } return [...(element?.childNodes || [])].flatMap((childNode) => expandSlotNodes(childNode)); } function getReadableElementChildren(element) { return getReadableChildNodes(element).filter((childNode) => isElementNode(childNode)); } function getReadableNodeText(node) { if (isTextNode(node)) { return node.textContent || ""; } if (!isElementNode(node) || isHiddenElement(node)) { return ""; } return getReadableChildNodes(node) .map((childNode) => getReadableNodeText(childNode)) .filter(Boolean) .join(" "); } function querySelectorAllDeep(selector, root = globalThis.document) { const results = []; const seen = new Set(); const addResult = (element) => { if (element && !seen.has(element)) { seen.add(element); results.push(element); } }; const visitRoot = (scope) => { if (!scope || typeof scope.querySelectorAll !== "function") { return; } [...(scope.querySelectorAll(selector) || [])].forEach(addResult); [...(scope.querySelectorAll("*") || [])].forEach((element) => { if (element.shadowRoot) { visitRoot(element.shadowRoot); } }); }; visitRoot(root); return results; } function getAttributeNamesSafe(element) { try { if (typeof element?.getAttributeNames === "function") { return element.getAttributeNames(); } return [...(element?.attributes || [])] .map((attribute) => String(attribute?.name || "").trim()) .filter(Boolean); } catch { return []; } } function normalizeInteractiveEventName(value) { return String(value || "") .trim() .toLowerCase() .split(/[.:]/u, 1)[0]; } function isGlobalOrDelegatedEventBinding(value) { const parts = String(value || "") .trim() .toLowerCase() .split(/[.:]/u) .map((part) => part.trim()) .filter(Boolean); return parts.includes("window") || parts.includes("document") || parts.includes("outside") || parts.includes("away"); } function isInteractiveEventName(value) { return INTERACTIVE_EVENT_NAMES.has(normalizeInteractiveEventName(value)); } function isInteractiveEventAttributeName(attributeName) { const normalizedName = String(attributeName || "").trim().toLowerCase(); if (!normalizedName) { return false; } if (normalizedName.startsWith("@")) { if (isGlobalOrDelegatedEventBinding(normalizedName.slice(1))) { return false; } return isInteractiveEventName(normalizedName.slice(1)); } if (normalizedName.startsWith("x-on:") || normalizedName.startsWith("v-on:")) { if (isGlobalOrDelegatedEventBinding(normalizedName.slice(5))) { return false; } return isInteractiveEventName(normalizedName.slice(5)); } if (normalizedName.startsWith("ng-")) { return isInteractiveEventName(normalizedName.slice(3)); } if (normalizedName.startsWith("on") && normalizedName.length > 2) { return isInteractiveEventName(normalizedName.slice(2)); } return false; } function hasHelperManagedNodeReference(element) { return Boolean(normalizeAttributeText(element?.getAttribute?.("data-space-browser-node-id"))); } function hasInteractiveEventHandlerAttribute(element) { return getAttributeNamesSafe(element).some((attributeName) => { return isInteractiveEventAttributeName(attributeName); }); } function hasInteractiveEventHandlerProperty(element) { return INTERACTIVE_EVENT_PROPERTIES.some((propertyName) => { return typeof element?.[propertyName] === "function"; }); } function hasInteractiveEventHandler(element) { return hasInteractiveEventHandlerAttribute(element) || hasInteractiveEventHandlerProperty(element); } function isStyleDeclarationHidden(styleValue) { const normalizedStyleValue = String(styleValue || "") .toLowerCase() .replace(/\s+/gu, ""); if (!normalizedStyleValue) { return false; } return /(?:^|;)display:none(?:;|$)/u.test(normalizedStyleValue) || /(?:^|;)visibility:hidden(?:;|$)/u.test(normalizedStyleValue) || /(?:^|;)visibility:collapse(?:;|$)/u.test(normalizedStyleValue) || /(?:^|;)content-visibility:hidden(?:;|$)/u.test(normalizedStyleValue) || /(?:^|;)opacity:0(?:\.0+)?(?:;|$)/u.test(normalizedStyleValue); } function isComputedStyleHidden(computedStyle) { if (!computedStyle) { return false; } const display = normalizeText(computedStyle.display).toLowerCase(); const visibility = normalizeText(computedStyle.visibility).toLowerCase(); const contentVisibility = normalizeText(computedStyle.contentVisibility).toLowerCase(); const opacity = Number(computedStyle.opacity || 1); return display === "none" || visibility === "hidden" || visibility === "collapse" || contentVisibility === "hidden" || opacity <= 0; } function isEffectivelyHiddenByAncestor(element) { let current = element; while (isElementNode(current)) { if (current.hidden || current.getAttribute?.("aria-hidden") === "true") { return true; } if (isStyleDeclarationHidden(current.getAttribute?.("style"))) { return true; } if (isComputedStyleHidden(getComputedStyleSafe(current))) { return true; } current = current.parentElement; } return false; } function isHiddenElement(element) { if (!isElementNode(element)) { return true; } const tagName = getTagName(element); if (SKIP_TAGS.has(tagName)) { return true; } if (element.hidden || element.getAttribute?.("aria-hidden") === "true") { return true; } if (tagName === "INPUT" && String(element.getAttribute?.("type") || "").toLowerCase() === "hidden") { return true; } if (isStyleDeclarationHidden(element.getAttribute?.("style"))) { return true; } const computedStyle = getComputedStyleSafe(element); if (isComputedStyleHidden(computedStyle)) { return true; } return isEffectivelyHiddenByAncestor(element.parentElement); } function isBlockElement(element) { return BLOCK_TAGS.has(getTagName(element)); } function isInteractiveElement(element) { if (!isElementNode(element) || isHiddenElement(element)) { return false; } if (hasHelperManagedNodeReference(element)) { return true; } const tagName = getTagName(element); if (tagName === "A" && element.hasAttribute?.("href")) { return true; } if (tagName === "BUTTON" || tagName === "INPUT" || tagName === "SELECT" || tagName === "TEXTAREA" || tagName === "SUMMARY") { return true; } if (String(element.getAttribute?.("contenteditable") || "").toLowerCase() === "true") { return true; } const role = String(element.getAttribute?.("role") || "").trim().toLowerCase(); return INTERACTIVE_ROLES.has(role) || hasInteractiveEventHandler(element); } function isFileInputElement(element) { return getTagName(element) === "INPUT" && String(element.getAttribute?.("type") || element.type || "").toLowerCase() === "file"; } function getAssociatedLabelFileInput(labelElement) { if (getTagName(labelElement) !== "LABEL") { return null; } if (isFileInputElement(labelElement.control)) { return labelElement.control; } const descendantInput = labelElement.querySelector?.("input[type='file']"); if (isFileInputElement(descendantInput)) { return descendantInput; } const forId = normalizeAttributeText(labelElement.getAttribute?.("for")); if (!forId) { return null; } return isFileInputElement(labelElement.ownerDocument?.getElementById?.(forId)) ? labelElement.ownerDocument.getElementById(forId) : null; } function isFileInputLabel(element) { if (getTagName(element) !== "LABEL" || isHiddenElement(element)) { return false; } const input = getAssociatedLabelFileInput(element); return Boolean(input && isHiddenElement(input)); } function getComputedStyleSafe(element) { try { return globalThis.getComputedStyle?.(element) || null; } catch { return null; } } function getElementRectSafe(element) { try { const rect = element?.getBoundingClientRect?.(); if (!rect) { return null; } return { height: Number(rect.height) || 0, width: Number(rect.width) || 0, x: Number(rect.x) || 0, y: Number(rect.y) || 0 }; } catch { return null; } } function readSerializedTagList(element, attributeName) { const rawValue = normalizeText(element?.getAttribute?.(attributeName)); if (!rawValue) { return []; } return rawValue .split(/\s+/u) .map((part) => normalizeText(part)) .filter(Boolean); } function detectSemanticTone(element, computedStyle, metadata = {}) { const opacity = Number(computedStyle?.opacity || 1); const backgroundColor = parseCssColor(computedStyle?.backgroundColor || ""); const borderColor = parseCssColor(computedStyle?.borderTopColor || ""); const foregroundColor = parseCssColor(computedStyle?.color || ""); const isButtonLike = ["BUTTON", "INPUT", "SUMMARY"].includes(getTagName(element)) || ["button", "tab", "menuitem"].includes(String(element?.getAttribute?.("role") || "").trim().toLowerCase()); if (metadata.disabled || metadata.blocked || opacity <= 0.58) { return "muted"; } const preferredColor = [backgroundColor, borderColor, foregroundColor] .filter((color) => color && color.a > 0.15) .map((color) => ({ color, hsl: rgbToHsl(color) })) .find((entry) => entry.hsl && entry.hsl.saturation >= 0.2); if (!preferredColor) { return ""; } const { hue, lightness, saturation } = preferredColor.hsl; if (saturation < 0.2) { return ""; } if ((hue >= 345 || hue < 20) && lightness >= 0.18 && lightness <= 0.82) { return "error"; } if (hue >= 20 && hue < 65 && lightness >= 0.2 && lightness <= 0.9) { return "warning"; } if (hue >= 65 && hue < 170 && lightness >= 0.16 && lightness <= 0.84) { return "success"; } if (hue >= 170 && hue < 280 && lightness >= 0.14 && lightness <= 0.82) { if (isButtonLike && backgroundColor?.a > 0.2) { return "primary"; } return ""; } return ""; } function collectElementStateMetadata(element, options = {}) { if (!isElementNode(element)) { return { descriptorTags: [], semanticTags: [], stateTags: [] }; } const computedStyle = getComputedStyleSafe(element); const rect = getElementRectSafe(element); const tagName = getTagName(element); const ariaDisabled = String(element.getAttribute?.("aria-disabled") || "").trim().toLowerCase() === "true"; const ariaBusy = String(element.getAttribute?.("aria-busy") || "").trim().toLowerCase() === "true"; const ariaChecked = String(element.getAttribute?.("aria-checked") || "").trim().toLowerCase() === "true"; const ariaCurrent = normalizeText(element.getAttribute?.("aria-current")); const ariaInvalid = String(element.getAttribute?.("aria-invalid") || "").trim().toLowerCase() === "true"; const ariaPressed = String(element.getAttribute?.("aria-pressed") || "").trim().toLowerCase() === "true"; const ariaReadonly = String(element.getAttribute?.("aria-readonly") || "").trim().toLowerCase() === "true"; const ariaRequired = String(element.getAttribute?.("aria-required") || "").trim().toLowerCase() === "true"; const ariaSelected = String(element.getAttribute?.("aria-selected") || "").trim().toLowerCase() === "true"; const helperStateTags = readSerializedTagList(element, "data-space-browser-state-tags"); const helperSemanticTags = readSerializedTagList(element, "data-space-browser-semantic-tags"); const closestInert = typeof element.closest === "function" ? element.closest("[inert]") : null; const pointerEventsNone = normalizeText(computedStyle?.pointerEvents || "").toLowerCase() === "none"; const disabled = Boolean(element.disabled || ariaDisabled || closestInert || helperStateTags.includes("disabled")); const blocked = !disabled && (pointerEventsNone || helperStateTags.includes("blocked")); const checked = Boolean(element.checked || ariaChecked || helperStateTags.includes("checked")); const selected = tagName === "OPTION" ? Boolean(element.selected) : Boolean(ariaSelected || helperStateTags.includes("selected")); const invalid = Boolean(ariaInvalid || helperStateTags.includes("invalid") || element.matches?.(":invalid")); const readonly = Boolean(element.readOnly || ariaReadonly); const required = Boolean(element.required || ariaRequired); const expanded = String(element.getAttribute?.("aria-expanded") || "").trim().toLowerCase() === "true" || helperStateTags.includes("expanded"); const pressed = ariaPressed || helperStateTags.includes("pressed"); const busy = ariaBusy || helperStateTags.includes("busy"); const current = Boolean((ariaCurrent && ariaCurrent !== "false") || helperStateTags.includes("current")); const zeroRect = Boolean( rect && element.ownerDocument === globalThis.document && rect.width <= 1 && rect.height <= 1 ); const opacity = Number(computedStyle?.opacity || 1); const semanticTone = helperSemanticTags[0] || detectSemanticTone(element, computedStyle, { blocked, disabled }); const stateTags = helperStateTags.length ? helperStateTags.slice() : [ disabled ? "disabled" : "", !disabled && (blocked || zeroRect) ? "blocked" : "", checked ? "checked" : "", selected && tagName !== "SELECT" ? "selected" : "", invalid ? "invalid" : "", expanded ? "expanded" : "", pressed ? "pressed" : "" ].filter(Boolean); const semanticTags = helperSemanticTags.length ? helperSemanticTags.slice(0, 1) : (semanticTone ? [semanticTone] : []); const descriptorTags = [ ...(options.includeStateTags !== false ? stateTags : []), ...(options.includeSemanticTags !== false ? semanticTags : []) ]; return { blocked, busy, checked, current, cursor: normalizeText(computedStyle?.cursor || "").toLowerCase(), descriptorTags, disabled, expanded, invalid, opacity, pointerEventsNone, pressed, readonly, required, selected, semanticTags, semanticTone, stateTags, visible: !isHiddenElement(element), zeroRect }; } function getReferenceValueMetadata(element) { const tagName = getTagName(element); const helperLiveValue = normalizeText(element?.getAttribute?.("data-space-browser-live-value")); const helperSelectedValue = normalizeText(element?.getAttribute?.("data-space-browser-selected-text")); if (tagName === "INPUT") { const inputType = String(element.getAttribute?.("type") || element.type || "text").toLowerCase(); if (inputType === "password") { return ""; } return truncateText(helperLiveValue || element.value || element.getAttribute?.("value") || "", 96); } if (tagName === "TEXTAREA") { return truncateText(helperLiveValue || element.value || "", 96); } if (tagName === "SELECT") { if (helperSelectedValue) { return helperSelectedValue; } const selectedOptions = [...(element.selectedOptions || [])] .map((option) => truncateText(option.textContent || option.label || option.value || "", 48)) .filter(Boolean); return selectedOptions.join(" | "); } if (String(element.getAttribute?.("contenteditable") || "").toLowerCase() === "true") { return truncateText(element.textContent || "", 96); } return ""; } function collectMetaLines(doc = globalThis.document) { const lines = []; const title = normalizeAttributeText(doc?.title || ""); const description = normalizeAttributeText( doc?.querySelector?.('meta[name="description"]')?.getAttribute?.("content") || "" ); const url = String(globalThis.location?.href || ""); if (!title && !description && !url) { return ""; } lines.push("---"); if (title) { lines.push(`title: ${quoteText(title)}`); } if (description) { lines.push(`description: ${quoteText(description)}`); } if (url) { lines.push(`url: ${quoteText(url)}`); } lines.push("---"); return lines.join("\n"); } function summarizeUrl(value) { const normalizedValue = String(value || "").trim(); if (!normalizedValue) { return ""; } try { const url = new URL(normalizedValue, globalThis.location?.href || "http://localhost/"); if (url.origin === globalThis.location?.origin) { const relative = `${url.pathname || "/"}${url.search || ""}${url.hash || ""}`; return truncateText(relative || "/", 96); } return truncateText(`${url.hostname}${url.pathname || "/"}`, 96); } catch { return truncateText(normalizedValue, 96); } } function getElementText(element) { const readableText = normalizeText(getReadableNodeText(element)); return readableText || normalizeText(element?.textContent || ""); } function isLabelableControlForText(element) { return ["BUTTON", "INPUT", "METER", "OUTPUT", "PROGRESS", "SELECT", "TEXTAREA"].includes(getTagName(element)); } function getLabelElementText(labelElement, controlElement = null) { const collect = (node) => { if (isTextNode(node)) { return node.textContent || ""; } if (!isElementNode(node) || isHiddenElement(node)) { return ""; } if (node !== labelElement && (node === controlElement || isLabelableControlForText(node))) { return ""; } return getReadableChildNodes(node) .map((childNode) => collect(childNode)) .filter(Boolean) .join(" "); }; return normalizeText(collect(labelElement)) || getElementText(labelElement); } function collectLabelCandidates(element, options = {}) { const includeAlt = options.includeAlt !== false; const includeDescendantImageAlt = options.includeDescendantImageAlt !== false; const includePlaceholder = options.includePlaceholder === true; const includeText = options.includeText !== false; const collectedLabels = []; try { if (Array.isArray(element?.labels) || typeof element?.labels?.forEach === "function") { element.labels.forEach((labelElement) => { const text = getLabelElementText(labelElement, element); if (text) { collectedLabels.push(text); } }); } } catch { // Ignore labels lookup failures from non-form elements. } [ element?.getAttribute?.("aria-label"), element?.getAttribute?.("title") ].forEach((candidate) => { const text = normalizeAttributeText(candidate); if (text) { collectedLabels.push(text); } }); if (includeAlt) { const altText = normalizeAttributeText(element?.getAttribute?.("alt")); if (altText) { collectedLabels.push(altText); } } if (includePlaceholder) { const placeholderText = normalizeAttributeText(element?.getAttribute?.("placeholder")); if (placeholderText) { collectedLabels.push(placeholderText); } } if (includeDescendantImageAlt) { try { [...(element?.querySelectorAll?.("img[alt], img[title]") || [])] .slice(0, 3) .forEach((mediaElement) => { const text = normalizeAttributeText( mediaElement.getAttribute?.("alt") || mediaElement.getAttribute?.("title") ); if (text) { collectedLabels.push(text); } }); } catch { // Ignore descendant-media lookup failures. } } if (includeText) { const textContent = getElementText(element); if (textContent) { collectedLabels.push(textContent); } } return [...new Set(collectedLabels.filter(Boolean))]; } function getLabelText(element, options = {}) { return collectLabelCandidates(element, options)[0] || ""; } function serializeElementSnapshot(element) { if (!isElementNode(element)) { return ""; } try { if (typeof element.outerHTML === "string" && element.outerHTML) { return element.outerHTML; } } catch { // Fall through to XMLSerializer. } try { if (typeof globalThis.XMLSerializer === "function") { return new globalThis.XMLSerializer().serializeToString(element); } } catch { // Ignore serialization errors. } return ""; } function getReferenceKind(element) { const tagName = getTagName(element); const role = String(element.getAttribute?.("role") || "").trim().toLowerCase(); const inputType = String(element.getAttribute?.("type") || element.type || "text").toLowerCase(); if (tagName === "A" || role === "link") { return "link"; } if (tagName === "IMG") { return "image"; } if (tagName === "BUTTON" || ["button", "menuitem", "tab"].includes(role)) { return "button"; } if (tagName === "TEXTAREA") { return "textarea"; } if (tagName === "SELECT" || role === "combobox") { return "select"; } if (tagName === "SUMMARY") { return "summary"; } if (tagName === "INPUT") { if (["button", "submit", "reset"].includes(inputType)) { return "button"; } if (inputType === "checkbox") { return "checkbox"; } if (inputType === "radio") { return "radio"; } return `input ${inputType || "text"}`; } if (tagName === "LABEL" && isFileInputLabel(element)) { return "file input label"; } if (String(element.getAttribute?.("contenteditable") || "").toLowerCase() === "true") { return "editable"; } if (role === "searchbox") { return "input search"; } if (role === "textbox") { return "input text"; } if (hasHelperManagedNodeReference(element) || hasInteractiveEventHandler(element)) { return "button"; } return role || tagName.toLowerCase(); } function collectReferenceSummaryData(element, options = {}) { const tagName = getTagName(element); const role = String(element.getAttribute?.("role") || "").trim().toLowerCase(); const id = normalizeAttributeText(element.getAttribute?.("id")); const name = normalizeAttributeText(element.getAttribute?.("name")); const kind = getReferenceKind(element); const stateMetadata = collectElementStateMetadata(element, options); const formatValue = (value) => formatSummaryValue(value, options); const includeLinkUrls = options.includeLinkUrls === true; const parts = []; const appendFallbackIdOrName = () => { if (id) { parts.push(`#${id}`); return; } if (name) { parts.push(`name=${formatValue(name)}`); } }; if (tagName === "A" || role === "link") { const hrefSummary = summarizeUrl(element.getAttribute?.("href") || element.href || ""); const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: true, includePlaceholder: false, includeText: true }), 120); const displayLabel = label || hrefSummary; if (displayLabel) { parts.push(formatValue(displayLabel)); } else { appendFallbackIdOrName(); } if (includeLinkUrls) { if (hrefSummary && hrefSummary !== displayLabel) { parts.push(`-> ${hrefSummary}`); } } } else if (tagName === "BUTTON" || ["button", "menuitem", "tab"].includes(role)) { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: true, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else if (tagName === "TEXTAREA" || role === "textbox" || role === "searchbox") { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } const placeholder = normalizeAttributeText(element.getAttribute?.("placeholder")); if (placeholder) { parts.push(`placeholder=${formatValue(placeholder)}`); } else if (!label) { appendFallbackIdOrName(); } } else if (tagName === "SELECT" || role === "combobox") { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } const selectedValue = getReferenceValueMetadata(element); const selectedOptions = selectedValue ? [selectedValue] : [...(element.selectedOptions || [])] .map((option) => truncateText(option.textContent || "", 48)) .filter(Boolean); if (selectedOptions.length) { parts.push(`selected=${formatValue(selectedOptions.join(" | "))}`); } } else if (tagName === "SUMMARY") { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: true, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else if (tagName === "INPUT") { const inputType = String(element.getAttribute?.("type") || element.type || "text").toLowerCase(); if (["button", "submit", "reset"].includes(inputType)) { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: false }) || element.value || "", 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else if (["checkbox", "radio"].includes(inputType)) { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: false }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else if (inputType === "file") { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: false }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: false }), 120); if (label) { parts.push(formatValue(label)); } const placeholder = normalizeAttributeText(element.getAttribute?.("placeholder")); const value = inputType === "password" ? "" : getReferenceValueMetadata(element); if (placeholder) { parts.push(`placeholder=${formatValue(placeholder)}`); } if (value) { parts.push(`value=${formatValue(value)}`); } if (!label && !placeholder && !value) { appendFallbackIdOrName(); } } } else if (String(element.getAttribute?.("contenteditable") || "").toLowerCase() === "true") { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: false, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else if (tagName === "IMG") { const srcSummary = summarizeUrl(element.currentSrc || element.getAttribute?.("src") || element.src || ""); const label = truncateText(getLabelText(element, { includeAlt: true, includeDescendantImageAlt: false, includePlaceholder: false, includeText: false }), 120); const displayLabel = label || srcSummary; if (displayLabel) { parts.push(formatValue(displayLabel)); } else { appendFallbackIdOrName(); } } else if (role) { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: true, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } else { const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: true, includePlaceholder: false, includeText: true }), 120); if (label) { parts.push(formatValue(label)); } else { appendFallbackIdOrName(); } } return { descriptorTags: stateMetadata.descriptorTags.slice(), kind, semanticTags: stateMetadata.semanticTags.slice(), state: stateMetadata, summary: parts.filter(Boolean).join(" ") }; } function createReferenceEntry(element, referenceId, options = {}) { const nodeId = normalizeAttributeText(element.getAttribute?.("data-space-browser-node-id")); const frameId = normalizeAttributeText(element.getAttribute?.("data-space-browser-frame-id")); const frameChain = normalizeFrameChain(element.getAttribute?.("data-space-browser-frame-chain")); const helperBacked = Boolean(nodeId && frameChain.length); const summaryData = collectReferenceSummaryData(element, options); return { connected: helperBacked ? true : element.isConnected !== false, dom: serializeElementSnapshot(element), descriptorTags: summaryData.descriptorTags, element: helperBacked ? null : element, frameChain, frameId, helperBacked, id: normalizeAttributeText(element.getAttribute?.("id")), name: normalizeAttributeText(element.getAttribute?.("name")), nodeId, referenceId, kind: summaryData.kind, semanticTags: summaryData.semanticTags, state: summaryData.state, summary: summaryData.summary, tagName: getTagName(element) }; } function ensureReference(element, context) { if (context.referenceIdsByElement.has(element)) { return context.referenceIdsByElement.get(element); } const referenceId = String(context.nextReferenceId++); const entry = createReferenceEntry(element, referenceId, context.options); context.referenceIdsByElement.set(element, referenceId); context.entries.set(referenceId, entry); return referenceId; } function renderReference(element, context) { const referenceId = ensureReference(element, context); const entry = context.entries.get(referenceId); const kind = normalizeText(entry?.kind || getTagName(element).toLowerCase()); const descriptorTags = Array.isArray(entry?.descriptorTags) ? entry.descriptorTags.map((tag) => normalizeText(tag)).filter(Boolean) : []; const summary = normalizeText(entry?.summary || ""); const descriptor = [...descriptorTags, kind, referenceId].filter(Boolean).join(" "); return summary ? `[${descriptor}] ${summary}` : `[${descriptor}]`; } function isReferenceableElement(element) { return isInteractiveElement(element) || getTagName(element) === "IMG" || isFileInputLabel(element); } function collectLabelControlElements(labelElement) { const controls = []; const seen = new Set(); const addControl = (element) => { if (!isElementNode(element) || seen.has(element) || !isReferenceableElement(element)) { return; } seen.add(element); controls.push(element); }; [ "input", "textarea", "select", "button", "summary", "a[href]", "[role]", "[contenteditable='true']", "[contenteditable='']" ].forEach((selector) => { try { [...(labelElement.querySelectorAll?.(selector) || [])].forEach(addControl); } catch { // Ignore unsupported selectors in unusual DOMs. } }); return controls; } function renderControlLabelReferences(labelElement, context) { return collectLabelControlElements(labelElement) .map((controlElement) => renderReference(controlElement, context)) .filter(Boolean) .join("\n"); } function renderInlineNode(node, context) { if (isTextNode(node)) { const textContent = normalizeText(node.textContent || ""); if (shouldDropReadableText(textContent)) { return ""; } return escapeMarkdownText(textContent); } if (!isElementNode(node) || isHiddenElement(node)) { return ""; } if (isReferenceableElement(node)) { return renderReference(node, context); } const tagName = getTagName(node); if (tagName === "LABEL" && (node.getAttribute?.("for") || node.querySelector?.("input, textarea, select, button"))) { return renderControlLabelReferences(node, context); } if (tagName === "BR") { return "\n"; } if (tagName === "STRONG" || tagName === "B") { const content = renderInlineChildren(node, context); return content ? `**${content}**` : ""; } if (tagName === "EM" || tagName === "I") { const content = renderInlineChildren(node, context); return content ? `*${content}*` : ""; } if (tagName === "S" || tagName === "STRIKE" || tagName === "DEL") { const content = renderInlineChildren(node, context); return content ? `~~${content}~~` : ""; } if (tagName === "CODE") { const content = normalizeText(node.textContent || ""); return content ? `\`${content.replace(/`/gu, "\\`")}\`` : ""; } return renderInlineChildren(node, context); } function renderInlineChildren(element, context) { const parts = []; getReadableChildNodes(element).forEach((childNode) => { const renderedChild = renderInlineNode(childNode, context); if (renderedChild) { parts.push(renderedChild); } }); return joinInlineParts(parts); } function renderParagraph(element, context) { return renderInlineChildren(element, context); } function renderHeading(element, context) { const level = Math.min(6, Math.max(1, Number.parseInt(getTagName(element).slice(1), 10) || 1)); const content = renderInlineChildren(element, context); return content ? `${"#".repeat(level)} ${content}` : ""; } function renderCodeBlock(element) { const content = String(element.textContent || "").trimEnd(); if (!content) { return ""; } return `\`\`\`\n${content.replace(/```/gu, "\\`\\`\\`")}\n\`\`\``; } function renderBlockquote(element, context) { const content = renderBlockChildren(element, context); if (!content) { return ""; } return content .split("\n") .map((line) => `> ${line}`) .join("\n"); } function renderListItem(element, context, depth, index, ordered) { const includeListMarkers = context.options.includeListMarkers === true; const includeListIndentation = context.options.includeListIndentation !== false; const marker = includeListMarkers ? (ordered ? `${index + 1}.` : "-") : ""; const indentation = includeListIndentation ? " ".repeat(Math.max(0, depth)) : ""; const inlineParts = []; const nestedBlocks = []; getReadableChildNodes(element).forEach((childNode) => { if (isElementNode(childNode) && (getTagName(childNode) === "UL" || getTagName(childNode) === "OL")) { const nestedList = renderList(childNode, context, depth + 1); if (nestedList) { nestedBlocks.push(nestedList); } return; } const renderedChild = renderInlineNode(childNode, context); if (renderedChild) { inlineParts.push(renderedChild); } }); const head = joinInlineParts(inlineParts); const linePrefix = marker ? `${indentation}${marker} ` : indentation; const lines = [`${linePrefix}${head || "(empty)"}`]; nestedBlocks.forEach((nestedBlock) => { lines.push(indentBlock(nestedBlock, includeListIndentation ? 1 : 0)); }); return lines.join("\n"); } function renderList(element, context, depth = 0) { const ordered = getTagName(element) === "OL"; return getReadableElementChildren(element) .filter((child) => getTagName(child) === "LI" && !isHiddenElement(child)) .map((item, index) => renderListItem(item, context, depth, index, ordered)) .filter(Boolean) .join("\n"); } function renderTableCell(element, context) { return renderInlineChildren(element, context); } function renderTable(element, context) { const rows = [...element.querySelectorAll?.(":scope > thead > tr, :scope > tbody > tr, :scope > tr, :scope > tfoot > tr") || []] .filter((row) => getTagName(row) === "TR"); if (!rows.length) { return ""; } const renderedRows = rows.map((row) => { return [...row.children] .filter((cell) => ["TD", "TH"].includes(getTagName(cell)) && !isHiddenElement(cell)) .map((cell) => renderTableCell(cell, context)); }).filter((cells) => cells.length); if (!renderedRows.length) { return ""; } const columnCount = Math.max(...renderedRows.map((cells) => cells.length)); const normalizedRows = renderedRows.map((cells) => { const nextCells = cells.slice(); while (nextCells.length < columnCount) { nextCells.push(""); } return nextCells; }); const headerRow = normalizedRows[0]; const separatorRow = headerRow.map(() => "---"); const tableLines = [ `| ${headerRow.join(" | ")} |`, `| ${separatorRow.join(" | ")} |` ]; normalizedRows.slice(1).forEach((row) => { tableLines.push(`| ${row.join(" | ")} |`); }); return tableLines.join("\n"); } function renderGenericContainer(element, context) { return renderBlockChildren(element, context); } function renderElementAsBlock(element, context) { if (!isElementNode(element) || isHiddenElement(element)) { return ""; } if (isReferenceableElement(element)) { return renderReference(element, context); } const tagName = getTagName(element); if (tagName === "LABEL" && (element.getAttribute?.("for") || element.querySelector?.("input, textarea, select, button"))) { return renderControlLabelReferences(element, context); } if (/^H[1-6]$/u.test(tagName)) { return renderHeading(element, context); } if (tagName === "P") { return renderParagraph(element, context); } if (tagName === "PRE") { return renderCodeBlock(element); } if (tagName === "BLOCKQUOTE") { return renderBlockquote(element, context); } if (tagName === "UL" || tagName === "OL") { return renderList(element, context); } if (tagName === "TABLE") { return renderTable(element, context); } if (tagName === "HR") { return "---"; } return renderGenericContainer(element, context); } function renderBlockChildren(element, context) { const blocks = []; const inlineParts = []; const flushInlineParts = () => { const inlineText = joinInlineParts(inlineParts.splice(0, inlineParts.length)); if (inlineText) { blocks.push(inlineText); } }; getReadableChildNodes(element).forEach((childNode) => { if (isTextNode(childNode)) { const rawTextContent = normalizeText(childNode.textContent || ""); if (shouldDropReadableText(rawTextContent)) { return; } const textContent = escapeMarkdownText(rawTextContent); if (textContent) { inlineParts.push(textContent); } return; } if (!isElementNode(childNode) || isHiddenElement(childNode)) { return; } const renderedChild = renderElementAsBlock(childNode, context); if (!renderedChild) { return; } if (isBlockElement(childNode) || isReferenceableElement(childNode)) { flushInlineParts(); blocks.push(renderedChild); return; } inlineParts.push(renderedChild); }); flushInlineParts(); return joinBlocks(blocks); } function createCaptureContext(payload = null) { return { entries: new Map(), nextReferenceId: 1, options: { includeLabelQuotes: normalizeIncludeLabelQuotes(payload), includeLinkUrls: normalizeIncludeLinkUrls(payload), includeSemanticTags: normalizeIncludeSemanticTags(payload), includeStateTags: normalizeIncludeStateTags(payload), includeListIndentation: normalizeIncludeListIndentation(payload), includeListMarkers: normalizeIncludeListMarkers(payload) }, referenceIdsByElement: new WeakMap() }; } function resolveSelectorTargets(payload, doc = globalThis.document) { const selectors = normalizeSelectorList(payload); if (!selectors.length) { return { includeMetaData: true, items: [ { key: "document", targets: [doc?.body || doc?.documentElement].filter(Boolean) } ] }; } return { includeMetaData: false, items: selectors.map((selector) => { let targets = []; try { targets = doc === globalThis.document ? querySelectorAllDeep(selector, doc) : [...(doc?.querySelectorAll?.(selector) || [])]; } catch (error) { throw createNamedError( "BrowserPageContentSelectorError", `Browser page content could not resolve selector "${selector}".`, { code: "browser_page_content_selector_error", details: { selector }, cause: error } ); } return { key: selector, targets }; }) }; } function parseSnapshotFragment(html, parser) { return parser.parseFromString( `${String(html || "")}`, "text/html" ); } function renderSnapshotFragment(html, captureContext, parser) { const parsedDocument = parseSnapshotFragment(html, parser); const blocks = []; const inlineParts = []; const flushInlineParts = () => { const inlineText = joinInlineParts(inlineParts.splice(0, inlineParts.length)); if (inlineText) { blocks.push(inlineText); } }; parsedDocument.body.childNodes.forEach((childNode) => { if (isTextNode(childNode)) { const rawTextContent = normalizeText(childNode.textContent || ""); if (shouldDropReadableText(rawTextContent)) { return; } const textContent = escapeMarkdownText(rawTextContent); if (textContent) { inlineParts.push(textContent); } return; } if (!isElementNode(childNode) || isHiddenElement(childNode)) { return; } const renderedChild = renderElementAsBlock(childNode, captureContext); if (!renderedChild) { return; } if (isBlockElement(childNode) || isReferenceableElement(childNode)) { flushInlineParts(); blocks.push(renderedChild); return; } inlineParts.push(renderedChild); }); flushInlineParts(); return cleanReadableMarkdown(joinBlocks(blocks)); } function captureLive(payload = null) { const captureContext = createCaptureContext(payload); const resolvedTargets = resolveSelectorTargets(payload); const snapshot = {}; resolvedTargets.items.forEach((item) => { const blocks = []; if (resolvedTargets.includeMetaData && item.key === "document") { const meta = collectMetaLines(globalThis.document); if (meta) { blocks.push(meta); } } item.targets.forEach((target) => { const renderedTarget = renderElementAsBlock(target, captureContext); if (renderedTarget) { blocks.push(renderedTarget); } }); snapshot[item.key] = cleanReadableMarkdown(joinBlocks(blocks)); }); state.captureId += 1; state.capturedAt = Date.now(); state.backend = "live"; state.captureOptions = { ...captureContext.options }; state.entries = captureContext.entries; return snapshot; } async function captureWithDomHelper(payload = null) { const helper = requireDomHelper("capture content"); const selectors = normalizeSelectorList(payload); const helperPayload = { snapshotMode: "content" }; if (selectors.length) { helperPayload.selectors = selectors; } const documentSnapshot = await helper.captureDocument({ ...helperPayload }); const snapshot = {}; const parser = new globalThis.DOMParser(); const captureContext = createCaptureContext(payload); try { if (selectors.length && documentSnapshot?.targets && typeof documentSnapshot.targets === "object") { selectors.forEach((selector) => { snapshot[selector] = renderSnapshotFragment(documentSnapshot.targets?.[selector] || "", captureContext, parser); }); state.captureId += 1; state.capturedAt = Date.now(); state.backend = "dom_helper"; state.captureOptions = { ...captureContext.options }; state.entries = captureContext.entries; return snapshot; } const parsedDocument = parser.parseFromString(String(documentSnapshot?.html || ""), "text/html"); const resolvedTargets = resolveSelectorTargets(payload, parsedDocument); resolvedTargets.items.forEach((item) => { const blocks = []; if (resolvedTargets.includeMetaData && item.key === "document") { const meta = collectMetaLines(parsedDocument); if (meta) { blocks.push(meta); } } item.targets.forEach((target) => { const renderedTarget = renderElementAsBlock(target, captureContext); if (renderedTarget) { blocks.push(renderedTarget); } }); snapshot[item.key] = cleanReadableMarkdown(joinBlocks(blocks)); }); state.captureId += 1; state.capturedAt = Date.now(); state.backend = "dom_helper"; state.captureOptions = { ...captureContext.options }; state.entries = captureContext.entries; return snapshot; } catch (error) { if (!isTrustedHtmlRequirementError(error)) { throw error; } return captureLive(payload); } } async function capture(payload = null) { if (getDomHelper()) { return captureWithDomHelper(payload); } return captureLive(payload); } function detailLive(entry) { const liveState = entry.connected && entry.element ? collectElementStateMetadata(entry.element, state.captureOptions) : entry.state || collectElementStateMetadata(null); return { captureId: state.captureId, capturedAt: state.capturedAt, connected: entry.connected, descriptorTags: liveState.descriptorTags, dom: entry.connected ? serializeElementSnapshot(entry.element) || entry.dom : entry.dom, referenceId: entry.referenceId, semanticTags: liveState.semanticTags, state: liveState, summary: entry.summary, tagName: entry.tagName }; } async function detail(referenceId) { const entry = requireReferenceEntry(referenceId, { actionLabel: "detail", requireConnected: false }); if (entry.helperBacked) { const helper = requireDomHelper("resolve detail"); const resolvedDetail = await helper.detailNode(entry.frameChain, entry.nodeId); return { captureId: state.captureId, capturedAt: state.capturedAt, connected: resolvedDetail?.connected !== false, descriptorTags: Array.isArray(resolvedDetail?.descriptorTags) ? resolvedDetail.descriptorTags : (entry.descriptorTags || []), dom: String(resolvedDetail?.dom || entry.dom || ""), frameChain: entry.frameChain.slice(), frameId: entry.frameId, nodeId: entry.nodeId, referenceId: entry.referenceId, semanticTags: Array.isArray(resolvedDetail?.semanticTags) ? resolvedDetail.semanticTags : (entry.semanticTags || []), state: resolvedDetail?.state || entry.state || collectElementStateMetadata(null), summary: entry.summary, tagName: String(resolvedDetail?.tagName || entry.tagName || "") }; } return detailLive(entry); } function requireReferenceEntry(referenceId, options = {}) { const normalizedReferenceId = normalizeReferenceId(referenceId); if (!normalizedReferenceId) { throw createNamedError( "BrowserPageContentReferenceError", "Browser page content requests require a reference id.", { code: "browser_page_content_reference_required", details: { action: String(options.actionLabel || "resolve") } } ); } if (!state.entries.size) { throw createNamedError( "BrowserPageContentReferenceError", `Browser page content has no reference capture for "${normalizedReferenceId}".`, { code: "browser_page_content_reference_missing_capture", details: { action: String(options.actionLabel || "resolve"), referenceId: normalizedReferenceId } } ); } const entry = state.entries.get(normalizedReferenceId); if (!entry) { throw createNamedError( "BrowserPageContentReferenceError", `Browser page content could not find reference "${normalizedReferenceId}".`, { code: "browser_page_content_reference_not_found", details: { action: String(options.actionLabel || "resolve"), referenceId: normalizedReferenceId } } ); } refreshReferenceEntry(entry); if (options.requireConnected !== false && !entry.connected) { throw createNamedError( "BrowserPageContentReferenceError", `Browser page content reference "${normalizedReferenceId}" is no longer connected.`, { code: "browser_page_content_reference_disconnected", details: { action: String(options.actionLabel || "resolve"), referenceId: normalizedReferenceId } } ); } return entry; } function computeStableSelector(el) { if (!el || el.nodeType !== 1) return null; const doc = el.ownerDocument || document; if (el.id && /^[A-Za-z_][\w-]*$/.test(el.id)) { const sel = "#" + (typeof CSS !== "undefined" && CSS.escape ? CSS.escape(el.id) : el.id); try { if (doc.querySelectorAll(sel).length === 1) return sel; } catch (_) {} } const parts = []; let node = el; while (node && node.nodeType === 1 && node !== doc.documentElement) { let part = node.tagName.toLowerCase(); if (node.id && /^[A-Za-z_][\w-]*$/.test(node.id)) { const idSel = "#" + (typeof CSS !== "undefined" && CSS.escape ? CSS.escape(node.id) : node.id); try { if (doc.querySelectorAll(idSel).length === 1) { parts.unshift(idSel); break; } } catch (_) {} } const parent = node.parentElement; if (parent) { const sibs = parent.children; let idx = 0; let sameTag = 0; for (let i = 0; i < sibs.length; i++) { if (sibs[i].tagName === node.tagName) { sameTag++; if (sibs[i] === node) idx = sameTag; } } if (sameTag > 1) part += ":nth-of-type(" + idx + ")"; } parts.unshift(part); node = parent; } const sel = parts.join(" > "); if (!sel) return null; try { if (doc.querySelectorAll(sel).length === 1) return sel; } catch (_) {} return null; } function boundingBoxFor(referenceId) { const entry = requireReferenceEntry(referenceId, { actionLabel: "boundingBox", requireConnected: false }); if (entry.helperBacked || !entry.element) return null; const el = entry.element; if (typeof el.getBoundingClientRect !== "function") return null; try { el.scrollIntoView({ block: "center", inline: "center", behavior: "instant" }); } catch (_) {} const r = el.getBoundingClientRect(); const selector = computeStableSelector(el); const hasBox = r && r.width > 0 && r.height > 0; if (!hasBox && !selector) return null; return { x: hasBox ? r.left : 0, y: hasBox ? r.top : 0, width: hasBox ? r.width : 0, height: hasBox ? r.height : 0, selector: selector || null }; } function pointFor(referenceId, offsets = {}) { const entry = requireReferenceEntry(referenceId, { actionLabel: "point", requireConnected: true }); if (entry.helperBacked || !entry.element) { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot resolve point for helper-backed reference "${entry.referenceId}".`, { code: "browser_page_content_point_helper_backed" } ); } const element = entry.element; scrollElementIntoView(element); const rect = getElementRectSafe(element); if (!rect || rect.width <= 0 || rect.height <= 0) { throw createNamedError( "BrowserPageContentActionError", `Browser page content reference "${entry.referenceId}" has no visible viewport box.`, { code: "browser_page_content_point_no_box" } ); } const offsetX = Number(offsets?.offset_x ?? offsets?.offsetX ?? 0) || 0; const offsetY = Number(offsets?.offset_y ?? offsets?.offsetY ?? 0) || 0; const useOffsets = offsets?.useOffsets === true || offsetX !== 0 || offsetY !== 0; return { rect, selector: computeStableSelector(element), x: rect.x + (useOffsets ? offsetX : rect.width / 2), y: rect.y + (useOffsets ? offsetY : rect.height / 2) }; } function normalizeActionValues(valueOrValues) { if (Array.isArray(valueOrValues)) { return valueOrValues.map((value) => String(value ?? "")); } if (valueOrValues === null || valueOrValues === undefined) { return []; } return [String(valueOrValues)]; } function optionMatchesValue(option, value) { const normalizedValue = normalizeText(value); const candidates = [ option?.value, option?.label, option?.textContent, option?.getAttribute?.("aria-label"), option?.getAttribute?.("data-value"), option?.getAttribute?.("id") ].map((candidate) => normalizeText(candidate)); return candidates.some((candidate) => candidate === normalizedValue); } function findNativeSelectOption(selectElement, value) { const options = [...(selectElement.options || [])]; return options.find((option) => optionMatchesValue(option, value)) || null; } function setNativeChecked(element, checked) { const descriptor = Object.getOwnPropertyDescriptor(globalThis.HTMLInputElement?.prototype || {}, "checked"); if (typeof descriptor?.set === "function") { descriptor.set.call(element, Boolean(checked)); } else { element.checked = Boolean(checked); } } async function selectNativeElement(entry, values) { const element = entry.element; const beforeSnapshot = captureActionEffectSnapshot(element); const requestedValues = values.length ? values : [""]; const appliedValues = []; const { observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { scrollElementIntoView(element); focusElement(element); if (element.multiple) { const matchedOptions = requestedValues.map((requestedValue) => { const option = findNativeSelectOption(element, requestedValue); if (!option) { throw createNamedError( "BrowserPageContentActionError", `Browser page content could not find select option "${requestedValue}".`, { code: "browser_page_content_select_option_not_found" } ); } return option; }); const matchedSet = new Set(matchedOptions); [...(element.options || [])].forEach((option) => { option.selected = matchedSet.has(option); }); matchedOptions.forEach((option) => appliedValues.push(option.value)); } else { const option = findNativeSelectOption(element, requestedValues[0]); if (!option) { throw createNamedError( "BrowserPageContentActionError", `Browser page content could not find select option "${requestedValues[0]}".`, { code: "browser_page_content_select_option_not_found" } ); } appliedValues.push(setNativeValue(element, option.value)); } dispatchDomEvent(element, "input", "InputEvent", { inputType: "insertReplacementText" }); dispatchDomEvent(element, "change"); return appliedValues.slice(); }); refreshReferenceEntry(entry); return buildActionResult(entry, { ...buildActionEffectResult(entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations), values: appliedValues.slice() }); } function ariaOptionMatchesValue(option, value) { const normalizedValue = normalizeText(value); const candidates = [ option?.getAttribute?.("aria-label"), option?.getAttribute?.("data-value"), option?.getAttribute?.("value"), option?.getAttribute?.("id"), getElementText(option) ].map((candidate) => normalizeText(candidate)); return candidates.some((candidate) => candidate === normalizedValue); } function visibleAriaOptions(root) { const scope = isElementNode(root) && String(root.getAttribute?.("role") || "").trim().toLowerCase() === "listbox" ? root : globalThis.document; try { return [...(scope.querySelectorAll?.("[role='option']") || [])] .filter((option) => isElementNode(option) && !isHiddenElement(option)); } catch { return []; } } function findAriaOption(root, value) { const matches = visibleAriaOptions(root).filter((option) => ariaOptionMatchesValue(option, value)); return matches.length === 1 ? matches[0] : null; } async function selectAriaElement(entry, values) { const element = entry.element; const role = String(element.getAttribute?.("role") || "").trim().toLowerCase(); if (!["combobox", "listbox"].includes(role)) { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot select options on <${getTagName(element).toLowerCase()}>.`, { code: "browser_page_content_select_unsupported" } ); } const beforeSnapshot = captureActionEffectSnapshot(element); const requestedValues = values.length ? values : [""]; const appliedValues = []; const { observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { scrollElementIntoView(element); focusElement(element); if (role === "combobox") { dispatchDomEvent(element, "mousedown", "MouseEvent", { button: 0 }); if (typeof element.click === "function") { element.click(); } else { dispatchDomEvent(element, "click", "MouseEvent", { button: 0 }); } await delayMs(80); } for (const requestedValue of requestedValues) { const option = findAriaOption(element, requestedValue); if (!option) { throw createNamedError( "BrowserPageContentActionError", `Browser page content could not safely find one ARIA option "${requestedValue}".`, { code: "browser_page_content_aria_option_not_found" } ); } scrollElementIntoView(option); dispatchDomEvent(option, "mousedown", "MouseEvent", { button: 0 }); if (typeof option.click === "function") { option.click(); } else { dispatchDomEvent(option, "click", "MouseEvent", { button: 0 }); } appliedValues.push(requestedValue); await delayMs(40); } }); refreshReferenceEntry(entry); return buildActionResult(entry, { ...buildActionEffectResult(entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations), values: appliedValues.slice() }); } async function selectReference(referenceId, valueOrValues) { const entry = requireReferenceEntry(referenceId, { actionLabel: "select" }); if (entry.helperBacked) { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot select helper-backed reference "${entry.referenceId}".`, { code: "browser_page_content_select_helper_backed" } ); } const element = entry.element; const values = normalizeActionValues(valueOrValues); if (getTagName(element) === "SELECT") { return selectNativeElement(entry, values); } return selectAriaElement(entry, values); } function checkedStateForElement(element) { const tagName = getTagName(element); const role = String(element.getAttribute?.("role") || "").trim().toLowerCase(); if (tagName === "INPUT") { const inputType = String(element.getAttribute?.("type") || element.type || "").toLowerCase(); if (["checkbox", "radio"].includes(inputType)) { return Boolean(element.checked); } } if (["checkbox", "radio", "switch", "menuitemcheckbox", "menuitemradio"].includes(role)) { return String(element.getAttribute?.("aria-checked") || "").trim().toLowerCase() === "true"; } if (role === "button" && element.hasAttribute?.("aria-pressed")) { return String(element.getAttribute?.("aria-pressed") || "").trim().toLowerCase() === "true"; } return null; } async function setCheckedReference(referenceId, checked = true) { const entry = requireReferenceEntry(referenceId, { actionLabel: "setChecked" }); if (entry.helperBacked) { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot set helper-backed reference "${entry.referenceId}".`, { code: "browser_page_content_checked_helper_backed" } ); } const element = entry.element; const beforeSnapshot = captureActionEffectSnapshot(element); const desiredChecked = Boolean(checked); const tagName = getTagName(element); const role = String(element.getAttribute?.("role") || "").trim().toLowerCase(); const currentChecked = checkedStateForElement(element); if (currentChecked === null) { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot set checked state on <${tagName.toLowerCase()}>.`, { code: "browser_page_content_checked_unsupported" } ); } const { observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { scrollElementIntoView(element); focusElement(element); if (tagName === "INPUT") { setNativeChecked(element, desiredChecked); dispatchDomEvent(element, "input", "InputEvent", { inputType: "insertReplacementText" }); dispatchDomEvent(element, "change"); } else if (["checkbox", "radio", "switch", "menuitemcheckbox", "menuitemradio"].includes(role)) { if (currentChecked !== desiredChecked) { if (typeof element.click === "function") { element.click(); } else { dispatchDomEvent(element, "click", "MouseEvent", { button: 0 }); } await delayMs(40); } if (checkedStateForElement(element) !== desiredChecked) { element.setAttribute("aria-checked", desiredChecked ? "true" : "false"); dispatchDomEvent(element, "input", "InputEvent", { inputType: "insertReplacementText" }); dispatchDomEvent(element, "change"); } } else if (role === "button" && element.hasAttribute?.("aria-pressed")) { if (currentChecked !== desiredChecked) { if (typeof element.click === "function") { element.click(); } else { dispatchDomEvent(element, "click", "MouseEvent", { button: 0 }); } await delayMs(40); } if (checkedStateForElement(element) !== desiredChecked) { element.setAttribute("aria-pressed", desiredChecked ? "true" : "false"); } } }); refreshReferenceEntry(entry); return buildActionResult(entry, { ...buildActionEffectResult(entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations), checked: desiredChecked }); } function resolveFileInputElement(referenceId) { const entry = requireReferenceEntry(referenceId, { actionLabel: "fileInput" }); if (entry.helperBacked) { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot upload files through helper-backed reference "${entry.referenceId}".`, { code: "browser_page_content_file_input_helper_backed" } ); } const element = entry.element; const isFileInput = (candidate) => { return getTagName(candidate) === "INPUT" && String(candidate.getAttribute?.("type") || candidate.type || "").toLowerCase() === "file"; }; if (isFileInput(element)) { return element; } if (getTagName(element) === "LABEL") { if (isFileInput(element.control)) { return element.control; } const labelledInput = element.querySelector?.("input[type='file']"); if (isFileInput(labelledInput)) { return labelledInput; } const forId = normalizeAttributeText(element.getAttribute?.("for")); if (forId) { const byId = element.ownerDocument?.getElementById?.(forId); if (isFileInput(byId)) { return byId; } } } const descendantInput = element.querySelector?.("input[type='file']"); if (isFileInput(descendantInput)) { return descendantInput; } const closestLabel = element.closest?.("label"); if (closestLabel) { if (isFileInput(closestLabel.control)) { return closestLabel.control; } const labelledInput = closestLabel.querySelector?.("input[type='file']"); if (isFileInput(labelledInput)) { return labelledInput; } } return null; } function fileInputFor(referenceId) { const input = resolveFileInputElement(referenceId); if (!input) { throw createNamedError( "BrowserPageContentActionError", `Browser page content reference "${normalizeReferenceId(referenceId)}" is not a file input or associated label.`, { code: "browser_page_content_file_input_not_found" } ); } return { accept: normalizeAttributeText(input.getAttribute?.("accept")), multiple: Boolean(input.multiple), name: normalizeAttributeText(input.getAttribute?.("name")), selector: computeStableSelector(input), tagName: getTagName(input), type: String(input.getAttribute?.("type") || input.type || "").toLowerCase() }; } function fileInputElementFor(referenceId) { return resolveFileInputElement(referenceId); } function refreshReferenceEntry(entry) { if (!entry || entry.helperBacked || !entry.element) { return entry; } entry.connected = entry.element.isConnected !== false; if (entry.connected) { entry.dom = serializeElementSnapshot(entry.element) || entry.dom; entry.id = normalizeAttributeText(entry.element.getAttribute?.("id")); entry.name = normalizeAttributeText(entry.element.getAttribute?.("name")); const summaryData = collectReferenceSummaryData(entry.element, state.captureOptions); entry.descriptorTags = summaryData.descriptorTags; entry.kind = summaryData.kind; entry.semanticTags = summaryData.semanticTags; entry.state = summaryData.state; entry.summary = summaryData.summary; entry.tagName = getTagName(entry.element); } return entry; } function scrollElementIntoView(element) { try { element.scrollIntoView?.({ behavior: "auto", block: "center", inline: "center" }); return true; } catch { return false; } } function focusElement(element) { try { element.focus?.({ preventScroll: true }); return true; } catch { try { element.focus?.(); return true; } catch { return false; } } } function describeActiveElement(element) { if (!isElementNode(element)) { return ""; } const tagName = getTagName(element).toLowerCase(); const id = normalizeAttributeText(element.getAttribute?.("id")); const name = normalizeAttributeText(element.getAttribute?.("name")); const label = truncateText(getLabelText(element, { includeAlt: false, includeDescendantImageAlt: true, includePlaceholder: false, includeText: false }), 48); return [tagName, id ? `#${id}` : "", name ? `name=${name}` : "", label].filter(Boolean).join(" "); } function getActionObservationRoot(element) { if (!isElementNode(element)) { return globalThis.document?.body || globalThis.document?.documentElement || null; } return element.closest?.("form, fieldset, dialog, [role='dialog'], [role='alert'], [role='status'], [aria-live], article, section, main, li, tr, td, th") || element.parentElement || element; } function getElementDirectText(element) { if (!isElementNode(element)) { return ""; } return normalizeText( [...(element.childNodes || [])] .filter((node) => isTextNode(node)) .map((node) => node.textContent || "") .join(" ") ); } function collectNearbyTextEntries(root, limit = 24) { if (!isElementNode(root)) { return []; } const entries = []; const seen = new Set(); const acceptElement = (element) => { if (!isElementNode(element) || isHiddenElement(element) || entries.length >= limit) { return; } const role = normalizeText(element.getAttribute?.("role")).toLowerCase(); const directText = getElementDirectText(element); const fallbackText = ["alert", "status"].includes(role) || element.hasAttribute?.("aria-live") ? getElementText(element) : ""; const text = truncateText(directText || fallbackText, 220); if (!text) { return; } const key = `${role}|${text}`; if (seen.has(key)) { return; } seen.add(key); const state = collectElementStateMetadata(element, { includeSemanticTags: true, includeStateTags: true }); entries.push({ invalid: state.invalid === true, role, semanticTone: state.semanticTone || "", text }); }; acceptElement(root); const walker = globalThis.document?.createTreeWalker?.(root, globalThis.NodeFilter?.SHOW_ELEMENT ?? 1); if (!walker) { return entries; } let currentNode = walker.nextNode(); while (currentNode && entries.length < limit) { acceptElement(currentNode); currentNode = walker.nextNode(); } return entries; } function captureActionEffectSnapshot(element) { const observationRoot = getActionObservationRoot(element); return { activeElement: describeActiveElement(globalThis.document?.activeElement), observationRoot, observationText: truncateText(getElementText(observationRoot), 2000), targetDom: truncateText(serializeElementSnapshot(element), 2000), targetState: collectElementStateMetadata(element, { includeSemanticTags: true, includeStateTags: true }), textEntries: collectNearbyTextEntries(observationRoot), value: getReferenceValueMetadata(element) }; } async function waitForObservedActionWindow(observationRoot, { quietMs = 40, timeoutMs = 180 } = {}) { const target = observationRoot?.ownerDocument?.body || observationRoot?.ownerDocument?.documentElement || globalThis.document?.body || globalThis.document?.documentElement; if (!target || typeof globalThis.MutationObserver !== "function") { await delayMs(timeoutMs); return { attributeNames: [], mutationCount: 0 }; } const attributeNames = new Set(); let lastMutationAt = 0; let mutationCount = 0; const observer = new globalThis.MutationObserver((mutations) => { mutationCount += mutations.length; lastMutationAt = Date.now(); mutations.forEach((mutation) => { if (mutation.type === "attributes" && mutation.attributeName) { attributeNames.add(String(mutation.attributeName)); } }); }); try { observer.observe(target, { attributes: true, characterData: true, childList: true, subtree: true }); const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { await delayMs(20); if (mutationCount > 0 && Date.now() - lastMutationAt >= quietMs) { break; } } } finally { observer.disconnect(); } return { attributeNames: [...attributeNames], mutationCount }; } async function withObservedActionWindow(observationRoot, action, options = {}) { const target = observationRoot?.ownerDocument?.body || observationRoot?.ownerDocument?.documentElement || globalThis.document?.body || globalThis.document?.documentElement; if (!target || typeof globalThis.MutationObserver !== "function") { const result = await action(); const observedMutations = await waitForObservedActionWindow(observationRoot, options); return { observedMutations, result }; } const attributeNames = new Set(); let lastMutationAt = 0; let mutationCount = 0; const observer = new globalThis.MutationObserver((mutations) => { mutationCount += mutations.length; lastMutationAt = Date.now(); mutations.forEach((mutation) => { if (mutation.type === "attributes" && mutation.attributeName) { attributeNames.add(String(mutation.attributeName)); } }); }); try { observer.observe(target, { attributes: true, characterData: true, childList: true, subtree: true }); const result = await action(); const quietMs = Math.max(0, Number(options.quietMs) || 40); const timeoutMs = Math.max(0, Number(options.timeoutMs) || 180); const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { await delayMs(20); if (mutationCount > 0 && Date.now() - lastMutationAt >= quietMs) { break; } } return { observedMutations: { attributeNames: [...attributeNames], mutationCount }, result }; } finally { observer.disconnect(); } } function compareDescriptorTags(beforeTags = [], afterTags = []) { const beforeValue = beforeTags.filter(Boolean).join("|"); const afterValue = afterTags.filter(Boolean).join("|"); return beforeValue !== afterValue; } function buildActionEffectResult(entry, beforeSnapshot, afterSnapshot, observedMutations, extra = {}) { const newTextEntries = afterSnapshot.textEntries.filter((entryData) => { return !beforeSnapshot.textEntries.some((beforeEntry) => beforeEntry.text === entryData.text); }); const validationEntries = newTextEntries.filter((entryData) => { return entryData.invalid || ["alert", "status"].includes(entryData.role) || ["error", "warning"].includes(entryData.semanticTone); }); const focusChanged = beforeSnapshot.activeElement !== afterSnapshot.activeElement; const nearbyTextChanged = beforeSnapshot.observationText !== afterSnapshot.observationText; const valueChanged = beforeSnapshot.value !== afterSnapshot.value; const checkedChanged = beforeSnapshot.targetState.checked !== afterSnapshot.targetState.checked; const selectedChanged = beforeSnapshot.targetState.selected !== afterSnapshot.targetState.selected; const expandedChanged = beforeSnapshot.targetState.expanded !== afterSnapshot.targetState.expanded; const pressedChanged = beforeSnapshot.targetState.pressed !== afterSnapshot.targetState.pressed; const descriptorChanged = compareDescriptorTags(beforeSnapshot.targetState.descriptorTags, afterSnapshot.targetState.descriptorTags); const targetDomChanged = beforeSnapshot.targetDom !== afterSnapshot.targetDom; const domChanged = Boolean(observedMutations.mutationCount) || targetDomChanged || nearbyTextChanged; const status = { alertTextAdded: newTextEntries.some((entryData) => ["alert", "status"].includes(entryData.role)), checkedChanged, descriptorChanged, domChanged, expandedChanged, focusChanged, nearbyTextChanged, pressedChanged, reacted: false, selectedChanged, targetChanged: descriptorChanged || targetDomChanged || valueChanged || checkedChanged || selectedChanged || expandedChanged || pressedChanged, targetDomChanged, valueChanged, validationTextAdded: validationEntries.length > 0 }; status.reacted = Object.entries(status).some(([key, value]) => key !== "reacted" && value === true); status.noObservedEffect = !status.reacted; return { ...extra, descriptorTags: afterSnapshot.targetState.descriptorTags.slice(), effect: { mutationAttributes: observedMutations.attributeNames.slice(0, 8), mutationCount: observedMutations.mutationCount, newText: newTextEntries.map((entryData) => entryData.text).slice(0, 3), semanticHints: [...new Set(newTextEntries.map((entryData) => entryData.semanticTone).filter(Boolean))].slice(0, 3), validationText: validationEntries.map((entryData) => entryData.text).slice(0, 3) }, semanticTags: afterSnapshot.targetState.semanticTags.slice(), state: afterSnapshot.targetState, status }; } function buildActionResult(entry, extra = {}) { return { captureId: state.captureId, descriptorTags: Array.isArray(entry?.descriptorTags) ? entry.descriptorTags.slice() : [], referenceId: entry.referenceId, semanticTags: Array.isArray(entry?.semanticTags) ? entry.semanticTags.slice() : [], state: entry.state || collectElementStateMetadata(entry.element, state.captureOptions), summary: entry.summary, tagName: entry.tagName, ...extra }; } function buildHelperBackedActionResult(entry, helperResult, extra = {}) { return { captureId: state.captureId, descriptorTags: Array.isArray(helperResult?.descriptorTags) ? helperResult.descriptorTags : (entry.descriptorTags || []), frameChain: entry.frameChain.slice(), frameId: entry.frameId, nodeId: entry.nodeId, referenceId: entry.referenceId, semanticTags: Array.isArray(helperResult?.semanticTags) ? helperResult.semanticTags : (entry.semanticTags || []), state: helperResult?.state || entry.state || collectElementStateMetadata(null), summary: entry.summary, tagName: String(helperResult?.tagName || entry.tagName || ""), ...extra }; } function mergeActionOutcomeResults(...results) { const normalizedResults = results.filter(Boolean); const mergedStatus = {}; const mergedEffect = { mutationAttributes: [], mutationCount: 0, newText: [], semanticHints: [], validationText: [] }; normalizedResults.forEach((result) => { Object.entries(result?.status || {}).forEach(([key, value]) => { if (typeof value === "boolean") { mergedStatus[key] = mergedStatus[key] === true || value === true; } }); if (Number.isFinite(result?.effect?.mutationCount)) { mergedEffect.mutationCount += Number(result.effect.mutationCount); } ["mutationAttributes", "newText", "semanticHints", "validationText"].forEach((key) => { const values = Array.isArray(result?.effect?.[key]) ? result.effect[key] : []; values.forEach((value) => { if (value && !mergedEffect[key].includes(value)) { mergedEffect[key].push(value); } }); }); }); mergedStatus.reacted = Object.entries(mergedStatus).some(([key, value]) => key !== "reacted" && key !== "noObservedEffect" && value === true); mergedStatus.noObservedEffect = !mergedStatus.reacted; return { effect: mergedEffect, status: mergedStatus }; } function dispatchDomEvent(target, eventName, EventType = "Event", options = {}) { const EventConstructor = typeof globalThis[EventType] === "function" ? globalThis[EventType] : globalThis.Event; const event = new EventConstructor(eventName, { bubbles: true, cancelable: true, composed: true, ...options }); target.dispatchEvent(event); return event; } function dispatchKeyboardEvent(target, eventName, options = {}) { const KeyboardEventConstructor = typeof globalThis.KeyboardEvent === "function" ? globalThis.KeyboardEvent : globalThis.Event; const event = new KeyboardEventConstructor(eventName, { bubbles: true, cancelable: true, composed: true, code: "Enter", key: "Enter", ...options }); [ ["charCode", Number(options.charCode ?? 0)], ["keyCode", Number(options.keyCode ?? 13)], ["which", Number(options.which ?? 13)] ].forEach(([propertyName, propertyValue]) => { try { if (typeof event[propertyName] !== "number") { Object.defineProperty(event, propertyName, { configurable: true, enumerable: true, value: propertyValue }); } } catch { // Ignore read-only KeyboardEvent properties. } }); target.dispatchEvent(event); return event; } function setNativeValue(element, nextValue) { const tagName = getTagName(element); const normalizedValue = String(nextValue ?? ""); if (tagName === "INPUT") { const descriptor = Object.getOwnPropertyDescriptor(globalThis.HTMLInputElement?.prototype || {}, "value"); if (typeof descriptor?.set === "function") { descriptor.set.call(element, normalizedValue); } else { element.value = normalizedValue; } return normalizedValue; } if (tagName === "TEXTAREA") { const descriptor = Object.getOwnPropertyDescriptor(globalThis.HTMLTextAreaElement?.prototype || {}, "value"); if (typeof descriptor?.set === "function") { descriptor.set.call(element, normalizedValue); } else { element.value = normalizedValue; } return normalizedValue; } if (tagName === "SELECT") { const matchedOption = [...(element.options || [])].find((option) => { return option.value === normalizedValue || normalizeText(option.textContent || "") === normalizeText(normalizedValue) || normalizeText(option.label || "") === normalizeText(normalizedValue); }); const resolvedValue = matchedOption ? matchedOption.value : normalizedValue; const descriptor = Object.getOwnPropertyDescriptor(globalThis.HTMLSelectElement?.prototype || {}, "value"); if (typeof descriptor?.set === "function") { descriptor.set.call(element, resolvedValue); } else { element.value = resolvedValue; } return resolvedValue; } if (String(element.getAttribute?.("contenteditable") || "").toLowerCase() === "true") { element.textContent = normalizedValue; return normalizedValue; } throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot type into <${getTagName(element).toLowerCase()}>.`, { code: "browser_page_content_type_unsupported" } ); } async function updateElementValue(referenceId, value) { const entry = requireReferenceEntry(referenceId, { actionLabel: "type" }); if (entry.helperBacked) { const helper = requireDomHelper("type into reference"); const typedResult = await helper.typeNode(entry.frameChain, entry.nodeId, value); return buildHelperBackedActionResult(entry, typedResult, { effect: typedResult?.effect || {}, status: typedResult?.status || {}, value: typedResult?.value ?? String(value ?? "") }); } const element = entry.element; const beforeSnapshot = captureActionEffectSnapshot(element); const { result: appliedValue, observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { scrollElementIntoView(element); focusElement(element); const nextValue = setNativeValue(element, value); if (typeof element.setSelectionRange === "function") { try { element.setSelectionRange(String(nextValue).length, String(nextValue).length); } catch { // Ignore selection errors for unsupported input types. } } dispatchDomEvent(element, "beforeinput", "InputEvent", { data: String(value ?? ""), inputType: "insertText" }); dispatchDomEvent(element, "input", "InputEvent", { data: String(value ?? ""), inputType: "insertText" }); dispatchDomEvent(element, "change"); return nextValue; }); refreshReferenceEntry(entry); return buildActionResult(entry, { ...buildActionEffectResult(entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations), value: appliedValue }); } async function activateElement(referenceId) { const entry = requireReferenceEntry(referenceId, { actionLabel: "click" }); if (entry.helperBacked) { const helper = requireDomHelper("click reference"); const clickedResult = await helper.clickNode(entry.frameChain, entry.nodeId); return buildHelperBackedActionResult(entry, clickedResult, { effect: clickedResult?.effect || {}, status: clickedResult?.status || {} }); } const element = entry.element; const beforeSnapshot = captureActionEffectSnapshot(element); scrollElementIntoView(element); focusElement(element); if (beforeSnapshot.targetState.disabled) { throw createNamedError( "BrowserPageContentActionError", `Browser page content reference "${entry.referenceId}" is disabled.`, { code: "browser_page_content_click_disabled" } ); } const { observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { if (typeof element.click === "function") { element.click(); } else { dispatchDomEvent(element, "click", "MouseEvent", { button: 0 }); } }); refreshReferenceEntry(entry); return buildActionResult(entry, buildActionEffectResult( entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations )); } async function submitElement(referenceId) { const entry = requireReferenceEntry(referenceId, { actionLabel: "submit" }); if (entry.helperBacked) { const helper = requireDomHelper("submit reference"); const submittedResult = await helper.submitNode(entry.frameChain, entry.nodeId); return buildHelperBackedActionResult(entry, submittedResult, { effect: submittedResult?.effect || {}, status: submittedResult?.status || {} }); } const element = entry.element; const tagName = getTagName(element); const beforeSnapshot = captureActionEffectSnapshot(element); const { observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { scrollElementIntoView(element); focusElement(element); if (tagName === "FORM") { if (typeof element.requestSubmit === "function") { element.requestSubmit(); } else { const submitEvent = dispatchDomEvent(element, "submit"); if (!submitEvent.defaultPrevented) { element.submit?.(); } } } else if (typeof element.form?.requestSubmit === "function") { if (tagName === "BUTTON" || tagName === "INPUT") { element.form.requestSubmit(element); } else { element.form.requestSubmit(); } } else if (element.form) { const submitEvent = dispatchDomEvent(element.form, "submit"); if (!submitEvent.defaultPrevented) { element.form.submit?.(); } } else if (typeof element.click === "function") { element.click(); } else { throw createNamedError( "BrowserPageContentActionError", `Browser page content cannot submit reference "${entry.referenceId}".`, { code: "browser_page_content_submit_unsupported" } ); } }); refreshReferenceEntry(entry); return buildActionResult(entry, buildActionEffectResult( entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations )); } function shouldEnterSubmitForm(element) { const tagName = getTagName(element); if (tagName !== "INPUT") { return false; } const inputType = String(element.getAttribute?.("type") || element.type || "text").toLowerCase(); return ![ "button", "checkbox", "color", "file", "hidden", "image", "radio", "range", "reset", "submit" ].includes(inputType); } async function pressEnterElement(referenceId, actionLabel = "type_submit") { const entry = requireReferenceEntry(referenceId, { actionLabel }); if (entry.helperBacked) { const helper = requireDomHelper("press enter on reference"); const submittedResult = await helper.typeSubmitNode(entry.frameChain, entry.nodeId, ""); return buildHelperBackedActionResult(entry, submittedResult, { effect: submittedResult?.effect || {}, status: submittedResult?.status || {} }); } const element = entry.element; const beforeSnapshot = captureActionEffectSnapshot(element); const { observedMutations } = await withObservedActionWindow(beforeSnapshot.observationRoot, async () => { scrollElementIntoView(element); focusElement(element); const keydownEvent = dispatchKeyboardEvent(element, "keydown", { charCode: 0, keyCode: 13, which: 13 }); const keypressEvent = dispatchKeyboardEvent(element, "keypress", { charCode: 13, keyCode: 13, which: 13 }); const keyupEvent = dispatchKeyboardEvent(element, "keyup", { charCode: 0, keyCode: 13, which: 13 }); if ( !keydownEvent.defaultPrevented && !keypressEvent.defaultPrevented && !keyupEvent.defaultPrevented && shouldEnterSubmitForm(element) ) { if (typeof element.form?.requestSubmit === "function") { element.form.requestSubmit(); } else if (element.form) { const submitEvent = dispatchDomEvent(element.form, "submit"); if (!submitEvent.defaultPrevented) { element.form.submit?.(); } } } }); refreshReferenceEntry(entry); return buildActionResult(entry, buildActionEffectResult( entry, beforeSnapshot, captureActionEffectSnapshot(element), observedMutations )); } async function typeAndSubmit(referenceId, value) { const entry = requireReferenceEntry(referenceId, { actionLabel: "type_submit" }); if (entry.helperBacked) { const helper = requireDomHelper("type and submit reference"); const submittedResult = await helper.typeSubmitNode(entry.frameChain, entry.nodeId, value); return buildHelperBackedActionResult(entry, submittedResult, { effect: submittedResult?.effect || {}, status: submittedResult?.status || {}, value: submittedResult?.value ?? String(value ?? "") }); } const typed = await updateElementValue(referenceId, value); const submitted = await pressEnterElement(referenceId); const mergedOutcome = mergeActionOutcomeResults(typed, submitted); return { ...submitted, ...mergedOutcome, value: typed.value }; } async function scrollToReference(referenceId) { const entry = requireReferenceEntry(referenceId, { actionLabel: "scroll" }); if (entry.helperBacked) { const helper = requireDomHelper("scroll to reference"); const scrollResult = await helper.scrollNode(entry.frameChain, entry.nodeId); return buildHelperBackedActionResult(entry, scrollResult, { effect: scrollResult?.effect || {}, status: scrollResult?.status || {} }); } const beforeSnapshot = captureActionEffectSnapshot(entry.element); scrollElementIntoView(entry.element); focusElement(entry.element); refreshReferenceEntry(entry); const afterSnapshot = captureActionEffectSnapshot(entry.element); const scrollEffect = buildActionEffectResult(entry, beforeSnapshot, afterSnapshot, { attributeNames: [], mutationCount: 0 }); return buildActionResult(entry, { ...scrollEffect, status: { ...scrollEffect.status, reacted: true, noObservedEffect: false } }); } function cssEscape(value) { const rawValue = String(value || ""); if (!rawValue) { return ""; } if (typeof globalThis.CSS?.escape === "function") { return globalThis.CSS.escape(rawValue); } return rawValue.replace(/[^a-zA-Z0-9_-]/gu, (character) => `\\${character}`); } function getClassSummary(element) { try { return [...(element?.classList || [])] .map((className) => normalizeAttributeText(className)) .filter(Boolean) .slice(0, 4) .join(" "); } catch { return ""; } } function buildCssSelector(element) { if (!isElementNode(element)) { return ""; } const id = normalizeAttributeText(element.getAttribute?.("id")); if (id) { return `#${cssEscape(id)}`; } const parts = []; let current = element; while (isElementNode(current) && current !== globalThis.document?.documentElement && parts.length < 6) { const tagName = getTagName(current).toLowerCase(); if (!tagName) { break; } let part = tagName; const classes = getClassSummary(current) .split(/\s+/u) .filter(Boolean) .slice(0, 2); if (classes.length && !["body", "html"].includes(tagName)) { part += classes.map((className) => `.${cssEscape(className)}`).join(""); } const parent = current.parentElement; if (parent) { const siblings = [...parent.children].filter((sibling) => getTagName(sibling) === getTagName(current)); if (siblings.length > 1) { part += `:nth-of-type(${siblings.indexOf(current) + 1})`; } } parts.unshift(part); if (tagName === "body") { break; } current = parent; } return parts.join(" > "); } function sanitizeAnnotationDom(value) { return truncateText( String(value || "") .replace(/(]*\btype\s*=\s*(["'])?password\2?)[^>]*?)\s+value\s*=\s*(["'])[\s\S]*?\3/giu, "$1 value=\"[redacted]\"") .replace(/\svalue\s*=\s*(["'])[\s\S]{0,600}?\1/giu, " value=\"[redacted]\"") .replace(/\sdata-space-browser-live-value\s*=\s*(["'])[\s\S]{0,600}?\1/giu, "") .replace(/\sdata-space-browser-selected-text\s*=\s*(["'])[\s\S]{0,600}?\1/giu, ""), 1200 ); } function summarizeAnnotationElement(element) { if (!isElementNode(element)) { return null; } const summaryData = collectReferenceSummaryData(element, { includeLabelQuotes: false, includeLinkUrls: true, includeSemanticTags: true, includeStateTags: true }); const rawDom = serializeElementSnapshot(element); return { classes: getClassSummary(element), dom: sanitizeAnnotationDom(rawDom), id: normalizeAttributeText(element.getAttribute?.("id")), kind: summaryData.kind, name: normalizeAttributeText(element.getAttribute?.("name")), rect: getElementRectSafe(element), role: normalizeAttributeText(element.getAttribute?.("role")).toLowerCase(), selector: buildCssSelector(element), semanticTags: Array.isArray(summaryData.semanticTags) ? summaryData.semanticTags.slice(0, 4) : [], stateTags: Array.isArray(summaryData.state?.stateTags) ? summaryData.state.stateTags.slice(0, 8) : [], summary: truncateText(summaryData.summary || getLabelText(element, { includeAlt: true, includeDescendantImageAlt: true, includePlaceholder: true, includeText: true }), 240), tagName: getTagName(element) }; } function annotationViewport() { return { height: Math.max(0, Number(globalThis.innerHeight || globalThis.document?.documentElement?.clientHeight || 0)), scrollX: Number(globalThis.scrollX || globalThis.pageXOffset || 0), scrollY: Number(globalThis.scrollY || globalThis.pageYOffset || 0), width: Math.max(0, Number(globalThis.innerWidth || globalThis.document?.documentElement?.clientWidth || 0)) }; } function normalizeAnnotationPoint(payload = {}, viewport = annotationViewport()) { const source = payload?.point && typeof payload.point === "object" ? payload.point : payload; const width = Math.max(1, Number(viewport.width || 1)); const height = Math.max(1, Number(viewport.height || 1)); return { x: Math.max(0, Math.min(width, Number(source?.x || 0))), y: Math.max(0, Math.min(height, Number(source?.y || 0))) }; } function normalizeAnnotationRectPayload(payload = {}, viewport = annotationViewport()) { const source = payload?.rect && typeof payload.rect === "object" ? payload.rect : payload; const width = Math.max(1, Number(viewport.width || 1)); const height = Math.max(1, Number(viewport.height || 1)); const x = Math.max(0, Math.min(width, Number(source?.x || 0))); const y = Math.max(0, Math.min(height, Number(source?.y || 0))); return { height: Math.max(1, Math.min(height - y, Number(source?.height || source?.h || 1))), width: Math.max(1, Math.min(width - x, Number(source?.width || source?.w || 1))), x, y }; } function intersectRects(leftRect, rightRect) { if (!leftRect || !rightRect) { return null; } const x = Math.max(Number(leftRect.x || 0), Number(rightRect.x || 0)); const y = Math.max(Number(leftRect.y || 0), Number(rightRect.y || 0)); const right = Math.min( Number(leftRect.x || 0) + Number(leftRect.width || 0), Number(rightRect.x || 0) + Number(rightRect.width || 0) ); const bottom = Math.min( Number(leftRect.y || 0) + Number(leftRect.height || 0), Number(rightRect.y || 0) + Number(rightRect.height || 0) ); const width = right - x; const height = bottom - y; if (width <= 0 || height <= 0) { return null; } return { area: width * height, height, width, x, y }; } function deepElementFromPoint(x, y) { let element = null; try { element = globalThis.document?.elementFromPoint?.(x, y) || null; } catch { return null; } let guard = 0; while (isElementNode(element) && element.shadowRoot && guard < 8) { guard += 1; try { const nestedElement = element.shadowRoot.elementFromPoint?.(x, y); if (!nestedElement || nestedElement === element) { break; } element = nestedElement; } catch { break; } } return element; } function findAnnotationTarget(element) { if (!isElementNode(element)) { return null; } const selector = [ "a[href]", "button", "input", "textarea", "select", "summary", "[role]", "img", "label", "form", "h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "td", "th", "article", "section", "nav", "header", "main", "footer" ].join(","); const target = element.closest?.(selector) || element; return isElementNode(target) && !isHiddenElement(target) ? target : element; } function isMeaningfulAnnotationElement(element) { if (!isElementNode(element) || isHiddenElement(element)) { return false; } if (isInteractiveElement(element) || getTagName(element) === "IMG") { return true; } const tagName = getTagName(element); const role = normalizeAttributeText(element.getAttribute?.("role")).toLowerCase(); return Boolean( role || /^H[1-6]$/u.test(tagName) || ["ARTICLE", "SECTION", "MAIN", "NAV", "HEADER", "FOOTER", "FORM", "LABEL", "P", "LI", "TD", "TH"].includes(tagName) ); } function collectIntersectingAnnotationElements(rect) { const selector = [ "a[href]", "button", "input", "textarea", "select", "summary", "[role]", "img", "label", "form", "h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "td", "th", "article", "section", "main", "nav", "header", "footer" ].join(","); let candidates = []; try { candidates = [...(globalThis.document?.querySelectorAll?.(selector) || [])]; } catch { candidates = []; } const seen = new Set(); return candidates .map((element) => { if (!isMeaningfulAnnotationElement(element) || seen.has(element)) { return null; } seen.add(element); const elementRect = getElementRectSafe(element); const intersection = intersectRects(rect, elementRect); if (!intersection || intersection.area < 48) { return null; } return { element, elementArea: Math.max(1, Number(elementRect.width || 0) * Number(elementRect.height || 0)), intersection }; }) .filter(Boolean) .sort((left, right) => { if (right.intersection.area !== left.intersection.area) { return right.intersection.area - left.intersection.area; } return left.elementArea - right.elementArea; }) .slice(0, 12) .map((entry) => summarizeAnnotationElement(entry.element)) .filter(Boolean); } function annotate(payload = null) { const request = payload && typeof payload === "object" ? payload : {}; const viewport = annotationViewport(); const kind = request.kind === "area" || request.rect ? "area" : "element"; if (kind === "area") { const rect = normalizeAnnotationRectPayload(request, viewport); const point = { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 }; const elements = collectIntersectingAnnotationElements(rect); const fallbackElement = findAnnotationTarget(deepElementFromPoint(point.x, point.y)); const fallbackTarget = fallbackElement ? summarizeAnnotationElement(fallbackElement) : null; return { elements, kind, point, rect, status: elements.length || fallbackTarget ? "ok" : "empty", target: elements[0] || fallbackTarget, viewport }; } const point = normalizeAnnotationPoint(request, viewport); const rawElement = deepElementFromPoint(point.x, point.y); const targetElement = findAnnotationTarget(rawElement); const target = targetElement ? summarizeAnnotationElement(targetElement) : null; return { kind, point, rect: target?.rect || { height: 1, width: 1, x: point.x, y: point.y }, status: target ? "ok" : "empty", target, viewport }; } globalThis[GLOBAL_KEY] = { click(referenceId) { return activateElement(referenceId); }, annotate, capture, clear() { state.captureId = 0; state.capturedAt = 0; state.captureOptions = { includeLabelQuotes: false, includeLinkUrls: false, includeSemanticTags: true, includeStateTags: true, includeListIndentation: true, includeListMarkers: false }; state.entries = new Map(); }, detail, getState() { return { captureId: state.captureId, capturedAt: state.capturedAt, includeLabelQuotes: state.captureOptions.includeLabelQuotes === true, includeLinkUrls: state.captureOptions.includeLinkUrls === true, includeSemanticTags: state.captureOptions.includeSemanticTags !== false, includeStateTags: state.captureOptions.includeStateTags !== false, includeListIndentation: state.captureOptions.includeListIndentation !== false, includeListMarkers: state.captureOptions.includeListMarkers === true, referenceCount: state.entries.size }; }, scroll(referenceId) { return scrollToReference(referenceId); }, submit(referenceId) { return submitElement(referenceId); }, type(referenceId, value) { return updateElementValue(referenceId, value); }, typeSubmit(referenceId, value) { return typeAndSubmit(referenceId, value); }, boundingBoxFor, fileInputElementFor, fileInputFor, pointFor, select(referenceId, valueOrValues) { return selectReference(referenceId, valueOrValues); }, setChecked(referenceId, checked) { return setCheckedReference(referenceId, checked); }, ready() { const api = globalThis[GLOBAL_KEY]; return Boolean(api && REQUIRED_API_NAMES.every((name) => typeof api[name] === "function")); }, requiredApis: REQUIRED_API_NAMES.slice(), version: VERSION }; })();