Claude-skills-journalism mobile-debugging
Remote JavaScript console access and debugging on mobile devices. Use when debugging web pages on phones/tablets, accessing console errors without desktop DevTools, testing responsive designs on real devices, or diagnosing mobile-specific issues. Covers Eruda, vConsole, Chrome/Safari remote debugging, and cloud testing platforms.
git clone https://github.com/jamditis/claude-skills-journalism
T=$(mktemp -d) && git clone --depth=1 https://github.com/jamditis/claude-skills-journalism "$T" && mkdir -p ~/.claude/skills && cp -r "$T/mobile-debugging" ~/.claude/skills/jamditis-claude-skills-journalism-mobile-debugging && rm -rf "$T"
mobile-debugging/SKILL.mdMobile debugging methodology
Patterns for accessing JavaScript console and debugging web pages on mobile devices without traditional desktop DevTools.
Quick-start: Inject console on any page
Eruda bookmarklet (recommended)
Add this as a bookmark on your mobile browser, then tap it on any page:
javascript:(function(){var script=document.createElement('script');script.src='https://cdn.jsdelivr.net/npm/eruda';document.body.append(script);script.onload=function(){eruda.init();}})();
vConsole bookmarklet
javascript:(function(){var script=document.createElement('script');script.src='https://unpkg.com/vconsole@latest/dist/vconsole.min.js';document.body.append(script);script.onload=function(){new VConsole();}})();
In-page console tools
Eruda setup
Eruda provides a full DevTools-like experience in a floating panel.
<!-- CDN (development only) --> <script src="https://cdn.jsdelivr.net/npm/eruda"></script> <script>eruda.init();</script> <!-- Conditional loading (recommended for production) --> <script> (function() { var src = 'https://cdn.jsdelivr.net/npm/eruda'; // Only load when ?eruda=true or localStorage flag set if (!/eruda=true/.test(window.location) && localStorage.getItem('active-eruda') !== 'true') return; var script = document.createElement('script'); script.src = src; script.onload = function() { eruda.init(); }; document.body.appendChild(script); })(); </script>
// NPM installation // npm install eruda --save-dev import eruda from 'eruda'; // Initialize with options eruda.init({ container: document.getElementById('eruda-container'), tool: ['console', 'elements', 'network', 'resources', 'info'], useShadowDom: true, autoScale: true }); // Add custom buttons eruda.add({ name: 'Clear Storage', init($el) { $el.html('<button>Clear All Storage</button>'); $el.find('button').on('click', () => { localStorage.clear(); sessionStorage.clear(); console.log('Storage cleared'); }); } }); // Remove when done eruda.destroy();
Eruda features:
- Console (logs, errors, warnings)
- Elements (DOM inspector)
- Network (XHR/fetch requests)
- Resources (localStorage, cookies, sessionStorage)
- Sources (page source code)
- Info (page/device information)
- Snippets (saved code snippets)
vConsole setup
Lighter weight alternative, official tool for WeChat debugging.
<!-- CDN --> <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script> <script> var vConsole = new VConsole(); </script>
// NPM // npm install vconsole import VConsole from 'vconsole'; // Initialize with options const vConsole = new VConsole({ theme: 'dark', onReady: function() { console.log('vConsole is ready'); }, log: { maxLogNumber: 1000 } }); // Dynamic configuration vConsole.setOption('log.maxLogNumber', 5000); // Destroy when done vConsole.destroy();
vConsole features:
- Log panel (console.log, info, warn, error)
- System panel (device info)
- Network panel (XHR, fetch)
- Element panel (DOM tree)
- Storage panel (cookies, localStorage)
Comparison: Eruda vs vConsole
| Feature | Eruda | vConsole |
|---|---|---|
| Size | ~100KB | ~85KB |
| DOM Editing | Yes | View only |
| Network Details | Full | Basic |
| Plugin System | Yes | Yes |
| Dark Theme | Via plugin | Built-in |
| Best For | Full debugging | Quick logging |
Native remote debugging
Chrome DevTools (Android)
# 1. Enable USB debugging on Android # Settings → Developer Options → USB Debugging = ON # 2. Connect via USB to computer # 3. Open Chrome on computer, navigate to: # chrome://inspect#devices # 4. Enable "Discover USB devices" # 5. Accept debugging prompt on Android device # 6. Click "Inspect" next to the page you want to debug
Port forwarding for localhost:
# In chrome://inspect, click "Port forwarding" # Add: localhost:3000 → localhost:3000 # Now Android Chrome can access your dev server at localhost:3000
Safari Web Inspector (iOS)
# 1. On iPhone/iPad: # Settings → Safari → Advanced → Web Inspector = ON # 2. On Mac: # Safari → Preferences → Advanced → "Show Develop menu" = ON # 3. Connect device via USB (or enable Wi-Fi debugging) # 4. Open Safari on Mac: # Develop → [Device Name] → [Page to debug] # Wireless debugging (after initial USB setup): # Develop → [Device] → Connect via Network
Firefox Remote Debugging (Android)
# 1. On Android Firefox: # Settings → Advanced → Remote debugging = ON # 2. On Desktop Firefox: # Open about:debugging # 3. Connect Android via USB # 4. Enable USB devices in about:debugging # 5. Click "Connect" next to your device
iOS debugging without Mac
Using ios-webkit-debug-proxy
# Install on Windows (via Scoop) scoop bucket add extras scoop install ios-webkit-debug-proxy # Install on Linux sudo apt-get install ios-webkit-debug-proxy # Install on Mac brew install ios-webkit-debug-proxy # Run the proxy ios_webkit_debug_proxy -f chrome-devtools://devtools/bundled/inspector.html # Connect to http://localhost:9221 to see connected devices
Commercial: Inspect.dev
Inspect.dev provides iOS debugging from Windows/Linux with a familiar DevTools interface.
# Download from https://inspect.dev/ # 1. Install application # 2. Connect iOS device via USB # 3. Enable Web Inspector on iOS # 4. Inspect.dev auto-detects pages # 5. Click to open DevTools interface
Cloud testing platforms
LambdaTest (freemium)
# LambdaTest provides real device cloud with console access # Free tier: 100 minutes/month import requests # LambdaTest REST API for automation LAMBDATEST_API = "https://api.lambdatest.com/automation/api/v1" # For manual testing: # 1. Go to https://www.lambdatest.com/ # 2. Select device/browser # 3. Enter URL # 4. DevTools available in toolbar # Selenium/Playwright integration for automated console capture from playwright.sync_api import sync_playwright def test_on_lambdatest(): with sync_playwright() as p: # Connect to LambdaTest browser = p.chromium.connect( f"wss://cdp.lambdatest.com/playwright?capabilities=" f"{{\"browserName\":\"Chrome\",\"platform\":\"android\"}}" ) page = browser.new_page() # Capture console logs logs = [] page.on('console', lambda msg: logs.append(msg.text())) page.goto('https://example.com') browser.close() return logs
BrowserStack
# BrowserStack: $29/month+, 10,000+ real devices from selenium import webdriver from selenium.webdriver.common.desired_capabilities import DesiredCapabilities def get_browserstack_driver(): """Create BrowserStack WebDriver with console logging.""" capabilities = { 'browserName': 'chrome', 'device': 'Samsung Galaxy S21', 'realMobile': 'true', 'os_version': '11.0', 'browserstack.console': 'verbose', # Capture console logs 'browserstack.networkLogs': 'true', 'browserstack.user': 'YOUR_USERNAME', 'browserstack.key': 'YOUR_KEY' } driver = webdriver.Remote( command_executor='https://hub-cloud.browserstack.com/wd/hub', desired_capabilities=capabilities ) return driver # After test, retrieve logs from BrowserStack dashboard or API
Programmatic console capture
Playwright console capture
const { chromium, devices } = require('playwright'); async function captureConsoleLogs(url) { const browser = await chromium.launch(); // Emulate mobile device const context = await browser.newContext({ ...devices['iPhone 13'] }); const page = await context.newPage(); // Capture all console messages const logs = []; page.on('console', msg => { logs.push({ type: msg.type(), text: msg.text(), location: msg.location(), timestamp: new Date().toISOString() }); }); // Capture page errors const errors = []; page.on('pageerror', error => { errors.push({ message: error.message, stack: error.stack, timestamp: new Date().toISOString() }); }); // Capture failed requests const failedRequests = []; page.on('requestfailed', request => { failedRequests.push({ url: request.url(), failure: request.failure().errorText, timestamp: new Date().toISOString() }); }); await page.goto(url); await page.waitForLoadState('networkidle'); await browser.close(); return { logs, errors, failedRequests }; } // Usage captureConsoleLogs('https://example.com') .then(result => console.log(JSON.stringify(result, null, 2)));
Puppeteer console capture
const puppeteer = require('puppeteer'); async function debugMobilePage(url) { const browser = await puppeteer.launch(); const page = await browser.newPage(); // Set mobile viewport await page.setViewport({ width: 375, height: 812, isMobile: true, hasTouch: true }); // Mobile user agent await page.setUserAgent( 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) ' + 'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1' ); // Console capture with full details page.on('console', async msg => { const args = await Promise.all( msg.args().map(arg => arg.jsonValue().catch(() => arg.toString())) ); console.log(`[${msg.type().toUpperCase()}]`, ...args); // Get source location const location = msg.location(); if (location.url) { console.log(` at ${location.url}:${location.lineNumber}`); } }); // Unhandled promise rejections page.on('pageerror', err => { console.error('[PAGE ERROR]', err.message); }); await page.goto(url, { waitUntil: 'networkidle0' }); // Execute JavaScript and capture result const result = await page.evaluate(() => { // Check for common mobile issues return { viewportWidth: window.innerWidth, devicePixelRatio: window.devicePixelRatio, touchSupport: 'ontouchstart' in window, errors: window.__capturedErrors || [] }; }); console.log('Page info:', result); await browser.close(); }
Error monitoring services
Sentry integration
// npm install @sentry/browser import * as Sentry from '@sentry/browser'; Sentry.init({ dsn: 'YOUR_SENTRY_DSN', environment: 'production', // Capture console.error integrations: [ new Sentry.BrowserTracing(), new Sentry.Replay() // Session replay for debugging ], // Sample rates tracesSampleRate: 0.1, replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0, beforeSend(event) { // Filter or modify events return event; } }); // Manual error capture try { riskyOperation(); } catch (error) { Sentry.captureException(error); } // Add context Sentry.setUser({ id: 'user123' }); Sentry.setTag('page', 'checkout');
LogRocket for session replay
// npm install logrocket import LogRocket from 'logrocket'; LogRocket.init('your-app/your-project'); // Identify user LogRocket.identify('user123', { name: 'Test User', email: 'user@example.com' }); // Console logs automatically captured console.log('This appears in LogRocket'); // Manual logging LogRocket.log('Custom event', { data: 'value' }); // Track errors LogRocket.captureException(new Error('Something went wrong'));
Android screen mirroring with Scrcpy
# Install scrcpy # Windows: scoop install scrcpy # Mac: brew install scrcpy # Linux: apt install scrcpy # Basic mirroring scrcpy # With specific options scrcpy --max-size 1024 --bit-rate 2M # Wireless connection (after initial USB) adb tcpip 5555 adb connect <device-ip>:5555 scrcpy # Record session scrcpy --record session.mp4 # Turn off device screen while mirroring scrcpy --turn-screen-off
Mobile debugging workflow
┌─────────────────────────────────────────────────────────────────┐ │ MOBILE DEBUGGING DECISION TREE │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Q: Do you have physical access to the device? │ │ │ │ │ ├─ YES: Can you connect via USB? │ │ │ │ │ │ │ ├─ Android: Use Chrome DevTools Remote │ │ │ │ chrome://inspect#devices │ │ │ │ │ │ │ └─ iOS: Have a Mac? │ │ │ │ │ │ │ ├─ YES: Use Safari Web Inspector │ │ │ │ │ │ │ └─ NO: Use Inspect.dev or │ │ │ ios-webkit-debug-proxy │ │ │ │ │ └─ NO USB: Inject Eruda/vConsole via bookmarklet │ │ │ │ Q: Remote/production debugging? │ │ │ │ │ ├─ Add conditional Eruda loading │ │ │ (?eruda=true parameter) │ │ │ │ │ └─ Set up Sentry/LogRocket for error monitoring │ │ │ │ Q: Automated testing? │ │ │ │ │ ├─ Playwright/Puppeteer with mobile emulation │ │ │ │ │ └─ Cloud platforms (LambdaTest, BrowserStack) │ │ │ └─────────────────────────────────────────────────────────────────┘
Common mobile debugging issues
Touch events not firing
// Check if touch events are supported eruda.init(); console.log('Touch support:', 'ontouchstart' in window); console.log('Pointer events:', 'onpointerdown' in window); // Debug touch events document.addEventListener('touchstart', e => { console.log('touchstart', e.touches.length, 'touches'); }, { passive: true }); document.addEventListener('click', e => { console.log('click at', e.clientX, e.clientY); });
Viewport issues
// Log viewport information console.log('Viewport:', { innerWidth: window.innerWidth, innerHeight: window.innerHeight, outerWidth: window.outerWidth, outerHeight: window.outerHeight, devicePixelRatio: window.devicePixelRatio, orientation: screen.orientation?.type }); // Check meta viewport const viewport = document.querySelector('meta[name="viewport"]'); console.log('Viewport meta:', viewport?.content);
Performance debugging
// Check performance timing const perf = performance.getEntriesByType('navigation')[0]; console.log('Page load timing:', { dns: perf.domainLookupEnd - perf.domainLookupStart, tcp: perf.connectEnd - perf.connectStart, request: perf.responseStart - perf.requestStart, response: perf.responseEnd - perf.responseStart, domParsing: perf.domInteractive - perf.responseEnd, domComplete: perf.domComplete - perf.domInteractive, total: perf.loadEventEnd - perf.navigationStart }); // Check memory (Chrome only) if (performance.memory) { console.log('Memory:', { usedJSHeapSize: (performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB', totalJSHeapSize: (performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB' }); }
Platform comparison
| Tool | Cost | Platforms | Setup Difficulty | Best For |
|---|---|---|---|---|
| Eruda | Free | All browsers | Easy (bookmarklet) | Quick debugging |
| vConsole | Free | All browsers | Easy | WeChat apps |
| Chrome Remote | Free | Android only | Medium | Full DevTools |
| Safari Inspector | Free | iOS only | Easy (Mac required) | Full DevTools |
| Inspect.dev | Paid | iOS from any OS | Easy | iOS without Mac |
| LambdaTest | Freemium | All | Easy | Cloud testing |
| BrowserStack | Paid | All | Easy | Real devices |
| Sentry | Freemium | All | Medium | Error monitoring |