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
226 lines • 8.43 kB
JavaScript
/**
* Flutter Structure Mapper
* Maps semantic nodes to logical UI structure
*/
export class FlutterStructureMapper {
/**
* Map semantic nodes to logical UI structure
*/
static mapToUIStructure(nodes, pageUrl) {
const elements = this.nodesToElements(nodes);
const currentPage = this.identifyCurrentPage(elements, pageUrl);
const forms = this.identifyForms(elements);
const interactables = this.identifyInteractables(nodes);
const navigation = this.identifyNavigation(elements, pageUrl);
return {
pages: [currentPage],
currentPage,
navigation,
forms,
interactables
};
}
/**
* Convert semantic nodes to UI elements
*/
static nodesToElements(nodes) {
return nodes.map(node => ({
id: node.id,
type: this.inferElementType(node),
label: node.label,
bounds: node.bounds,
children: this.nodesToElements(node.children),
semanticNode: node
}));
}
/**
* Infer element type from semantic node
*/
static inferElementType(node) {
if (node.isButton)
return 'button';
if (node.isTextField)
return 'input';
const label = node.label.toLowerCase();
if (label.includes('image') || label.includes('photo'))
return 'image';
if (label.includes('text') || node.role === 'text')
return 'text';
if (node.role === 'group' || node.role === 'region')
return 'container';
return 'unknown';
}
/**
* Identify current page structure
*/
static identifyCurrentPage(elements, url) {
// Try to find page title
const titleElement = elements.find(el => el.type === 'text' &&
(el.label.includes('No Grocery Lines') ||
el.bounds.y < 100) // Near top of page
);
// Find primary action (like Submit Report button)
const primaryButton = elements.find(el => el.type === 'button' &&
(el.label.toLowerCase().includes('submit') ||
el.label.toLowerCase().includes('start') ||
el.label.toLowerCase().includes('continue')));
return {
title: titleElement?.label || 'Unknown Page',
url,
elements,
primaryAction: primaryButton ? {
id: primaryButton.id,
label: primaryButton.label,
type: 'button',
bounds: primaryButton.bounds,
actions: ['click'],
primaryAction: 'click',
semanticNode: primaryButton.semanticNode
} : undefined
};
}
/**
* Identify form structures
*/
static identifyForms(elements) {
const forms = [];
// Look for groups of input fields
const inputElements = elements.filter(el => el.type === 'input');
if (inputElements.length > 0) {
// Group inputs that are near each other
const form = {
id: 'form-1',
fields: inputElements.map(input => ({
id: input.id,
label: input.label,
type: this.inferInputType(input),
required: false, // Would need more context to determine
element: input
})),
submitButton: undefined,
validationState: 'unknown'
};
// Look for submit button near the form
const submitButton = elements.find(el => el.type === 'button' &&
el.label.toLowerCase().includes('submit'));
if (submitButton) {
form.submitButton = {
id: submitButton.id,
label: submitButton.label,
type: 'button',
bounds: submitButton.bounds,
actions: ['click'],
primaryAction: 'click',
semanticNode: submitButton.semanticNode
};
}
forms.push(form);
}
return forms;
}
/**
* Infer input field type
*/
static inferInputType(element) {
const label = element.label.toLowerCase();
if (label.includes('email'))
return 'email';
if (label.includes('password'))
return 'password';
if (label.includes('select') || label.includes('choose'))
return 'select';
if (label.includes('checkbox'))
return 'checkbox';
if (label.includes('radio'))
return 'radio';
return 'text';
}
/**
* Identify all interactable elements
*/
static identifyInteractables(nodes) {
const interactables = [];
for (const node of nodes) {
if (node.isClickable || node.actions.length > 0) {
interactables.push({
id: node.id,
label: node.label,
type: node.isButton ? 'button' :
node.isTextField ? 'input' :
'link',
bounds: node.bounds,
actions: node.actions,
primaryAction: node.actions[0] || 'click',
semanticNode: node
});
}
// Recursively check children
if (node.children.length > 0) {
interactables.push(...this.identifyInteractables(node.children));
}
}
return interactables;
}
/**
* Identify navigation structure
*/
static identifyNavigation(elements, url) {
const navigationElements = [];
// Look for elements that might be navigation
for (const element of elements) {
if (element.type === 'button' || element.type === 'unknown') {
const label = element.label.toLowerCase();
if (label.includes('back') ||
label.includes('home') ||
label.includes('menu') ||
label.includes('next') ||
label.includes('submit')) {
navigationElements.push({
id: element.id,
label: element.label,
type: 'button',
bounds: element.bounds,
actions: ['click'],
primaryAction: 'click',
semanticNode: element.semanticNode
});
}
}
}
return {
currentRoute: url,
possibleRoutes: [url], // Would need more analysis to find other routes
navigationElements
};
}
/**
* Generate a human-readable summary of the UI structure
*/
static generateSummary(structure) {
let summary = '=== UI Structure Summary ===\n\n';
if (structure.currentPage) {
summary += `Current Page: ${structure.currentPage.title}\n`;
summary += `URL: ${structure.currentPage.url}\n\n`;
if (structure.currentPage.primaryAction) {
summary += `Primary Action: ${structure.currentPage.primaryAction.label}\n`;
summary += ` Location: (${Math.round(structure.currentPage.primaryAction.bounds.x)}, ${Math.round(structure.currentPage.primaryAction.bounds.y)})\n\n`;
}
}
summary += `Interactable Elements: ${structure.interactables.length}\n`;
for (const element of structure.interactables) {
summary += ` - ${element.type}: "${element.label}" at (${Math.round(element.bounds.x)}, ${Math.round(element.bounds.y)})\n`;
}
if (structure.forms.length > 0) {
summary += `\nForms: ${structure.forms.length}\n`;
for (const form of structure.forms) {
summary += ` Form ${form.id}:\n`;
summary += ` Fields: ${form.fields.length}\n`;
if (form.submitButton) {
summary += ` Submit: "${form.submitButton.label}"\n`;
}
}
}
return summary;
}
}
//# sourceMappingURL=flutter-structure-mapper.js.map