feat: make terminal resizable with drag handle
Build & Push Portfolio Image / build-push (push) Successful in 2m44s
Build & Push Portfolio Image / build-push (push) Successful in 2m44s
This commit is contained in:
@@ -67,9 +67,13 @@ export function InteractiveTerminal() {
|
|||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const { isOpen, setIsOpen } = useTerminal()
|
const { isOpen, setIsOpen } = useTerminal()
|
||||||
const [isMac, setIsMac] = useState(true)
|
const [isMac, setIsMac] = useState(true)
|
||||||
|
const [width, setWidth] = useState(448) // 28rem = 448px
|
||||||
|
const [height, setHeight] = useState(512) // 32rem = 512px
|
||||||
|
const [isResizing, setIsResizing] = useState(false)
|
||||||
const terminalRef = useRef<HTMLDivElement>(null)
|
const terminalRef = useRef<HTMLDivElement>(null)
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
const resizeHandleRef = useRef<HTMLDivElement>(null)
|
||||||
const isDark = useTheme()
|
const isDark = useTheme()
|
||||||
|
|
||||||
// Close on click outside
|
// Close on click outside
|
||||||
@@ -108,6 +112,35 @@ export function InteractiveTerminal() {
|
|||||||
if (isOpen) inputRef.current?.focus()
|
if (isOpen) inputRef.current?.focus()
|
||||||
}, [isOpen])
|
}, [isOpen])
|
||||||
|
|
||||||
|
// Handle terminal resize
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!isResizing) return
|
||||||
|
const container = containerRef.current
|
||||||
|
if (!container) return
|
||||||
|
|
||||||
|
const rect = container.getBoundingClientRect()
|
||||||
|
const newWidth = Math.max(320, e.clientX - rect.left)
|
||||||
|
const newHeight = Math.max(300, e.clientY - rect.top)
|
||||||
|
|
||||||
|
setWidth(newWidth)
|
||||||
|
setHeight(newHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
setIsResizing(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isResizing) {
|
||||||
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
|
document.addEventListener('mouseup', handleMouseUp)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousemove', handleMouseMove)
|
||||||
|
document.removeEventListener('mouseup', handleMouseUp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isResizing])
|
||||||
|
|
||||||
const handleCommand = async (cmd: string) => {
|
const handleCommand = async (cmd: string) => {
|
||||||
const trimmed = cmd.trim()
|
const trimmed = cmd.trim()
|
||||||
if (!trimmed) return
|
if (!trimmed) return
|
||||||
@@ -216,7 +249,8 @@ export function InteractiveTerminal() {
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
initial={{ opacity: 0, y: 10 }}
|
initial={{ opacity: 0, y: 10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
className={`rounded-lg shadow-2xl w-[28rem] h-[32rem] flex flex-col border ${
|
style={{ width: `${width}px`, height: `${height}px` }}
|
||||||
|
className={`rounded-lg shadow-2xl flex flex-col border relative ${
|
||||||
isDark
|
isDark
|
||||||
? 'bg-gray-950 border-gray-700'
|
? 'bg-gray-950 border-gray-700'
|
||||||
: 'bg-white border-gray-300'
|
: 'bg-white border-gray-300'
|
||||||
@@ -303,6 +337,19 @@ export function InteractiveTerminal() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Resize handle */}
|
||||||
|
<div
|
||||||
|
ref={resizeHandleRef}
|
||||||
|
onMouseDown={() => setIsResizing(true)}
|
||||||
|
className={`absolute bottom-0 right-0 w-4 h-4 cursor-se-resize ${
|
||||||
|
isDark ? 'bg-gray-700 hover:bg-gray-600' : 'bg-gray-300 hover:bg-gray-400'
|
||||||
|
} transition-colors`}
|
||||||
|
style={{
|
||||||
|
borderBottomRightRadius: 'inherit',
|
||||||
|
opacity: isResizing ? 1 : 0.5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
Reference in New Issue
Block a user