@hyperbrowser/agent
Version:
Hyperbrowsers Web Agent
108 lines (107 loc) • 4.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildDomView = void 0;
const find_interactive_elements_1 = require("./find-interactive-elements");
const highlight_1 = require("./highlight");
const get_css_path_1 = require("./get-css-path");
const const_1 = require("./const");
const get_x_path_1 = require("./get-x-path");
// Helper function to convert ImageBitmap to PNG Data URL
const imageBitmapToPngDataUrl = (bitmap) => {
try {
// Create an intermediate canvas
const canvas = document.createElement("canvas");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
// Get context and draw the bitmap
const ctx = canvas.getContext("2d");
ctx.drawImage(bitmap, 0, 0);
// Export as PNG Data URL
// Note: might want to add error handling for toDataURL
return canvas.toDataURL("image/png");
}
finally {
// Close the bitmap to free up resources (important!)
bitmap.close();
}
};
// --- Start new function definition ---
const getElementTextContent = (el) => {
const tagName = el.tagName.toLowerCase();
if (tagName === "input") {
const inputElement = el;
let labelText = null;
// Try finding label by "for" attribute
if (inputElement.id) {
const label = document.querySelector(`label[for="${inputElement.id}"]`);
if (label) {
labelText = label.textContent?.trim() || null;
}
}
// Use label text if found, otherwise use input value. Fallback to empty string if neither.
return labelText ?? inputElement.value?.trim() ?? "";
}
else {
// Original logic for non-input elements
return el.textContent?.trim() || "";
}
};
// --- End new function definition ---
const buildDomView = () => {
const interactiveElements = (0, find_interactive_elements_1.findInteractiveElements)();
// 1. Render highlights to an ImageBitmap
const screenBitmap = (0, highlight_1.renderHighlightsOffscreen)(interactiveElements.map((element, index) => ({
element: element.element,
index: index + 1, // index range from 1 -> index
parentIframe: element.iframe ?? null,
})), window.innerWidth, window.innerHeight);
// 2. Convert the ImageBitmap to a PNG Data URL
const screenshotPngDataUrl = imageBitmapToPngDataUrl(screenBitmap);
for (let idx = 0; idx < interactiveElements.length; idx++) {
const element = interactiveElements[idx];
element.highlightIndex = idx + 1; // index range from 1 -> index
element.cssPath = (0, get_css_path_1.getCSSPath)(element.element);
element.xpath = (0, get_x_path_1.getXPath)(element.element);
}
const domRepresentation = [];
const getTextBetween = (node, nextNode) => {
const texts = [];
let current = node.nextSibling;
while (current && current !== nextNode) {
if (current.nodeType === Node.TEXT_NODE && current.textContent) {
const text = current.textContent.trim();
if (text)
texts.push(text);
}
current = current.nextSibling;
}
return texts.join(" ");
};
for (let i = 0; i < interactiveElements.length; i++) {
const element = interactiveElements[i];
const el = element.element;
const tagName = el.tagName.toLowerCase();
let attributes = "";
Array.from(el.attributes).forEach((attr) => {
if (const_1.CONTEXT_ATTRIBUTES.includes(attr.name)) {
attributes += ` ${attr.name}="${attr.value}"`;
}
});
// Use the helper function to get text content
const textContent = getElementTextContent(el);
const indexPrefix = `[${element.highlightIndex}]`;
const elementString = `${indexPrefix}<${tagName}${attributes}>${textContent.replace(/\s+/g, " ")}</${tagName}>`;
domRepresentation.push(elementString);
const nextElement = interactiveElements[i + 1]?.element || null;
const betweenText = getTextBetween(el, nextElement);
if (betweenText) {
domRepresentation.push(betweenText);
}
}
return {
elements: interactiveElements,
domState: domRepresentation.join("\n"),
screenshot: screenshotPngDataUrl,
};
};
exports.buildDomView = buildDomView;