Improve keyboard view and fix various issues with it
This commit is contained in:
@@ -98,49 +98,6 @@ export const Terminal = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
||||
return () => window.removeEventListener('resize', handleWindowResize);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!terminal) return;
|
||||
|
||||
const textarea = (terminal as any)._core?._textarea as HTMLTextAreaElement | undefined;
|
||||
if (textarea) {
|
||||
textarea.setAttribute("readonly", "true");
|
||||
textarea.setAttribute("inputmode", "none");
|
||||
textarea.style.caretColor = "transparent";
|
||||
|
||||
const preventKeyboard = () => {
|
||||
textarea.blur();
|
||||
};
|
||||
|
||||
textarea.addEventListener('focus', preventKeyboard);
|
||||
textarea.blur();
|
||||
|
||||
return () => {
|
||||
textarea.removeEventListener('focus', preventKeyboard);
|
||||
};
|
||||
}
|
||||
}, [terminal]);
|
||||
|
||||
function syncOverlay() {
|
||||
if (!terminal || !overlayTextareaRef.current) return;
|
||||
const buffer = terminal.buffer.active;
|
||||
let text = "";
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
text += buffer.getLine(i)?.translateToString() + "\n";
|
||||
}
|
||||
overlayTextareaRef.current.value = text;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!terminal) return;
|
||||
syncOverlay();
|
||||
|
||||
const disposeRender = terminal.onRender(() => syncOverlay());
|
||||
|
||||
return () => {
|
||||
disposeRender.dispose();
|
||||
};
|
||||
}, [terminal]);
|
||||
|
||||
function handleWindowResize() {
|
||||
if (!isVisibleRef.current) return;
|
||||
fitAddonRef.current?.fit();
|
||||
@@ -300,26 +257,10 @@ export const Terminal = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-full w-full m-1 relative"
|
||||
style={{ opacity: visible && isVisible ? 1 : 0 }}
|
||||
>
|
||||
<div ref={xtermRef} className="h-full w-full" />
|
||||
|
||||
<textarea
|
||||
ref={overlayTextareaRef}
|
||||
readOnly
|
||||
className="absolute top-0 left-0 w-full h-full"
|
||||
style={{
|
||||
opacity: 0.01,
|
||||
cursor: "text",
|
||||
background: "none",
|
||||
border: "none",
|
||||
resize: "none",
|
||||
userSelect: "text",
|
||||
WebkitUserSelect: "text",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
ref={xtermRef}
|
||||
className="h-full w-full m-1"
|
||||
style={{opacity: visible && isVisible ? 1 : 0, overflow: 'hidden'}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,19 +12,6 @@ export function TerminalKeyboard({onSendInput}: TerminalKeyboardProps) {
|
||||
const [isCtrl, setIsCtrl] = useState(false);
|
||||
const [isAlt, setIsAlt] = useState(false);
|
||||
|
||||
const handlePaste = useCallback(async () => {
|
||||
if (navigator.clipboard?.readText) {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
if (text) {
|
||||
onSendInput(text);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Paste failed:", err);
|
||||
}
|
||||
}
|
||||
}, [onSendInput]);
|
||||
|
||||
const onKeyPress = useCallback((button: string) => {
|
||||
if (button === "{shift}") {
|
||||
setLayoutName("shift");
|
||||
@@ -60,11 +47,6 @@ export function TerminalKeyboard({onSendInput}: TerminalKeyboardProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (button === "{paste}") {
|
||||
handlePaste();
|
||||
return;
|
||||
}
|
||||
|
||||
let input = button;
|
||||
|
||||
const specialKeyMap: { [key: string]: string } = {
|
||||
@@ -103,7 +85,7 @@ export function TerminalKeyboard({onSendInput}: TerminalKeyboardProps) {
|
||||
}
|
||||
|
||||
onSendInput(input);
|
||||
}, [onSendInput, handlePaste, isCtrl, isAlt]);
|
||||
}, [onSendInput, isCtrl, isAlt]);
|
||||
|
||||
const buttonTheme = [
|
||||
{
|
||||
@@ -151,7 +133,7 @@ export function TerminalKeyboard({onSendInput}: TerminalKeyboardProps) {
|
||||
"! @ # $ % ^ & * ( ) _ +",
|
||||
"[ ] { } | \\ ; : ' \" , . / < >",
|
||||
"F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
|
||||
"{arrowLeft} {arrowRight} {arrowUp} {arrowDown} {paste} {backspace}",
|
||||
"{arrowLeft} {arrowRight} {arrowUp} {arrowDown} {backspace}",
|
||||
"{hide} {less} {space} {enter}",
|
||||
],
|
||||
hide: [
|
||||
@@ -178,7 +160,6 @@ export function TerminalKeyboard({onSendInput}: TerminalKeyboardProps) {
|
||||
"{tab}": "tab",
|
||||
"{ctrl}": "ctrl",
|
||||
"{alt}": "alt",
|
||||
"{paste}": "paste",
|
||||
"{end}": "end",
|
||||
"{home}": "home",
|
||||
"{pgUp}": "pgUp",
|
||||
|
||||
@@ -134,7 +134,7 @@ const AppContent: FC = () => {
|
||||
{tabs.map(tab => (
|
||||
<div
|
||||
key={tab.id}
|
||||
className="absolute inset-0"
|
||||
className="absolute inset-0 mb-2"
|
||||
style={{
|
||||
visibility: tab.id === currentTab ? 'visible' : 'hidden',
|
||||
opacity: ready ? 1 : 0,
|
||||
@@ -148,13 +148,18 @@ const AppContent: FC = () => {
|
||||
</div>
|
||||
))}
|
||||
{tabs.length === 0 && (
|
||||
<div className="flex items-center justify-center h-full text-white">
|
||||
Select a host to start a terminal session.
|
||||
<div className="flex flex-col items-center justify-center h-full text-white gap-3 px-4 text-center">
|
||||
<h1 className="text-lg font-semibold">
|
||||
Select a host to start your terminal session
|
||||
</h1>
|
||||
<p className="text-sm text-gray-300 max-w-xs">
|
||||
Mobile support is currently limited. A dedicated mobile app is coming soon to enhance your experience.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{currentTab &&
|
||||
<div className="mb-1">
|
||||
<div className="mb-1 z-10">
|
||||
<TerminalKeyboard onSendInput={handleKeyboardInput}/>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export function LeftSidebar({isSidebarOpen, setIsSidebarOpen, onHostConnect, dis
|
||||
</SidebarHeader>
|
||||
<Separator/>
|
||||
<SidebarContent className="px-2 py-2">
|
||||
<div className="!bg-[#222225] rounded-lg mb-2">
|
||||
<div className="!bg-[#222225] rounded-lg">
|
||||
<Input
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
|
||||
Reference in New Issue
Block a user