diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 02450af..904d954 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { - "recommendations": [ - "expo.vscode-expo-tools", - "gruntfuggly.todo-tree", - "eamodio.gitlens" - ] + "recommendations": [ + "expo.vscode-expo-tools", + "gruntfuggly.todo-tree", + "eamodio.gitlens" + ] } diff --git a/src/app/index.tsx b/src/app/index.tsx index 096d446..bc594e3 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -37,7 +37,8 @@ export default function Index() { // console.log(data) // }) await sshClientConnection.startShell(PtyType.XTERM) - const sshConn = sshConnectionManager.addSession({ + // const sshConn = + sshConnectionManager.addSession({ client: sshClientConnection, }) router.push(`/shell`) diff --git a/src/app/shell.tsx b/src/app/shell.tsx index d2303f7..a0e2b18 100644 --- a/src/app/shell.tsx +++ b/src/app/shell.tsx @@ -1,8 +1,16 @@ /** * This is the page that is shown after an ssh connection */ -import { useEffect, useState } from 'react' -import { Text, View } from 'react-native' +import { useEffect, useRef, useState } from 'react' +import { + Platform, + Pressable, + ScrollView, + StyleSheet, + Text, + TextInput, + View, +} from 'react-native' import { sshConnectionManager } from '../lib/ssh-connection-manager' export default function Shell() { @@ -15,6 +23,7 @@ export default function Shell() { useEffect(() => { sshConn.client.on('Shell', (data) => { + console.log('Received data (on Shell):', data) setShellData((prev) => prev + data) }) // return () => { @@ -22,9 +31,132 @@ export default function Shell() { // } }, [setShellData, sshConn.client]) + const scrollViewRef = useRef(null) + + useEffect(() => { + // Auto-scroll to bottom when new data arrives + scrollViewRef.current?.scrollToEnd({ animated: true }) + }, [shellData]) + return ( - - {shellData} + + SSH Shell + + + + {shellData || 'Connected. Output will appear here...'} + + + + { + console.log('Executing command:', command) + sshConn.client.writeToShell(command + '\n') + }} + /> ) } + +function CommandInput(props: { executeCommand: (command: string) => void }) { + const [command, setCommand] = useState('') + + function handleExecute() { + if (!command.trim()) return + props.executeCommand(command) + setCommand('') + } + + return ( + + + + Execute + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#0B1324', + padding: 16, + }, + title: { + color: '#E5E7EB', + fontSize: 18, + fontWeight: '700', + marginBottom: 12, + }, + terminal: { + flex: 1, + backgroundColor: '#0E172B', + borderRadius: 12, + borderWidth: 1, + borderColor: '#2A3655', + overflow: 'hidden', + marginBottom: 12, + }, + terminalContent: { + padding: 12, + }, + terminalText: { + color: '#D1D5DB', + fontSize: 14, + lineHeight: 18, + fontFamily: Platform.select({ + ios: 'Menlo', + android: 'monospace', + default: 'monospace', + }), + }, + commandBar: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + commandInput: { + flex: 1, + backgroundColor: '#0E172B', + borderWidth: 1, + borderColor: '#2A3655', + borderRadius: 10, + paddingHorizontal: 12, + paddingVertical: 12, + color: '#E5E7EB', + fontSize: 16, + fontFamily: Platform.select({ + ios: 'Menlo', + android: 'monospace', + default: 'monospace', + }), + }, + executeButton: { + backgroundColor: '#2563EB', + borderRadius: 10, + paddingHorizontal: 16, + paddingVertical: 12, + alignItems: 'center', + justifyContent: 'center', + }, + executeButtonText: { + color: '#FFFFFF', + fontWeight: '700', + fontSize: 14, + }, +})