import { evaluateSchema, getContentSchema, querySelectorSchema } from '../schemas.js';
import { getPageForOperation } from '../tabs.js';
import { handleResult, ok, err, evaluationError, selectorNotFound, normalizeError, } from '../errors.js';
/**
* Register content extraction tools
*/
export function registerContentTools(server) {
// Evaluate JavaScript
server.tool('evaluate', 'Execute JavaScript code in the browser context and return the result', evaluateSchema.shape, async ({ script, tabId }) => {
const pageResult = await getPageForOperation(tabId);
if (!pageResult.success) {
return handleResult(pageResult);
}
const page = pageResult.data;
try {
// Use Function constructor to evaluate the script
const result = await page.evaluate((code) => {
// eslint-disable-next-line no-new-func
const fn = new Function(code);
return fn();
}, script);
return handleResult(ok({ result }));
}
catch (error) {
if (error instanceof Error) {
return handleResult(err(evaluationError(error.message)));
}
return handleResult(err(normalizeError(error)));
}
});
// Get page content
server.tool('get_content', 'Get the HTML or text content of the page or a specific element', getContentSchema.shape, async ({ selector, type, tabId }) => {
const pageResult = await getPageForOperation(tabId);
if (!pageResult.success) {
return handleResult(pageResult);
}
const page = pageResult.data;
const contentType = type ?? 'text';
try {
if (selector) {
// Get content of specific element
const element = await page.$(selector);
if (!element) {
return handleResult(err(selectorNotFound(selector)));
}
const content = await element.evaluate((el, t) => {
return t === 'html' ? el.innerHTML : el.textContent ?? '';
}, contentType);
return handleResult(ok({ content, selector }));
}
else {
// Get full page content
let content;
if (contentType === 'html') {
content = await page.content();
}
else {
content = await page.evaluate(() => document.body.innerText);
}
return handleResult(ok({ content }));
}
}
catch (error) {
return handleResult(err(normalizeError(error)));
}
});
// Query selector
server.tool('query_selector', 'Get information about an element matching a CSS selector', querySelectorSchema.shape, async ({ selector, tabId }) => {
const pageResult = await getPageForOperation(tabId);
if (!pageResult.success) {
return handleResult(pageResult);
}
const page = pageResult.data;
try {
const element = await page.$(selector);
if (!element) {
return handleResult(ok({
exists: false,
selector,
}));
}
const info = await element.evaluate((el) => {
const rect = el.getBoundingClientRect();
const attributes = {};
for (let i = 0; i < el.attributes.length; i++) {
const attr = el.attributes[i];
if (attr) {
attributes[attr.name] = attr.value;
}
}
return {
tagName: el.tagName.toLowerCase(),
textContent: el.textContent?.slice(0, 1000) ?? '',
attributes,
boundingBox: {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
},
};
});
return handleResult(ok({
exists: true,
selector,
...info,
}));
}
catch (error) {
return handleResult(err(normalizeError(error)));
}
});
}
//# sourceMappingURL=content.js.map