mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-12-14 16:16:21 +00:00
Update script-terminal-modal.tsx
This commit is contained in:
@@ -56,8 +56,10 @@ export function ScriptTerminalModal({
|
|||||||
|
|
||||||
const [modalHeight, setModalHeight] = useState(80)
|
const [modalHeight, setModalHeight] = useState(80)
|
||||||
const [isResizing, setIsResizing] = useState(false)
|
const [isResizing, setIsResizing] = useState(false)
|
||||||
const startYRef = useRef(0)
|
const resizeHandlersRef = useRef<{
|
||||||
const startHeightRef = useRef(80)
|
handleMove: ((e: MouseEvent | TouchEvent) => void) | null
|
||||||
|
handleEnd: (() => void) | null
|
||||||
|
}>({ handleMove: null, handleEnd: null })
|
||||||
|
|
||||||
const terminalContainerRef = useCallback(
|
const terminalContainerRef = useCallback(
|
||||||
(node: HTMLDivElement | null) => {
|
(node: HTMLDivElement | null) => {
|
||||||
@@ -283,6 +285,16 @@ export function ScriptTerminalModal({
|
|||||||
termRef.current.dispose()
|
termRef.current.dispose()
|
||||||
termRef.current = null
|
termRef.current = null
|
||||||
}
|
}
|
||||||
|
if (resizeHandlersRef.current.handleMove) {
|
||||||
|
document.removeEventListener("mousemove", resizeHandlersRef.current.handleMove as any)
|
||||||
|
document.removeEventListener("touchmove", resizeHandlersRef.current.handleMove as any)
|
||||||
|
}
|
||||||
|
if (resizeHandlersRef.current.handleEnd) {
|
||||||
|
document.removeEventListener("mouseup", resizeHandlersRef.current.handleEnd)
|
||||||
|
document.removeEventListener("touchend", resizeHandlersRef.current.handleEnd)
|
||||||
|
}
|
||||||
|
resizeHandlersRef.current = { handleMove: null, handleEnd: null }
|
||||||
|
|
||||||
sessionIdRef.current = Math.random().toString(36).substring(2, 8)
|
sessionIdRef.current = Math.random().toString(36).substring(2, 8)
|
||||||
setIsComplete(false)
|
setIsComplete(false)
|
||||||
setExitCode(null)
|
setExitCode(null)
|
||||||
@@ -347,29 +359,73 @@ export function ScriptTerminalModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleResizeStart = (e: React.MouseEvent | React.TouchEvent) => {
|
const handleResizeStart = (e: React.MouseEvent | React.TouchEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
setIsResizing(true)
|
setIsResizing(true)
|
||||||
startYRef.current = "clientY" in e ? e.clientY : e.touches[0].clientY
|
const startY = "clientY" in e ? e.clientY : e.touches[0].clientY
|
||||||
startHeightRef.current = modalHeight
|
const startHeight = modalHeight
|
||||||
document.addEventListener("mousemove", handleResize as any)
|
|
||||||
document.addEventListener("touchmove", handleResize as any)
|
|
||||||
document.addEventListener("mouseup", handleResizeEnd)
|
|
||||||
document.addEventListener("touchend", handleResizeEnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleResize = (e: MouseEvent | TouchEvent) => {
|
const handleMove = (moveEvent: MouseEvent | TouchEvent) => {
|
||||||
if (!isResizing) return
|
const currentY = moveEvent instanceof MouseEvent ? moveEvent.clientY : moveEvent.touches[0].clientY
|
||||||
const currentY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY
|
const deltaY = currentY - startY
|
||||||
const deltaY = currentY - startYRef.current
|
const deltaPercent = (deltaY / window.innerHeight) * 100
|
||||||
const newHeight = startHeightRef.current + (deltaY / window.innerHeight) * 100
|
const newHeight = Math.max(50, Math.min(95, startHeight + deltaPercent))
|
||||||
setModalHeight(Math.max(50, Math.min(95, newHeight)))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleResizeEnd = () => {
|
setModalHeight(newHeight)
|
||||||
setIsResizing(false)
|
|
||||||
document.removeEventListener("mousemove", handleResize as any)
|
if (fitAddonRef.current && termRef.current && wsRef.current?.readyState === WebSocket.OPEN) {
|
||||||
document.removeEventListener("touchmove", handleResize as any)
|
try {
|
||||||
document.removeEventListener("mouseup", handleResizeEnd)
|
setTimeout(() => {
|
||||||
document.removeEventListener("touchend", handleResizeEnd)
|
fitAddonRef.current.fit()
|
||||||
|
wsRef.current?.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "resize",
|
||||||
|
cols: termRef.current.cols,
|
||||||
|
rows: termRef.current.rows,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}, 10)
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEnd = () => {
|
||||||
|
setIsResizing(false)
|
||||||
|
|
||||||
|
if (fitAddonRef.current && termRef.current && wsRef.current?.readyState === WebSocket.OPEN) {
|
||||||
|
try {
|
||||||
|
setTimeout(() => {
|
||||||
|
fitAddonRef.current.fit()
|
||||||
|
wsRef.current?.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "resize",
|
||||||
|
cols: termRef.current.cols,
|
||||||
|
rows: termRef.current.rows,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}, 50)
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.removeEventListener("mousemove", handleMove as any)
|
||||||
|
document.removeEventListener("touchmove", handleMove as any)
|
||||||
|
document.removeEventListener("mouseup", handleEnd)
|
||||||
|
document.removeEventListener("touchend", handleEnd)
|
||||||
|
|
||||||
|
resizeHandlersRef.current = { handleMove: null, handleEnd: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeHandlersRef.current = { handleMove, handleEnd }
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", handleMove as any)
|
||||||
|
document.addEventListener("touchmove", handleMove as any, { passive: false })
|
||||||
|
document.addEventListener("mouseup", handleEnd)
|
||||||
|
document.addEventListener("touchend", handleEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -411,13 +467,15 @@ export function ScriptTerminalModal({
|
|||||||
|
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<div
|
<div
|
||||||
className={`h-2 cursor-ns-resize flex items-center justify-center transition-colors ${
|
className={`h-2 cursor-ns-resize flex items-center justify-center transition-all duration-150 ${
|
||||||
isResizing ? "bg-blue-500" : "bg-zinc-800 hover:bg-blue-500/50"
|
isResizing ? "bg-blue-500 h-3" : "bg-zinc-800 hover:bg-blue-500/50"
|
||||||
}`}
|
}`}
|
||||||
onMouseDown={handleResizeStart}
|
onMouseDown={handleResizeStart}
|
||||||
onTouchStart={handleResizeStart}
|
onTouchStart={handleResizeStart}
|
||||||
>
|
>
|
||||||
<GripHorizontal className={`h-4 w-4 ${isResizing ? "text-white" : "text-zinc-500"}`} />
|
<GripHorizontal
|
||||||
|
className={`h-4 w-4 transition-all duration-150 ${isResizing ? "text-white scale-110" : "text-zinc-500"}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -445,7 +503,7 @@ export function ScriptTerminalModal({
|
|||||||
{currentInteraction && (
|
{currentInteraction && (
|
||||||
<Dialog open={true}>
|
<Dialog open={true}>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="max-w-4xl max-h-[80vh] overflow-y-auto"
|
className="max-w-4xl max-h-[80vh] overflow-y-auto animate-in fade-in-0 zoom-in-95 duration-100"
|
||||||
onInteractOutside={(e) => e.preventDefault()}
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
onEscapeKeyDown={(e) => e.preventDefault()}
|
onEscapeKeyDown={(e) => e.preventDefault()}
|
||||||
hideClose
|
hideClose
|
||||||
@@ -458,14 +516,14 @@ export function ScriptTerminalModal({
|
|||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse("yes")}
|
onClick={() => handleInteractionResponse("yes")}
|
||||||
className="flex-1 bg-blue-600 hover:bg-blue-700 text-white"
|
className="flex-1 bg-blue-600 hover:bg-blue-700 text-white transition-all duration-150"
|
||||||
>
|
>
|
||||||
Yes
|
Yes
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse("cancel")}
|
onClick={() => handleInteractionResponse("cancel")}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="flex-1 hover:bg-red-600 hover:text-white hover:border-red-600"
|
className="flex-1 hover:bg-red-600 hover:text-white hover:border-red-600 transition-all duration-150"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
@@ -474,12 +532,13 @@ export function ScriptTerminalModal({
|
|||||||
|
|
||||||
{currentInteraction.type === "menu" && currentInteraction.options && (
|
{currentInteraction.type === "menu" && currentInteraction.options && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{currentInteraction.options.map((option) => (
|
{currentInteraction.options.map((option, index) => (
|
||||||
<Button
|
<Button
|
||||||
key={option.value}
|
key={option.value}
|
||||||
onClick={() => handleInteractionResponse(option.value)}
|
onClick={() => handleInteractionResponse(option.value)}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full justify-start hover:bg-blue-600 hover:text-white"
|
className="w-full justify-start hover:bg-blue-600 hover:text-white transition-all duration-100 animate-in fade-in-0 slide-in-from-left-2"
|
||||||
|
style={{ animationDelay: `${index * 30}ms` }}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -487,7 +546,7 @@ export function ScriptTerminalModal({
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse("cancel")}
|
onClick={() => handleInteractionResponse("cancel")}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full hover:bg-red-600 hover:text-white hover:border-red-600"
|
className="w-full hover:bg-red-600 hover:text-white hover:border-red-600 transition-all duration-150"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
@@ -506,18 +565,19 @@ export function ScriptTerminalModal({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placeholder={currentInteraction.default || ""}
|
placeholder={currentInteraction.default || ""}
|
||||||
|
className="transition-all duration-150"
|
||||||
/>
|
/>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse(interactionInput)}
|
onClick={() => handleInteractionResponse(interactionInput)}
|
||||||
className="flex-1 bg-blue-600 hover:bg-blue-700"
|
className="flex-1 bg-blue-600 hover:bg-blue-700 transition-all duration-150"
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse("cancel")}
|
onClick={() => handleInteractionResponse("cancel")}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="flex-1 hover:bg-red-600 hover:text-white hover:border-red-600"
|
className="flex-1 hover:bg-red-600 hover:text-white hover:border-red-600 transition-all duration-150"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
@@ -529,14 +589,14 @@ export function ScriptTerminalModal({
|
|||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse("ok")}
|
onClick={() => handleInteractionResponse("ok")}
|
||||||
className="flex-1 bg-blue-600 hover:bg-blue-700"
|
className="flex-1 bg-blue-600 hover:bg-blue-700 transition-all duration-150"
|
||||||
>
|
>
|
||||||
OK
|
OK
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleInteractionResponse("cancel")}
|
onClick={() => handleInteractionResponse("cancel")}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="flex-1 hover:bg-red-600 hover:text-white hover:border-red-600"
|
className="flex-1 hover:bg-red-600 hover:text-white hover:border-red-600 transition-all duration-150"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user