UNPKG

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
/** * 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