import { useState, useEffect } from 'react' import { MonitorSmartphone, Play, Square, Check, AlertTriangle, Camera } from 'lucide-react' import { ScrcpyAvailable, ScrcpyRunning, StartScrcpy, StopScrcpy, CaptureScreenshot } from '../../lib/wails' import { notify } from '../../lib/notify' // Wails runtime is injected on window['runtime'] (same access as ViewLogcat). const rt = () => (window as any)['runtime'] interface Options { maxSize: number bitRateMbps: number maxFps: number stayAwake: boolean turnScreenOff: boolean showTouches: boolean alwaysOnTop: boolean fullscreen: boolean borderless: boolean record: boolean detached: boolean noAudio: boolean viewOnly: boolean videoCodec: string orientation: string } const DEFAULTS: Options = { maxSize: 0, bitRateMbps: 8, maxFps: 60, stayAwake: true, turnScreenOff: false, showTouches: false, alwaysOnTop: false, fullscreen: false, borderless: false, record: false, detached: false, noAudio: false, viewOnly: false, videoCodec: '', orientation: '', } export default function ViewScreenMirror() { const [available, setAvailable] = useState(null) const [missing, setMissing] = useState('') const [running, setRunning] = useState(false) const [starting, setStarting] = useState(false) const [opts, setOpts] = useState(DEFAULTS) useEffect(() => { ScrcpyAvailable().then(setAvailable).catch((e: any) => setMissing(String(e))) ScrcpyRunning().then(setRunning).catch(() => {}) const off = rt()?.EventsOn?.('scrcpy:stopped', () => setRunning(false)) return () => off?.() }, []) const set = (k: K, v: Options[K]) => setOpts(o => ({ ...o, [k]: v })) const start = async () => { setStarting(true) try { await StartScrcpy(opts) setRunning(true) notify.success('Mirror started — the window opens separately and can be moved anywhere') } catch (e: any) { notify.error(e) } finally { setStarting(false) } } const stop = async () => { try { await StopScrcpy() setRunning(false) } catch (e: any) { notify.error(e) } } const screenshot = async () => { try { const path = await CaptureScreenshot() if (path) notify.success(`Saved ${path}`) } catch (e: any) { notify.error(e) } } return (

Screen Mirror

Mirror and control your phone on your computer. The mirror opens in its own window you can move, resize, and snap anywhere — drive the phone with your mouse and keyboard. Powered by scrcpy.

{/* Availability */} {available && (
{available} detected
)} {missing && (

scrcpy isn't installed.

Install it with sudo apt install scrcpy, then reopen this view.

)} {/* Options */}

Options

set('bitRateMbps', v)} options={[[2, '2'], [4, '4'], [8, '8'], [16, '16'], [32, '32']]} /> set('videoCodec', e.target.value)}> {[['', 'Auto'], ['h264', 'H.264'], ['h265', 'H.265'], ['av1', 'AV1']].map(([v, l]) => )}
set('stayAwake', v)} /> set('turnScreenOff', v)} /> set('showTouches', v)} /> set('alwaysOnTop', v)} /> set('borderless', v)} /> set('fullscreen', v)} /> set('noAudio', v)} /> set('viewOnly', v)} /> set('record', v)} />

Keep running after ATK closes

Detaches the mirror — quitting ATK won't close it. It'll show up here again next time you open ATK.

{/* Capture */}

Capture

Saves the phone's current screen as a PNG. Works anytime a device is connected — no mirror needed.

Screen recording: enable “Record to file” above, then Start — a save dialog asks where to save the .mp4 (pick any folder/name). It records the whole session and finalizes the file when you Stop the mirror (or close its window). Perfect for repro clips.

{/* Controls */}
{running ? ( ) : ( )} {running && ● Mirroring — check the separate scrcpy window}

Borderless hides the window's title bar for a clean look. Move it with Super + drag, and close it with Stop mirror above (the phone's own title bar can't be themed by ATK — it's drawn by your window manager).

{/* Shortcut cheat-sheet */}

Controls & shortcuts

MOD = Left Alt or Super (⊞ / ⌘ key) — use these when a laptop has no middle-click.

{SHORTCUTS.map(s => (
{s.action} {s.keys} {s.alt && <>or{s.alt}}
))}
) } const SHORTCUTS: { action: string; keys: string; alt?: string }[] = [ { action: 'Home', keys: 'MOD+H', alt: 'Middle-click' }, { action: 'Back', keys: 'MOD+B', alt: 'Right-click' }, { action: 'Tap', keys: 'Left-click' }, { action: 'Long-press / select', keys: 'Click + hold' }, { action: 'Recent apps', keys: 'MOD+S' }, { action: 'App menu', keys: 'MOD+M' }, { action: 'Notifications', keys: 'MOD+N' }, { action: 'Power', keys: 'MOD+P' }, { action: 'Volume up', keys: 'MOD+↑' }, { action: 'Volume down', keys: 'MOD+↓' }, { action: 'Rotate screen', keys: 'MOD+← / →' }, { action: 'Fullscreen', keys: 'MOD+F' }, { action: 'Phone screen off', keys: 'MOD+O' }, { action: 'Phone screen on', keys: 'MOD+⇧+O' }, { action: 'Copy to computer', keys: 'MOD+C' }, { action: 'Paste to phone', keys: 'MOD+V' }, { action: 'Swipe / gesture', keys: 'Click + drag' }, { action: 'Pinch to zoom', keys: 'Ctrl + drag' }, ] function Kbd({ children }: { children: React.ReactNode }) { return ( {children} ) } function Select({ label, value, onChange, options }: { label: string; value: number; onChange: (v: number) => void; options: [number, string][] }) { return ( ) } function Toggle({ label, on, onChange }: { label: string; on: boolean; onChange: (v: boolean) => void }) { return (
{label}
) }