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