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
433 lines • 19.2 kB
JavaScript
/**
* Flutter Platform Integration Module
*
* Handles platform channels, browser compatibility, asset loading, and Flutter web-specific features.
* This module provides comprehensive platform integration insights and issue detection.
*/
export class FlutterPlatformIntegration {
page;
platformChannels = new Map();
gestureConflicts = [];
async attachToPage(page) {
this.page = page;
await this.setupPlatformChannelMonitoring();
await this.setupGestureTracking();
}
async getBrowserCompatibility() {
if (!this.page) {
return {
browser: 'unknown',
version: 'unknown',
flutterSupport: 'none',
missingFeatures: [],
performanceIssues: [],
recommendations: []
};
}
try {
const compatibility = await this.page.evaluate(() => {
const userAgent = navigator.userAgent;
let browser = 'unknown';
let version = 'unknown';
// Detect browser
if (userAgent.includes('Chrome')) {
browser = 'Chrome';
const match = userAgent.match(/Chrome\/(\d+\.\d+)/);
version = match ? match[1] : 'unknown';
}
else if (userAgent.includes('Firefox')) {
browser = 'Firefox';
const match = userAgent.match(/Firefox\/(\d+\.\d+)/);
version = match ? match[1] : 'unknown';
}
else if (userAgent.includes('Safari')) {
browser = 'Safari';
const match = userAgent.match(/Version\/(\d+\.\d+)/);
version = match ? match[1] : 'unknown';
}
else if (userAgent.includes('Edge')) {
browser = 'Edge';
const match = userAgent.match(/Edge\/(\d+\.\d+)/);
version = match ? match[1] : 'unknown';
}
// Assess Flutter support
const hasCanvasKit = !!window.CanvasKit;
const hasWebAssembly = typeof WebAssembly !== 'undefined';
const hasWebGL = !!document.createElement('canvas').getContext('webgl');
const hasIntersectionObserver = 'IntersectionObserver' in window;
const hasResizeObserver = 'ResizeObserver' in window;
let flutterSupport = 'none';
const missingFeatures = [];
const performanceIssues = [];
const recommendations = [];
if (hasWebAssembly && hasWebGL) {
flutterSupport = 'full';
}
else if (hasWebAssembly || hasWebGL) {
flutterSupport = 'partial';
}
// Check for missing features
if (!hasWebAssembly) {
missingFeatures.push('WebAssembly');
recommendations.push('Update to a modern browser that supports WebAssembly');
}
if (!hasWebGL) {
missingFeatures.push('WebGL');
recommendations.push('Enable hardware acceleration in browser settings');
}
if (!hasIntersectionObserver) {
missingFeatures.push('IntersectionObserver');
recommendations.push('Consider using polyfills for older browsers');
}
if (!hasResizeObserver) {
missingFeatures.push('ResizeObserver');
}
// Browser-specific performance issues
if (browser === 'Safari') {
performanceIssues.push('Safari has known issues with WebAssembly performance');
recommendations.push('Consider using HTML renderer for Safari users');
}
if (browser === 'Firefox') {
performanceIssues.push('Firefox may have slower CanvasKit rendering');
recommendations.push('Monitor performance metrics on Firefox');
}
return {
browser,
version,
flutterSupport,
missingFeatures,
performanceIssues,
recommendations
};
});
return compatibility || {
browser: 'unknown',
version: 'unknown',
flutterSupport: 'none',
missingFeatures: [],
performanceIssues: [],
recommendations: []
};
}
catch (error) {
return {
browser: 'unknown',
version: 'unknown',
flutterSupport: 'none',
missingFeatures: [],
performanceIssues: [],
recommendations: []
};
}
}
async analyzeAssetLoading() {
if (!this.page) {
return this.getDefaultAssetAnalysis();
}
try {
const analysis = await this.page.evaluate(() => {
const resources = performance.getEntriesByType('resource');
const assets = [];
const fonts = [];
resources.forEach(resource => {
const name = resource.name.split('/').pop() || resource.name;
const size = resource.transferSize || 0;
const loadTime = resource.duration;
const cached = resource.transferSize === 0 && resource.decodedBodySize > 0;
if (resource.name.includes('assets/') || resource.name.includes('images/')) {
const optimized = resource.name.includes('.webp') ||
resource.name.includes('.avif') ||
(resource.name.includes('.png') && size < 50000);
assets.push({
name,
size,
loadTime,
cached,
optimized
});
}
else if (resource.name.includes('.ttf') ||
resource.name.includes('.otf') ||
resource.name.includes('.woff') ||
resource.name.includes('fonts/')) {
fonts.push({
name,
size,
loadTime,
cached
});
}
});
const totalAssets = assets.length + fonts.length;
const totalSize = assets.reduce((sum, asset) => sum + asset.size, 0) +
fonts.reduce((sum, font) => sum + font.size, 0);
const totalLoadTime = Math.max(...assets.map(asset => asset.loadTime), ...fonts.map(font => font.loadTime));
const unoptimizedAssets = assets.filter(asset => !asset.optimized).length;
return {
assets,
fonts,
totalAssets,
totalSize,
totalLoadTime: isFinite(totalLoadTime) ? totalLoadTime : 0,
unoptimizedAssets
};
});
return analysis || this.getDefaultAssetAnalysis();
}
catch (error) {
return this.getDefaultAssetAnalysis();
}
}
async detectFlutterWebIssues() {
if (!this.page) {
return [];
}
try {
const issues = await this.page.evaluate(() => {
const detectedIssues = [];
const userAgent = navigator.userAgent;
// Check for browser-specific issues
if (userAgent.includes('Safari')) {
detectedIssues.push({
type: 'rendering',
severity: 'warning',
browser: 'Safari',
description: 'Safari may have text rendering inconsistencies with CanvasKit',
solution: 'Consider using HTML renderer for Safari users',
codeExample: 'flutter build web --web-renderer html'
});
detectedIssues.push({
type: 'performance',
severity: 'warning',
browser: 'Safari',
description: 'Safari WebAssembly performance may be slower than other browsers',
solution: 'Monitor performance metrics and consider HTML renderer fallback'
});
}
if (userAgent.includes('Firefox')) {
detectedIssues.push({
type: 'performance',
severity: 'info',
browser: 'Firefox',
description: 'Firefox may have slower CanvasKit rendering performance',
solution: 'Test performance on Firefox and optimize accordingly'
});
}
// Check for mobile issues
if (/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)) {
detectedIssues.push({
type: 'gesture',
severity: 'warning',
description: 'Mobile gesture handling may conflict with browser gestures',
solution: 'Test gesture interactions thoroughly on mobile devices',
codeExample: 'Use GestureDetector with proper hit testing'
});
detectedIssues.push({
type: 'performance',
severity: 'warning',
description: 'Mobile devices may have performance limitations',
solution: 'Optimize for mobile performance and consider device-specific builds'
});
}
// Check for input issues
const hasTextInput = document.querySelector('input, textarea, [contenteditable]');
if (hasTextInput) {
detectedIssues.push({
type: 'input',
severity: 'info',
description: 'Text input handling may vary across browsers',
solution: 'Test text input functionality across different browsers',
codeExample: 'Use TextFormField with proper validation'
});
}
// Check for font loading issues
const customFonts = [];
try {
if (document.fonts && typeof document.fonts.forEach === 'function') {
document.fonts.forEach((font) => {
if (!['Arial', 'Helvetica', 'Times', 'Courier'].includes(font.family)) {
customFonts.push(font);
}
});
}
}
catch (error) {
// Fallback if forEach method is not available
}
if (customFonts.length > 0) {
detectedIssues.push({
type: 'font',
severity: 'info',
description: 'Custom fonts may cause layout shifts during loading',
solution: 'Use font-display: swap and preload critical fonts',
codeExample: 'Add font preloading to index.html'
});
}
return detectedIssues;
});
return issues || [];
}
catch (error) {
return [];
}
}
async getFlutterHealthCheck() {
if (!this.page) {
return {
overall: 'critical',
checks: [],
warnings: [],
errors: ['No page attached']
};
}
try {
const healthCheck = await this.page.evaluate(() => {
const checks = [];
const warnings = [];
const errors = [];
// Check Flutter initialization
const hasFlutter = !!window._flutter || !!window.flutter;
checks.push({
category: 'initialization',
status: hasFlutter ? 'pass' : 'fail',
message: hasFlutter ? 'Flutter framework detected' : 'Flutter framework not detected'
});
if (!hasFlutter) {
errors.push('Flutter framework not properly initialized');
}
// Check renderer
const hasCanvasKit = !!window.CanvasKit;
const hasHtmlRenderer = document.querySelector('flt-scene') !== null;
const hasRenderer = hasCanvasKit || hasHtmlRenderer;
checks.push({
category: 'renderer',
status: hasRenderer ? 'pass' : 'fail',
message: hasRenderer ?
`Renderer: ${hasCanvasKit ? 'CanvasKit' : 'HTML'}` :
'No renderer detected'
});
if (!hasRenderer) {
errors.push('No Flutter renderer detected');
}
// Check performance
const performanceEntries = performance.getEntriesByType('navigation');
const loadTime = performanceEntries.length > 0 ?
performanceEntries[0].loadEventEnd -
performanceEntries[0].fetchStart : 0;
checks.push({
category: 'performance',
status: loadTime < 3000 ? 'pass' : loadTime < 5000 ? 'warning' : 'fail',
message: `Load time: ${loadTime}ms`
});
if (loadTime > 3000) {
warnings.push(`Slow load time: ${loadTime}ms`);
}
// Check memory
const memoryInfo = performance.memory;
if (memoryInfo) {
const memoryUsage = memoryInfo.usedJSHeapSize / 1024 / 1024; // MB
checks.push({
category: 'memory',
status: memoryUsage < 50 ? 'pass' : memoryUsage < 100 ? 'warning' : 'fail',
message: `Memory usage: ${memoryUsage.toFixed(1)}MB`
});
if (memoryUsage > 50) {
warnings.push(`High memory usage: ${memoryUsage.toFixed(1)}MB`);
}
}
// Check for errors
const flutterDebug = window.__FLUTTER_DEBUG__;
if (flutterDebug && flutterDebug.errors && flutterDebug.errors.length > 0) {
errors.push(...flutterDebug.errors);
}
// Determine overall status
const hasErrors = errors.length > 0 || checks.some(check => check.status === 'fail');
const hasWarnings = warnings.length > 0 || checks.some(check => check.status === 'warning');
const overall = hasErrors ? 'critical' : hasWarnings ? 'warning' : 'healthy';
return {
overall,
checks,
warnings,
errors
};
});
return healthCheck || {
overall: 'critical',
checks: [],
warnings: [],
errors: ['Health check failed']
};
}
catch (error) {
return {
overall: 'critical',
checks: [],
warnings: [],
errors: ['Health check failed']
};
}
}
async setupPlatformChannelMonitoring() {
if (!this.page)
return;
await this.page.evaluate(() => {
const flutterDebug = window.__FLUTTER_DEBUG__;
// Monitor platform channel messages
const originalPostMessage = window.postMessage;
window.postMessage = function (message, targetOriginOrOptions) {
if (message && message.method && message.channel) {
if (!flutterDebug.platformChannels.has(message.channel)) {
flutterDebug.platformChannels.set(message.channel, {
messageCount: 0,
lastMessage: null,
lastError: null
});
}
const channelInfo = flutterDebug.platformChannels.get(message.channel);
channelInfo.messageCount++;
channelInfo.lastMessage = message;
}
return originalPostMessage.call(this, message, targetOriginOrOptions);
};
});
}
async setupGestureTracking() {
if (!this.page)
return;
await this.page.evaluate(() => {
const flutterDebug = window.__FLUTTER_DEBUG__;
// Track gesture conflicts
const gestureEvents = ['touchstart', 'touchmove', 'touchend', 'click', 'scroll'];
gestureEvents.forEach(eventType => {
document.addEventListener(eventType, (event) => {
// Check if event is in Flutter area
const flutterArea = document.querySelector('flt-glass-pane');
if (flutterArea && flutterArea.contains(event.target)) {
const conflict = {
type: eventType,
timestamp: Date.now(),
target: event.target?.tagName || 'unknown',
preventDefault: event.defaultPrevented
};
flutterDebug.gestures.conflicts.push(conflict);
// Keep only recent conflicts
if (flutterDebug.gestures.conflicts.length > 100) {
flutterDebug.gestures.conflicts.shift();
}
}
}, true);
});
});
}
getDefaultAssetAnalysis() {
return {
assets: [],
fonts: [],
totalAssets: 0,
totalSize: 0,
totalLoadTime: 0,
unoptimizedAssets: 0
};
}
}
//# sourceMappingURL=flutter-platform-integration.js.map