import { useState, useRef } from 'react'; import { createRoot } from 'react-dom/client'; import { RemoteControl, RemoteControlHandle } from './components/remote-control'; import { AxSnapshot } from './core/ax-tree'; type InspectChoice = 'off' | 'hover-only' | 'select'; // Pre-fill from query string so the developer testing the demo can deeplink // `?url=...&token=...&inspect=select&autoconnect=1`. Nothing is persisted. const initialParams = new URLSearchParams(window.location.search); const initialUrl = initialParams.get('url') || 'ws://localhost:8833/signaling'; const initialToken = initialParams.get('token') || 'token'; const initialPlatformParam = initialParams.get('platform'); const initialPlatform: 'ios' | 'android' = initialPlatformParam === 'android' ? 'android' : 'ios'; const initialInspect = (initialParams.get('inspect') as InspectChoice | null) ?? 'off'; const initialAutoconnect = initialParams.get('autoconnect') === '1' && initialParams.has('url') && initialParams.has('token'); function Demo() { const [url, setUrl] = useState(initialUrl); const [token, setToken] = useState(initialToken); const [platform, setPlatform] = useState<'ios' | 'android'>(initialPlatform); const [isConnected, setIsConnected] = useState(initialAutoconnect); const [key, setKey] = useState(0); const [showDebugInfo, setShowDebugInfo] = useState(false); const [inspectChoice, setInspectChoice] = useState(initialInspect); const [latestSnapshot, setLatestSnapshot] = useState(null); const [latestSelection, setLatestSelection] = useState(null); const remoteControlRef = useRef(null); const inspectModeProp: boolean | 'hover-only' | undefined = inspectChoice === 'off' ? undefined : inspectChoice === 'hover-only' ? 'hover-only' : true; const handleConnect = () => { if (url) { setIsConnected(true); // Force remount by changing key setKey((prev) => prev + 1); } }; const handleDisconnect = () => { setIsConnected(false); setKey((prev) => prev + 1); }; const handleScreenshot = async () => { if (remoteControlRef.current) { try { const screenshot = await remoteControlRef.current.screenshot(); // Open screenshot in new window const win = window.open(); if (win) { win.document.write(``); } } catch (error) { console.error('Screenshot failed:', error); alert('Screenshot failed: ' + (error as Error).message); } } }; return ( <>

📱 RemoteControl Component Demo

Test the iOS device frame and remote control features

ℹ️ How to Use:

Enter your WebSocket URL and authentication token below, select iOS or Android platform, then click Connect to see the remote control in action. The iOS platform will display a realistic iPhone frame around the stream.

✨ iOS Feature: Touches can start from the bottom bezel area (below the screen, near the home indicator) to enable authentic iOS swipe-up gestures for going home or switching apps!

setUrl(e.target.value)} placeholder="wss://your-instance.limrun.com/control" disabled={isConnected} />
setToken(e.target.value)} placeholder="Enter your token" disabled={isConnected} />
{!isConnected ? : <> }
{showDebugInfo && platform === 'ios' && (

🔧 iOS Extended Touch Area

The iOS frame includes a 60-pixel extended touch area below the visible screen. This area (where the home indicator is located) can receive touch events and sends coordinates beyond the screen bounds (y > screenHeight), allowing iOS to properly detect gestures that start from outside the screen - just like on a real iPhone. Try starting a swipe gesture from the home indicator area!

)} {isConnected ? <>

{platform === 'ios' ? '📱 iOS with Frame' : '🤖 Android (No Frame)'}

setLatestSelection( sel ? `${sel.element.role || sel.element.type} · ${sel.element.label || '(no label)'}` : null, ) } />
{inspectChoice !== 'off' && (
Inspect debug (demo-only) elements: {latestSnapshot?.elements.length ?? 0} · screen:{' '} {latestSnapshot ? `${latestSnapshot.screen.width}×${latestSnapshot.screen.height}` : '—'}{' '} · platform: {latestSnapshot?.platform ?? '—'} · selection: {latestSelection ?? '(none)'}
)} :
Enter your connection details above and click Connect to start
}
); } // Mount the demo app const container = document.getElementById('root'); if (container) { const root = createRoot(container); root.render(); }