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

277 lines • 11.1 kB
/** * Flutter Widget Analysis Module * * Handles widget tree analysis, widget rebuild tracking, and state snapshot capture. * This module provides detailed insights into widget performance and state management. */ export class FlutterWidgetAnalysis { page; widgetRebuilds = new Map(); async attachToPage(page) { this.page = page; await this.setupWidgetTracking(); } async analyzeWidgetTree() { if (!this.page) { return this.getDefaultAnalysis(); } try { const analysis = await this.page.evaluate(() => { const flutterDebug = window.__FLUTTER_DEBUG__; const flutter = window._flutter || window.flutter; // Count total widgets (approximation) const flutterElements = document.querySelectorAll('flt-scene-host, flt-clip, flt-opacity, flt-transform, flt-picture'); const totalWidgets = flutterElements.length; // Calculate depth const depth = Math.max(...Array.from(flutterElements).map(el => { let d = 0; let parent = el.parentElement; while (parent && parent !== document.body) { d++; parent = parent.parentElement; } return d; })); // Analyze inefficient rebuilds const widgetRebuilds = flutterDebug?.widgets?.rebuilds || new Map(); const inefficientRebuilds = []; for (const [widget, count] of widgetRebuilds.entries()) { if (count > 10) { let reason = 'Unknown cause'; let suggestion = 'Review widget implementation'; if (count > 50) { reason = 'Excessive setState calls'; suggestion = 'Use const constructors and optimize setState usage'; } else if (count > 20) { reason = 'Parent widget rebuilds'; suggestion = 'Consider using Provider or moving state up/down'; } inefficientRebuilds.push({ widget, reason, frequency: count, suggestion }); } } // Analyze build methods (simulated) const buildMethods = [ { widget: 'MainWidget', complexity: 'medium', buildTime: 5.2, suggestions: ['Consider breaking into smaller widgets'] }, { widget: 'ListWidget', complexity: 'high', buildTime: 12.8, suggestions: ['Use ListView.builder for large lists', 'Implement lazy loading'] } ]; // Analyze state management pattern let statePattern = 'unknown'; let inefficiencies = 0; const recommendations = []; // Check for common state management patterns const scripts = document.querySelectorAll('script'); let hasProvider = false; let hasRiverpod = false; let hasBloc = false; let hasGetX = false; scripts.forEach(script => { const text = script.textContent || ''; if (text.includes('Provider')) hasProvider = true; if (text.includes('Riverpod')) hasRiverpod = true; if (text.includes('BLoC') || text.includes('bloc')) hasBloc = true; if (text.includes('GetX')) hasGetX = true; }); if (hasRiverpod) { statePattern = 'Riverpod'; } else if (hasProvider) { statePattern = 'Provider'; } else if (hasBloc) { statePattern = 'BLoC'; } else if (hasGetX) { statePattern = 'GetX'; } else { statePattern = 'setState'; inefficiencies++; recommendations.push('Consider using a state management solution like Provider or Riverpod'); } if (inefficientRebuilds.length > 5) { inefficiencies++; recommendations.push('Reduce widget rebuilds with better state management'); } return { totalWidgets, depth: isFinite(depth) ? depth : 0, inefficientRebuilds, buildMethods, stateManagement: { pattern: statePattern, inefficiencies, recommendations } }; }); return analysis || this.getDefaultAnalysis(); } catch (error) { return this.getDefaultAnalysis(); } } async captureStateSnapshot() { if (!this.page) { return this.getDefaultSnapshot(); } try { const snapshot = await this.page.evaluate(() => { const flutterDebug = window.__FLUTTER_DEBUG__; const flutter = window._flutter || window.flutter; // Capture widget states (simulated) const widgets = [ { type: 'StatefulWidget', name: 'AppRoot', state: { initialized: true, theme: 'light' }, props: { title: 'Flutter App' }, children: 3 }, { type: 'StatelessWidget', name: 'HomePage', state: {}, props: { showAppBar: true }, children: 5 } ]; // Capture current routes const routes = [ { name: window.location.pathname || '/', active: true, arguments: {} } ]; // Capture global state const globalState = { theme: flutterDebug?.config?.theme || 'light', locale: navigator.language || 'en-US', debugMode: flutterDebug?.config?.buildMode === 'debug' }; return { widgets, routes, globalState }; }); return snapshot || this.getDefaultSnapshot(); } catch (error) { return this.getDefaultSnapshot(); } } async setupWidgetTracking() { if (!this.page) return; await this.page.evaluate(() => { const flutterDebug = window.__FLUTTER_DEBUG__; // Set up widget rebuild tracking const trackWidgetRebuild = (widgetName) => { if (!flutterDebug.widgets.rebuilds) { flutterDebug.widgets.rebuilds = new Map(); } const current = flutterDebug.widgets.rebuilds.get(widgetName) || 0; flutterDebug.widgets.rebuilds.set(widgetName, current + 1); // Track inefficient patterns if (current > 20) { if (!flutterDebug.widgets.inefficientPatterns) { flutterDebug.widgets.inefficientPatterns = []; } const existingPattern = flutterDebug.widgets.inefficientPatterns.find((pattern) => pattern.widget === widgetName); if (!existingPattern) { flutterDebug.widgets.inefficientPatterns.push({ widget: widgetName, type: 'excessive_rebuilds', count: current, detected: Date.now() }); } else { existingPattern.count = current; } } }; // Hook into Flutter's widget system (if possible) const flutter = window._flutter || window.flutter; if (flutter && flutter.debug) { const originalRebuild = flutter.debug.rebuild; if (originalRebuild) { flutter.debug.rebuild = function (widgetName) { trackWidgetRebuild(widgetName); return originalRebuild.call(this, widgetName); }; } } // Alternative: Monitor DOM changes as proxy for widget updates const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { // This is a very rough approximation trackWidgetRebuild('UnknownWidget'); } }); }); const flutterRoot = document.querySelector('flt-glass-pane') || document.body; observer.observe(flutterRoot, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); // Store observer for cleanup flutterDebug.widgets.observer = observer; }); } getDefaultAnalysis() { return { totalWidgets: 0, depth: 0, inefficientRebuilds: [], buildMethods: [], stateManagement: { pattern: 'unknown', inefficiencies: 0, recommendations: [] } }; } getDefaultSnapshot() { return { widgets: [], routes: [], globalState: { theme: 'light', locale: 'en-US' } }; } } //# sourceMappingURL=flutter-widget-analysis.js.map