import { useState, useCallback } from 'react' import type { FileEntry } from '@/types' import { useTabsStore } from '@/stores/useTabsStore' import { useContextMenuStore } from '@/stores/useContextMenuStore' const extColors: Record = { ts: '#3178c6', tsx: '#3178c6', js: '#f7df1e', jsx: '#f7df1e', rs: '#dea584', json: '#5eb7e0', md: '#e8e8ed', html: '#e34c4c', css: '#42a5f5', py: '#3572a5', } const ROW_HEIGHT = 32 const INDENT = 16 const GUIDE_COLOR = 'var(--color-panel-border)' const GUIDE_HOVER_COLOR = 'var(--color-text-muted)' function ChevronIcon({ expanded }: { expanded: boolean }) { return ( ) } function FileTypeIcon({ name, isDir, expanded }: { name: string; isDir: boolean; expanded: boolean }) { if (isDir) { return ( {expanded ? ( ) : ( )} ) } const ext = name.split('.').pop()?.toLowerCase() ?? '' const color = extColors[ext] ?? '#5c5e64' return ( ) } interface GuideLinesProps { depth: number branchDepths: number[] isLast: boolean parentHovered: boolean } function GuideLines({ depth, branchDepths, isLast, parentHovered }: GuideLinesProps) { const color = parentHovered ? GUIDE_HOVER_COLOR : GUIDE_COLOR const mid = ROW_HEIGHT / 2 const leftBase = 12 return (
{branchDepths.map((bd) => { const l = leftBase + bd * INDENT return (
) })}
) } interface FileTreeItemProps { entry: FileEntry depth: number isLast: boolean branchDepths: number[] } function FileTreeItem({ entry, depth, isLast, branchDepths }: FileTreeItemProps) { const [expanded, setExpanded] = useState(depth < 1) const [parentHovered, setParentHovered] = useState(false) const openFile = useTabsStore((s) => s.openFile) const activeTabId = useTabsStore((s) => s.activeTabId) const openContextMenu = useContextMenuStore((s) => s.open) const handleContextMenu = useCallback((e: React.MouseEvent) => { e.preventDefault() openContextMenu(e.clientX, e.clientY, [ { id: 'open', label: entry.is_dir ? 'Expand' : 'Open', icon: entry.is_dir ? 'folder' : 'file', action: () => entry.is_dir ? setExpanded(!expanded) : openFile(entry.path) }, { id: 'sep1', label: '', separator: true, action: () => {} }, { id: 'copyPath', label: 'Copy Path', icon: 'link', action: () => navigator.clipboard.writeText(entry.path) }, ]) }, [entry, expanded, openFile, openContextMenu]) if (entry.is_dir) { return (
{entry.children && ( )}
) } const isActive = entry.path === activeTabId return ( ) } interface FileTreeProps { entries: FileEntry[] depth?: number parentBranchDepths?: number[] } export default function FileTree({ entries, depth = 0, parentBranchDepths = [] }: FileTreeProps) { return (
{entries.map((entry, idx) => ( ))}
) }