Update terminal modal

This commit is contained in:
MacRimi
2026-02-01 01:13:13 +01:00
parent fc8bf841bf
commit c2d2745777
3 changed files with 115 additions and 70 deletions

View File

@@ -12,7 +12,16 @@ import {
ArrowRight, ArrowRight,
CornerDownLeft, CornerDownLeft,
GripHorizontal, GripHorizontal,
ChevronDown,
} from "lucide-react" } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
DropdownMenuLabel,
} from "@/components/ui/dropdown-menu"
import "xterm/css/xterm.css" import "xterm/css/xterm.css"
import { API_PORT } from "@/lib/api-config" import { API_PORT } from "@/lib/api-config"
@@ -62,12 +71,7 @@ export function LxcTerminalModal({
const resizeBarRef = useRef<HTMLDivElement>(null) const resizeBarRef = useRef<HTMLDivElement>(null)
const modalHeightRef = useRef(500) const modalHeightRef = useRef(500)
const [showLoginModal, setShowLoginModal] = useState(false)
const [loginUsername, setLoginUsername] = useState("")
const [loginPassword, setLoginPassword] = useState("")
const [loginError, setLoginError] = useState("")
const [isLoggingIn, setIsLoggingIn] = useState(false)
const waitingForPasswordRef = useRef(false)
// Detect mobile/tablet // Detect mobile/tablet
useEffect(() => { useEffect(() => {
@@ -171,7 +175,7 @@ export function LxcTerminalModal({
const ws = new WebSocket(wsUrl) const ws = new WebSocket(wsUrl)
wsRef.current = ws wsRef.current = ws
// Reset state for new connection // Reset state for new connection
isInsideLxcRef.current = false isInsideLxcRef.current = false
outputBufferRef.current = "" outputBufferRef.current = ""
@@ -354,33 +358,6 @@ export function LxcTerminalModal({
const sendEnter = useCallback(() => sendKey("\r"), [sendKey]) const sendEnter = useCallback(() => sendKey("\r"), [sendKey])
const sendCtrlC = useCallback(() => sendKey("\x03"), [sendKey]) // Ctrl+C const sendCtrlC = useCallback(() => sendKey("\x03"), [sendKey]) // Ctrl+C
// Ctrl key state - user presses Ctrl button, then types a letter
const [ctrlPressed, setCtrlPressed] = useState(false)
const handleCtrlPress = useCallback(() => {
setCtrlPressed(true)
setTimeout(() => setCtrlPressed(false), 3000)
}, [])
// Handle keyboard input when Ctrl is pressed
useEffect(() => {
if (!ctrlPressed || !isOpen) return
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key.length === 1) {
e.preventDefault()
const code = e.key.toLowerCase().charCodeAt(0) - 96
if (code >= 1 && code <= 26) {
sendKey(String.fromCharCode(code))
}
setCtrlPressed(false)
}
}
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [ctrlPressed, isOpen, sendKey])
const showMobileControls = isMobile || isTablet const showMobileControls = isMobile || isTablet
return ( return (
@@ -477,16 +454,34 @@ export function LxcTerminalModal({
<CornerDownLeft className="h-4 w-4 mr-1" /> <CornerDownLeft className="h-4 w-4 mr-1" />
Enter Enter
</Button> </Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={handleCtrlPress} className="h-8 px-2 text-xs bg-zinc-800 border-zinc-700 text-zinc-300 gap-1"
className={`h-8 px-2 text-xs ${ctrlPressed
? "bg-yellow-600/30 border-yellow-600/50 text-yellow-400"
: "bg-zinc-800 border-zinc-700 text-zinc-300"}`}
> >
{ctrlPressed ? "Ctrl+?" : "Ctrl"} Ctrl
<ChevronDown className="h-3 w-3" />
</Button> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel className="text-xs text-muted-foreground">Control Sequences</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => sendKey("\x03")}>
<span className="font-mono text-xs mr-2">Ctrl+C</span>
<span className="text-muted-foreground text-xs">Cancel/Interrupt</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendKey("\x18")}>
<span className="font-mono text-xs mr-2">Ctrl+X</span>
<span className="text-muted-foreground text-xs">Exit (nano)</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendKey("\x12")}>
<span className="font-mono text-xs mr-2">Ctrl+R</span>
<span className="text-muted-foreground text-xs">Search history</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> </div>
</div> </div>
)} )}

View File

@@ -15,7 +15,16 @@ import {
ArrowRight, ArrowRight,
CornerDownLeft, CornerDownLeft,
GripHorizontal, GripHorizontal,
ChevronDown,
} from "lucide-react" } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
DropdownMenuLabel,
} from "@/components/ui/dropdown-menu"
import "xterm/css/xterm.css" import "xterm/css/xterm.css"
import { API_PORT } from "@/lib/api-config" import { API_PORT } from "@/lib/api-config"
@@ -741,18 +750,34 @@ export function ScriptTerminalModal({
<CornerDownLeft className="h-4 w-4 mr-1" /> <CornerDownLeft className="h-4 w-4 mr-1" />
Enter Enter
</Button> </Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button <Button
onPointerDown={(e) => {
e.preventDefault()
e.stopPropagation()
sendCommand("\x03")
}}
variant="outline" variant="outline"
size="sm" size="sm"
className="h-8 px-2 text-xs bg-zinc-800 hover:bg-zinc-700 border-zinc-700 text-white min-w-[65px]" className="h-8 px-2 text-xs bg-zinc-800 hover:bg-zinc-700 border-zinc-700 text-white min-w-[65px] gap-1"
> >
Ctrl Ctrl
<ChevronDown className="h-3 w-3" />
</Button> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel className="text-xs text-muted-foreground">Control Sequences</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => sendCommand("\x03")}>
<span className="font-mono text-xs mr-2">Ctrl+C</span>
<span className="text-muted-foreground text-xs">Cancel/Interrupt</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendCommand("\x18")}>
<span className="font-mono text-xs mr-2">Ctrl+X</span>
<span className="text-muted-foreground text-xs">Exit (nano)</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendCommand("\x12")}>
<span className="font-mono text-xs mr-2">Ctrl+R</span>
<span className="text-muted-foreground text-xs">Search history</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> </div>
)} )}

View File

@@ -15,7 +15,16 @@ import {
AlignJustify, AlignJustify,
Grid2X2, Grid2X2,
GripHorizontal, GripHorizontal,
ChevronDown,
} from "lucide-react" } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
DropdownMenuLabel,
} from "@/components/ui/dropdown-menu"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
@@ -967,18 +976,34 @@ const handleClose = () => {
> >
Enter Enter
</Button> </Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button <Button
onPointerDown={(e) => {
e.preventDefault()
e.stopPropagation()
sendSequence("\x03", e)
}}
variant="outline" variant="outline"
size="sm" size="sm"
className="h-8 px-2 text-xs" className="h-8 px-2 text-xs gap-1 bg-transparent"
> >
Ctrl Ctrl
<ChevronDown className="h-3 w-3" />
</Button> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel className="text-xs text-muted-foreground">Control Sequences</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => sendSequence("\x03")}>
<span className="font-mono text-xs mr-2">Ctrl+C</span>
<span className="text-muted-foreground text-xs">Cancel/Interrupt</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendSequence("\x18")}>
<span className="font-mono text-xs mr-2">Ctrl+X</span>
<span className="text-muted-foreground text-xs">Exit (nano)</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => sendSequence("\x12")}>
<span className="font-mono text-xs mr-2">Ctrl+R</span>
<span className="text-muted-foreground text-xs">Search history</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> </div>
)} )}