import React from 'react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Toggle } from '@/components/ui/toggle'; import { DirectoryTree } from './DirectoryTree'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; import { useFileSystemAccess } from '@/hooks/useFileSystemAccess'; import { cn, formatPathForDisplay } from '@/lib/utils'; import { toast } from 'sonner'; import { RiCheckboxBlankLine, RiCheckboxLine } from '@remixicon/react'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { useDeviceInfo } from '@/lib/device'; import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel'; const SHOW_HIDDEN_STORAGE_KEY = 'directoryTreeShowHidden'; interface DirectoryExplorerDialogProps { open: boolean; onOpenChange: (open: boolean) => void; } export const DirectoryExplorerDialog: React.FC = ({ open, onOpenChange, }) => { const { currentDirectory, homeDirectory, setDirectory, isHomeReady } = useDirectoryStore(); const [pendingPath, setPendingPath] = React.useState(null); const [hasUserSelection, setHasUserSelection] = React.useState(false); const [isConfirming, setIsConfirming] = React.useState(false); const [showHidden, setShowHidden] = React.useState(() => { if (typeof window === 'undefined') { return false; } try { const stored = window.localStorage.getItem(SHOW_HIDDEN_STORAGE_KEY); if (stored === 'true') { return true; } if (stored === 'false') { return false; } } catch { /* ignored */ } return false; }); const { isDesktop, requestAccess, startAccessing } = useFileSystemAccess(); const { isMobile } = useDeviceInfo(); React.useEffect(() => { if (open) { setPendingPath(null); setHasUserSelection(false); setIsConfirming(false); } }, [open]); React.useEffect(() => { if (!open) { return; } if (!hasUserSelection && !pendingPath && homeDirectory && isHomeReady) { setPendingPath(homeDirectory); setHasUserSelection(true); } }, [open, hasUserSelection, pendingPath, homeDirectory, isHomeReady]); React.useEffect(() => { if (typeof window === 'undefined') { return; } try { window.localStorage.setItem(SHOW_HIDDEN_STORAGE_KEY, showHidden ? 'true' : 'false'); } catch { /* ignored */ } }, [showHidden]); const formattedPendingPath = React.useMemo(() => { if (!pendingPath) { return 'Select a directory'; } return formatPathForDisplay(pendingPath, homeDirectory); }, [pendingPath, homeDirectory]); const handleClose = React.useCallback(() => { onOpenChange(false); }, [onOpenChange]); const finalizeSelection = React.useCallback(async (targetPath: string) => { if (!targetPath || isConfirming) { return; } if (targetPath === currentDirectory) { handleClose(); return; } setIsConfirming(true); try { let resolvedPath = targetPath; if (isDesktop) { const accessResult = await requestAccess(targetPath); if (!accessResult.success) { toast.error('Unable to access directory', { description: accessResult.error || 'Desktop denied directory access.', }); return; } resolvedPath = accessResult.path ?? targetPath; const startResult = await startAccessing(resolvedPath); if (!startResult.success) { toast.error('Failed to open directory', { description: startResult.error || 'Desktop could not grant file access.', }); return; } } setDirectory(resolvedPath); handleClose(); } catch (error) { toast.error('Failed to select directory', { description: error instanceof Error ? error.message : 'Unknown error occurred.', }); } finally { setIsConfirming(false); } }, [ currentDirectory, handleClose, isDesktop, requestAccess, setDirectory, startAccessing, isConfirming, ]); const handleConfirm = React.useCallback(async () => { if (!pendingPath) { return; } await finalizeSelection(pendingPath); }, [finalizeSelection, pendingPath]); const handleSelectPath = React.useCallback((path: string) => { setPendingPath(path); setHasUserSelection(true); }, []); const handleDoubleClickPath = React.useCallback(async (path: string) => { setPendingPath(path); setHasUserSelection(true); await finalizeSelection(path); }, [ finalizeSelection, ]); const dialogHeader = ( Select project directory Choose the working directory used for sessions, commands, and OpenCode operations. ); const scrollContent = (
Currently selected
{formatPathForDisplay(currentDirectory, homeDirectory)}
Selected directory
{formattedPendingPath}
setShowHidden(Boolean(value))} variant="outline" className="w-full justify-start gap-2 rounded-xl border-border/40 bg-sidebar/60 px-3 py-2 text-foreground min-w-0 h-auto sm:px-4 sm:py-3" > {showHidden ? ( ) : ( )} Show hidden directories

Use the tree to browse, pin frequently used locations, or create a new directory. Select a folder, then confirm to update the working directory for OpenChamber.

); const renderActionButtons = () => ( <> ); if (isMobile) { return ( onOpenChange(false)} title="Select project directory" className="max-w-full" footer={
{renderActionButtons()}
} > {scrollContent}
); } return ( {dialogHeader} {scrollContent} {renderActionButtons()} ); };