import classNames from 'classnames'; import { motion } from 'framer-motion'; import type { CSSProperties, HTMLAttributes, MouseEvent as ReactMouseEvent, ReactNode, } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useWindowSize } from 'react-use'; import { useOsInfo } from '../hooks/useOsInfo'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarWidth } from '../hooks/useSidebarWidth'; import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { WINDOW_FLOATING_SIDEBAR_WIDTH } from '../lib/constants'; import { Button } from './core/Button'; import { HStack } from './core/Stacks'; import { Overlay } from './Overlay'; import { RequestResponse } from './RequestResponse'; import { ResizeHandle } from './ResizeHandle'; import { Sidebar } from './Sidebar'; import { SidebarActions } from './SidebarActions'; import { WorkspaceHeader } from './WorkspaceHeader'; const side = { gridArea: 'side' }; const head = { gridArea: 'head' }; const body = { gridArea: 'body' }; const drag = { gridArea: 'drag' }; export default function Workspace() { const { setWidth, width, resetWidth } = useSidebarWidth(); const { hide, show, hidden, toggle } = useSidebarHidden(); const windowSize = useWindowSize(); const [floating, setFloating] = useState(false); const [isResizing, setIsResizing] = useState(false); const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void } | null>( null, ); useListenToTauriEvent('toggle_sidebar', toggle); // float/un-float sidebar on window resize useEffect(() => { const shouldHide = windowSize.width <= WINDOW_FLOATING_SIDEBAR_WIDTH; if (shouldHide) setFloating(true); else if (!shouldHide) setFloating(false); }, [windowSize.width]); const unsub = () => { if (moveState.current !== null) { document.documentElement.removeEventListener('mousemove', moveState.current.move); document.documentElement.removeEventListener('mouseup', moveState.current.up); } }; const handleResizeStart = useCallback( (e: ReactMouseEvent) => { if (width === undefined) return; unsub(); const mouseStartX = e.clientX; const startWidth = width; moveState.current = { move: async (e: MouseEvent) => { e.preventDefault(); // Prevent text selection and things const newWidth = startWidth + (e.clientX - mouseStartX); if (newWidth < 100) { hide(); resetWidth(); } else { show(); setWidth(newWidth); } }, up: (e: MouseEvent) => { e.preventDefault(); unsub(); setIsResizing(false); }, }; document.documentElement.addEventListener('mousemove', moveState.current.move); document.documentElement.addEventListener('mouseup', moveState.current.up); setIsResizing(true); }, [setWidth, resetWidth, width, hide, show], ); const sideWidth = hidden ? 0 : width; const styles = useMemo( () => ({ gridTemplate: floating ? ` ' ${head.gridArea}' auto ' ${body.gridArea}' minmax(0,1fr) / 1fr` : ` ' ${head.gridArea} ${head.gridArea} ${head.gridArea}' auto ' ${side.gridArea} ${drag.gridArea} ${body.gridArea}' minmax(0,1fr) / ${sideWidth}px 0 1fr`, }), [sideWidth, floating], ); if (windowSize.width <= 100) { return (
); } return (
{floating ? ( ) : ( <>
)}
); } interface HeaderSizeProps extends HTMLAttributes { children: ReactNode; } function HeaderSize({ className, ...props }: HeaderSizeProps) { const platform = useOsInfo(); return (
); }