import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import useAdb from '@/hooks/useAdb' import { encodeUtf8, type AdbSubprocessProtocol } from '@yume-chan/adb' import { Consumable, WritableStream } from '@yume-chan/stream-extra' import { useEffect, useRef, useState } from 'react' import { Terminal } from 'xterm' import { FitAddon } from 'xterm-addon-fit' import 'xterm/css/xterm.css' export const ShellTab: React.FC = () => { const adb = useAdb(state => state.adb) const shellRef = useRef(null) const [process, setProcess] = useState() useEffect(() => { const startTerminal = async () => { if (shellRef.current && adb) { if (shellRef.current.children.length > 0) { // remove all children from the shellRef while (shellRef.current.firstChild) { shellRef.current.removeChild(shellRef.current.firstChild) } } const terminal: Terminal = new Terminal() const fitAddon = new FitAddon() terminal.loadAddon(fitAddon) const process: AdbSubprocessProtocol = await adb.subprocess.shell( '/data/data/com.termux/files/usr/bin/telnet localhost 45515' ) process.stdout.pipeTo( new WritableStream({ write(chunk) { terminal.write(chunk) } }) ) const writer = process.stdin.getWriter() terminal.onData(data => { const buffer = encodeUtf8(data) const consumable = new Consumable(buffer) writer.write(consumable) }) terminal.options.cursorBlink = true terminal.options.theme = { background: '#1e1e1e', foreground: '#d4d4d4' } terminal.open(shellRef.current) fitAddon.fit() setProcess(process) } } startTerminal() return () => { console.log('cleaning up shell') shellRef.current && shellRef.current.firstChild && shellRef.current.removeChild(shellRef.current.firstChild) process?.stderr.cancel() process?.stdin.close() process?.stdout.cancel() process?.kill() } }, [shellRef, adb]) return ( Shell Access your device's shell using a terminal emulator
) }