153 lines
4.1 KiB
TypeScript

import React, { ComponentPropsWithoutRef } from "react";
import XTermJs, { XTermRef } from "./xtermjs";
import Ionicons from "@expo/vector-icons/Ionicons";
import {
Pressable,
ScrollView,
StyleProp,
StyleSheet,
Text,
TextStyle,
View,
} from "react-native";
const Keys = {
ArrowLeft: "\x1b[D",
ArrowRight: "\x1b[C",
ArrowUp: "\x1b[A",
ArrowDown: "\x1b[B",
Enter: "\x0D",
Escape: "\x1b",
Home: "\x1b[H",
End: "\x1b[F",
PageUp: "\x1b[5~",
PageDown: "\x1b[6~",
Alt: "\x1b",
Tab: "\x09",
};
type XTermJsProps = {
client?: "xtermjs";
wsUrl: string;
};
type TerminalProps = ComponentPropsWithoutRef<typeof View> & XTermJsProps;
const Terminal = ({ client = "xtermjs", style, ...props }: TerminalProps) => {
const xtermRef = React.useRef<XTermRef>(null);
const send = (data: string) => {
switch (client) {
case "xtermjs":
xtermRef.current?.send(data);
break;
}
};
return (
<View style={[styles.container, style]} {...props}>
{client === "xtermjs" && (
<XTermJs
ref={xtermRef}
dom={{ scrollEnabled: false }}
wsUrl={props.wsUrl}
/>
)}
<ScrollView
horizontal
style={{ flexGrow: 0 }}
contentContainerStyle={styles.buttons}
>
<TerminalButton
title={<Ionicons name="swap-horizontal" color="white" size={16} />}
onPress={() => send(Keys.Tab)}
/>
<TerminalButton title="ESC" onPress={() => send(Keys.Escape)} />
<TerminalButton
title={<Ionicons name="home" color="white" size={16} />}
onPress={() => send(Keys.Home)}
/>
<TerminalButton
title={<Ionicons name="arrow-back" color="white" size={18} />}
onPress={() => send(Keys.ArrowLeft)}
/>
<TerminalButton
title={<Ionicons name="arrow-up" color="white" size={18} />}
onPress={() => send(Keys.ArrowUp)}
/>
<TerminalButton
title={<Ionicons name="arrow-down" color="white" size={18} />}
onPress={() => send(Keys.ArrowDown)}
/>
<TerminalButton
title={<Ionicons name="arrow-forward" color="white" size={18} />}
onPress={() => send(Keys.ArrowRight)}
/>
<TerminalButton title="Enter" onPress={() => send(Keys.Enter)} />
<TerminalButton title="End" onPress={() => send(Keys.End)} />
<TerminalButton title="PgUp" onPress={() => send(Keys.PageUp)} />
<TerminalButton title="PgDn" onPress={() => send(Keys.PageDown)} />
{/* <TerminalButton title="Alt" onPress={() => send(Keys.Alt)} /> */}
<TerminalButton title="^C" onPress={() => send("\x03")} />
<TerminalButton title="^D" onPress={() => send("\x04")} />
<TerminalButton title="^Q" onPress={() => send("\x11")} />
<TerminalButton title="^V" onPress={() => send("\x11")} />
<TerminalButton title="^S" onPress={() => send("\x13")} />
<TerminalButton title="^W" onPress={() => send("\x18")} />
<TerminalButton title="^X" onPress={() => send("\x18")} />
<TerminalButton title="^Z" onPress={() => send("\x1a")} />
</ScrollView>
</View>
);
};
const TerminalButton = ({
title,
textStyle,
...props
}: ComponentPropsWithoutRef<typeof Pressable> & {
title: string | React.ReactNode;
textStyle?: StyleProp<TextStyle>;
}) => (
<Pressable
style={({ pressed }) => [styles.btn, pressed && styles.btnPressed]}
{...props}
>
{typeof title === "string" ? (
<Text style={[styles.btnText, textStyle]}>{title}</Text>
) : (
title
)}
</Pressable>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#232323",
},
buttons: {
display: "flex",
flexDirection: "row",
alignItems: "stretch",
backgroundColor: "#232323",
},
btn: {
display: "flex",
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 14,
paddingVertical: 10,
},
btnPressed: {
backgroundColor: "#3a3a3a",
},
btnText: {
color: "white",
fontSize: 16,
},
});
export default Terminal;