ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
213 lines • 7.55 kB
JavaScript
/**
* Coordinates DOM element inspection functionality
* Extracted from LocalDebugEngine for better separation of concerns and testability
*
* Handles:
* - Single and multiple element inspection
* - Element existence and visibility checks
* - Element property and style extraction
* - Element bounds and positioning
*/
export class ElementInspectionCoordinator {
page = null;
/**
* Attach to a page for element inspection
*/
async attachToPage(page) {
this.page = page;
}
/**
* Inspect a specific element and capture its state
*/
async inspectElement(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
const element = document.querySelector(sel);
if (!element) {
return { exists: false };
}
const rect = element.getBoundingClientRect();
const styles = window.getComputedStyle(element);
const computedStyles = {};
// Get key computed styles
['display', 'visibility', 'opacity', 'position', 'top', 'left', 'width', 'height'].forEach(prop => {
computedStyles[prop] = styles.getPropertyValue(prop);
});
return {
exists: true,
visible: rect.width > 0 && rect.height > 0 && styles.display !== 'none' && styles.visibility !== 'hidden',
properties: {
tagName: element.tagName,
id: element.id,
className: element.className,
textContent: element.textContent?.trim(),
innerHTML: element.innerHTML.substring(0, 200),
rect: {
top: rect.top,
left: rect.left,
width: rect.width,
height: rect.height
}
},
computedStyles
};
}, selector);
}
/**
* Inspect multiple elements in a single evaluation for efficiency
*/
async inspectMultipleElements(selectors) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sels) => {
return sels.map(sel => {
const element = document.querySelector(sel);
if (!element) {
return { selector: sel, exists: false };
}
const rect = element.getBoundingClientRect();
const styles = window.getComputedStyle(element);
const computedStyles = {};
['display', 'visibility', 'opacity', 'position'].forEach(prop => {
computedStyles[prop] = styles.getPropertyValue(prop);
});
return {
selector: sel,
exists: true,
visible: rect.width > 0 && rect.height > 0 && styles.display !== 'none' && styles.visibility !== 'hidden',
properties: {
tagName: element.tagName,
id: element.id,
className: element.className,
textContent: element.textContent?.trim()
},
computedStyles
};
});
}, selectors);
}
/**
* Find all elements matching a selector
*/
async findAllElements(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
const elements = document.querySelectorAll(sel);
return Array.from(elements).map(element => ({
tagName: element.tagName,
id: element.id,
className: element.className
}));
}, selector);
}
/**
* Check if an element exists
*/
async elementExists(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
return document.querySelector(sel) !== null;
}, selector);
}
/**
* Check if an element is visible
*/
async isElementVisible(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
const element = document.querySelector(sel);
if (!element)
return false;
const rect = element.getBoundingClientRect();
const styles = window.getComputedStyle(element);
return rect.width > 0 && rect.height > 0 &&
styles.display !== 'none' &&
styles.visibility !== 'hidden' &&
styles.opacity !== '0';
}, selector);
}
/**
* Get the count of elements matching a selector
*/
async getElementCount(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
return document.querySelectorAll(sel).length;
}, selector);
}
/**
* Get all attributes of an element
*/
async getElementAttributes(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
const element = document.querySelector(sel);
if (!element)
return null;
const attributes = {};
for (const attr of Array.from(element.attributes)) {
attributes[attr.name] = attr.value;
}
return attributes;
}, selector);
}
/**
* Get computed styles for an element
*/
async getComputedStyles(selector, properties) {
if (!this.page) {
throw new Error('No page attached');
}
const propertiesToGet = properties || [
'display', 'visibility', 'opacity', 'position', 'top', 'left',
'width', 'height', 'color', 'backgroundColor', 'fontSize', 'fontFamily'
];
return await this.page.evaluate((data) => {
const element = document.querySelector(data.selector);
if (!element)
return null;
const styles = window.getComputedStyle(element);
const computedStyles = {};
data.properties.forEach((prop) => {
computedStyles[prop] = styles.getPropertyValue(prop);
});
return computedStyles;
}, { selector, properties: propertiesToGet });
}
/**
* Get element bounds (getBoundingClientRect)
*/
async getElementBounds(selector) {
if (!this.page) {
throw new Error('No page attached');
}
return await this.page.evaluate((sel) => {
const element = document.querySelector(sel);
if (!element)
return null;
const rect = element.getBoundingClientRect();
return {
top: rect.top,
left: rect.left,
width: rect.width,
height: rect.height,
right: rect.right,
bottom: rect.bottom
};
}, selector);
}
}
//# sourceMappingURL=element-inspection-coordinator.js.map