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

218 lines • 8.3 kB
/** * Flutter Canvas Inspector * Uses Flutter's built-in debug capabilities to inspect CanvasKit rendering */ export class FlutterCanvasInspector { /** * Enable Flutter's debug paint modes */ static async enableDebugPaint(page) { await page.evaluate(() => { // Try to access Flutter's debug flags through various methods // Method 1: Through window.flutter if (window.flutter) { const flutter = window.flutter; if (flutter.debugPaintSizeEnabled !== undefined) { flutter.debugPaintSizeEnabled = true; } } // Method 2: Through global debug flags if (window.debugPaintSizeEnabled !== undefined) { window.debugPaintSizeEnabled = true; } // Method 3: Through Flutter's internal APIs const flutterInternals = [ '_flutter', 'flutter_internalWebGLVersion', 'flutterCanvasKit', '__flutter__' ]; for (const prop of flutterInternals) { if (window[prop]) { // console.log(`Found Flutter internal: ${prop}`); } } }); } /** * Try to access Flutter's render tree */ static async getRenderTree(page) { return await page.evaluate(() => { // Look for Flutter's render tree in various locations const possiblePaths = [ 'window.flutter.renderTree', 'window._flutter.renderTree', 'window.flutterRenderer', 'document.querySelector("flutter-view")?.renderTree' ]; for (const path of possiblePaths) { try { const result = eval(path); if (result) return result; } catch (e) { // Continue searching } } return null; }); } /** * Enable semantics if not already enabled */ static async enableSemantics(page) { return await page.evaluate(() => { // Try to enable semantics through various methods // Method 1: Click the accessibility button if present const accessibilityButton = document.querySelector('[aria-label*="accessibility" i]'); if (accessibilityButton) { accessibilityButton.click(); return true; } // Method 2: Through Flutter APIs if (window.flutter?.semanticsEnabled !== undefined) { window.flutter.semanticsEnabled = true; return true; } // Method 3: Dispatch custom event window.dispatchEvent(new CustomEvent('flutter-enable-semantics')); return false; }); } /** * Get Flutter widget inspector data if available */ static async getWidgetInspectorData(page) { return await page.evaluate(() => { // Try to access widget inspector service const inspectorPaths = [ 'window.flutter?.widgetInspectorService', 'window._widgetInspectorService', 'window.flutterWidgetInspector' ]; for (const path of inspectorPaths) { try { const inspector = eval(path); if (inspector) { return { available: true, methods: Object.getOwnPropertyNames(inspector).filter(prop => typeof inspector[prop] === 'function') }; } } catch (e) { // Continue searching } } return { available: false }; }); } /** * Inject a custom overlay to visualize Flutter elements */ static async injectDebugOverlay(page) { await page.evaluate(() => { // Create overlay canvas const overlay = document.createElement('canvas'); overlay.id = 'flutter-debug-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 999999; `; document.body.appendChild(overlay); // Set up canvas context const ctx = overlay.getContext('2d'); if (!ctx) return; overlay.width = window.innerWidth; overlay.height = window.innerHeight; // Function to draw semantic node bounds const drawSemanticBounds = () => { ctx.clearRect(0, 0, overlay.width, overlay.height); const semanticNodes = document.querySelectorAll('[role], [aria-label], flt-semantics'); semanticNodes.forEach((node) => { const rect = node.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { // Draw bounding box ctx.strokeStyle = 'red'; ctx.lineWidth = 2; ctx.strokeRect(rect.x, rect.y, rect.width, rect.height); // Draw label const label = node.getAttribute('aria-label') || node.getAttribute('role') || ''; if (label) { ctx.fillStyle = 'red'; ctx.font = '12px Arial'; ctx.fillText(label, rect.x, rect.y - 5); } } }); }; // Update overlay on mutations const observer = new MutationObserver(drawSemanticBounds); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['aria-label', 'role'] }); // Initial draw drawSemanticBounds(); }); } /** * Try to hook into Flutter's rendering pipeline */ static async hookRenderingPipeline(page) { await page.evaluate(() => { // Attempt to intercept Flutter's requestAnimationFrame calls const originalRAF = window.requestAnimationFrame; let frameCount = 0; window.requestAnimationFrame = function (callback) { frameCount++; // Wrap the callback to intercept render frames const wrappedCallback = (timestamp) => { // console.log(`Flutter frame ${frameCount} at ${timestamp}`); // Try to capture render tree state if (window.flutter?.getRenderTree) { const renderTree = window.flutter.getRenderTree(); // console.log('Render tree:', renderTree); } // Call original callback callback(timestamp); }; return originalRAF.call(window, wrappedCallback); }; }); } /** * Get comprehensive Flutter debug info */ static async getDebugInfo(page) { const info = { debugPaintEnabled: false, semanticsEnabled: false }; // Check debug paint status info.debugPaintEnabled = await page.evaluate(() => { return window.debugPaintSizeEnabled === true || window.flutter?.debugPaintSizeEnabled === true; }); // Check semantics status info.semanticsEnabled = await page.evaluate(() => { return document.querySelectorAll('flt-semantics').length > 0; }); // Try to get render tree info.renderTree = await this.getRenderTree(page); // Try to get widget inspector data info.widgetTree = await this.getWidgetInspectorData(page); return info; } } //# sourceMappingURL=flutter-canvas-inspector.js.map