From b8d7eb6e8a19dac690e94d0eebc6bbaeac2d4119 Mon Sep 17 00:00:00 2001 From: ali asaria <aliasaria@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:07:03 -0500 Subject: [PATCH] The terminal at bottom is now draggable in size --- src/renderer/App.tsx | 68 ++++++++++++++----- .../components/Shared/DraggableEllipsis.tsx | 55 +++++++++++++++ 2 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 src/renderer/components/Shared/DraggableEllipsis.tsx diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index b7e981fe..594a4c4e 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { CssVarsProvider } from '@mui/joy/styles'; import CssBaseline from '@mui/joy/CssBaseline'; import Box from '@mui/joy/Box'; @@ -20,10 +20,12 @@ import { ChevronDown, ChevronDownIcon, ChevronUpIcon, + EllipsisIcon, Icon, } from 'lucide-react'; import { IconButton } from '@mui/joy'; import { log } from 'node:console'; +import DraggableElipsis from './components/Shared/DraggableEllipsis'; // import OutputTerminal from './components/OutputTerminal'; // import AutoUpdateModal from './components/AutoUpdateModal'; @@ -37,6 +39,7 @@ export default function App() { const [sshConnection, setSSHConnection] = useState(null); const [logsDrawerOpen, setLogsDrawerOpen] = useState(false); + const [logsDrawerHeight, setLogsDrawerHeight] = useState(0); useEffect(() => { async function getSavedExperimentId() { @@ -81,6 +84,24 @@ export default function App() { mutate: experimentInfoMutate, } = useSWR(chatAPI.GET_EXPERIMENT_URL(experimentId), fetcher); + const onOutputDrawerDrag = useCallback((pos) => { + const ypos = pos.y; + // calculate how far from the bottom of the screen that ypos is: + let bottom = window.innerHeight - ypos; + + // now clamp the height so it is between 0 and the screen height - 200px + // (200px is the minimum height of the logs drawer) + if (bottom < 120) { + bottom = 120; + } + if (bottom > window.innerHeight / 2) { + bottom = window.innerHeight / 2; + } + + // now set the height of the logs drawer to be that distance + setLogsDrawerHeight(bottom); + }, []); + return ( <CssVarsProvider disableTransitionOnChange theme={customTheme}> <CssBaseline /> @@ -93,12 +114,14 @@ export default function App() { width: '100dvw', overflow: 'hidden', gridTemplateColumns: '220px 1fr', - gridTemplateRows: logsDrawerOpen ? '60px 5fr 308px' : '60px 5fr 18px', + gridTemplateRows: logsDrawerOpen + ? `60px 5fr ${logsDrawerHeight}px` + : '60px 5fr 18px', gridTemplateAreas: ` - "sidebar header" - "sidebar main" - "sidebar footer" - `, + "sidebar header" + "sidebar main" + "sidebar footer" + `, // backgroundColor: (theme) => theme.vars.palette.background.surface, })} > @@ -147,20 +170,33 @@ export default function App() { height: logsDrawerOpen ? '100%' : '18px', width: '100%', overflow: 'hidden', - alignItems: 'flex-end', + alignItems: 'stretch', backgroundColor: 'var(--joy-palette-background-level3)', }} > - <IconButton - sx={{ padding: 0, margin: 0, minHeight: 0 }} - onClick={() => setLogsDrawerOpen(!logsDrawerOpen)} + <div + style={{ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + height: '18px', + }} > - {logsDrawerOpen ? ( - <ChevronDownIcon size="18px" /> - ) : ( - <ChevronUpIcon size="18px" /> - )} - </IconButton> + <div> </div> + <DraggableElipsis notifyOnMove={onOutputDrawerDrag} /> + <IconButton + sx={{ padding: 0, margin: 0, minHeight: 0 }} + onClick={() => setLogsDrawerOpen(!logsDrawerOpen)} + > + {logsDrawerOpen ? ( + <> + <ChevronDownIcon size="18px" /> + </> + ) : ( + <ChevronUpIcon size="18px" /> + )} + </IconButton> + </div> <Box sx={{ height: logsDrawerOpen ? '100%' : '0px', diff --git a/src/renderer/components/Shared/DraggableEllipsis.tsx b/src/renderer/components/Shared/DraggableEllipsis.tsx new file mode 100644 index 00000000..e1fd7c7f --- /dev/null +++ b/src/renderer/components/Shared/DraggableEllipsis.tsx @@ -0,0 +1,55 @@ +import { EllipsisIcon } from 'lucide-react'; +import React, { useState, useEffect, useRef } from 'react'; + +export default function DraggableEllipsis({ notifyOnMove = (pos) => {} }) { + const [isDragging, setIsDragging] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + + const handleMouseDown = (e: React.MouseEvent) => { + e.target.style.userSelect = 'none'; + setIsDragging(true); + }; + + const handleMouseUp = (e) => { + e.target.style.userSelect = 'auto'; + setIsDragging(false); + }; + + const handleMouseMove = (e: MouseEvent) => { + if (isDragging) { + setPosition({ x: e.clientX, y: e.clientY }); + notifyOnMove({ x: e.clientX, y: e.clientY }); + } + }; + + useEffect(() => { + if (isDragging) { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + } else { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + } + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + }; + }, [isDragging]); + + return ( + <div + onMouseDown={handleMouseDown} + style={{ + // position: isDragging ? 'absolute' : 'relative', + // left: isDragging ? position.x : 'auto', + // top: isDragging ? position.y : 'auto', + cursor: isDragging ? 'grabbing' : 'grab', + zIndex: 3000, + height: '100%', + }} + > + <EllipsisIcon size="18px" /> + </div> + ); +} -- GitLab