@zendir/ui 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +419 -406
- package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
- package/dist/react/3d/ZenSpace3D.js.map +1 -1
- package/dist/react/3d/ZenSpace3DCesium.js.map +1 -1
- package/dist/react/3d/ZenSpace3DShaders.js.map +1 -1
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
- package/dist/react/3d/capturePngAnalysis.js.map +1 -1
- package/dist/react/3d/threeLoader.js.map +1 -1
- package/dist/react/astro/ClassificationBanner.js.map +1 -1
- package/dist/react/astro/GlobalStatusBar.js.map +1 -1
- package/dist/react/astro/MissionClock.js.map +1 -1
- package/dist/react/astro/MonitoringIcon.js.map +1 -1
- package/dist/react/astro/Notification.js.map +1 -1
- package/dist/react/astro/Progress.js.map +1 -1
- package/dist/react/astro/SimulationControls.js.map +1 -1
- package/dist/react/astro/StatusIndicator.js.map +1 -1
- package/dist/react/astro/UnifiedTimeline.js.map +1 -1
- package/dist/react/cards/AccessCard.js.map +1 -1
- package/dist/react/cards/OrbitCard.js.map +1 -1
- package/dist/react/cards/SpacecraftCard.js.map +1 -1
- package/dist/react/cards/TelemetryCard.js.map +1 -1
- package/dist/react/cards/TelemetryStreamCard.js.map +1 -1
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
- package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
- package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -1
- package/dist/react/charts/unified/AstroChart.js.map +1 -1
- package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -1
- package/dist/react/charts/unified/domain.js.map +1 -1
- package/dist/react/charts/unified/generators.js.map +1 -1
- package/dist/react/charts/unified/presets.js.map +1 -1
- package/dist/react/charts/unified/sync.js.map +1 -1
- package/dist/react/charts/unified/theme.js.map +1 -1
- package/dist/react/charts/unified/useChartStream.js.map +1 -1
- package/dist/react/chatgpt/index.js.map +1 -1
- package/dist/react/context/CategoryContext.js.map +1 -1
- package/dist/react/context/DisplaySettingsContext.js.map +1 -1
- package/dist/react/context/SpatialSelectionContext.js.map +1 -1
- package/dist/react/core/data/DataTable.js.map +1 -1
- package/dist/react/core/data/DataValue.js.map +1 -1
- package/dist/react/core/data/propertyConfig.js.map +1 -1
- package/dist/react/core/display/AstroIcon.js.map +1 -1
- package/dist/react/core/display/Badge.js.map +1 -1
- package/dist/react/core/display/CardHeader.js.map +1 -1
- package/dist/react/core/display/Container.js.map +1 -1
- package/dist/react/core/display/CopyButton.js.map +1 -1
- package/dist/react/core/display/GlassCard.js.map +1 -1
- package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -1
- package/dist/react/core/display/Icon.js.map +1 -1
- package/dist/react/core/display/Typography.js.map +1 -1
- package/dist/react/core/feedback/ConfirmDialog.js.map +1 -1
- package/dist/react/core/feedback/Dialog.js.map +1 -1
- package/dist/react/core/feedback/Toast.js.map +1 -1
- package/dist/react/core/inputs/Button.js.map +1 -1
- package/dist/react/core/inputs/Checkbox.js.map +1 -1
- package/dist/react/core/inputs/Input.js.map +1 -1
- package/dist/react/core/inputs/LimitsBar.js.map +1 -1
- package/dist/react/core/inputs/NumberInput.js.map +1 -1
- package/dist/react/core/inputs/PinInput.js.map +1 -1
- package/dist/react/core/inputs/Select.js.map +1 -1
- package/dist/react/core/inputs/Toggle.js.map +1 -1
- package/dist/react/core/layout/Box.js.map +1 -1
- package/dist/react/core/layout/Center.js.map +1 -1
- package/dist/react/core/layout/Divider.js.map +1 -1
- package/dist/react/core/layout/Flex.js.map +1 -1
- package/dist/react/core/layout/Grid.js.map +1 -1
- package/dist/react/core/layout/Spacer.js.map +1 -1
- package/dist/react/core/layout/Stack.js.map +1 -1
- package/dist/react/core/layout/responsive.js.map +1 -1
- package/dist/react/core/layout/useBreakpoint.js.map +1 -1
- package/dist/react/core/navigation/AppBar.js.map +1 -1
- package/dist/react/core/navigation/Pagination.js.map +1 -1
- package/dist/react/core/navigation/SideNav.js.map +1 -1
- package/dist/react/core/navigation/Tabs.js.map +1 -1
- package/dist/react/core/overlays/Popover.js.map +1 -1
- package/dist/react/core/overlays/SidePanel.js.map +1 -1
- package/dist/react/core/overlays/Tooltip.js.map +1 -1
- package/dist/react/core/widgets/ActivityPlanner.js.map +1 -1
- package/dist/react/core/widgets/Capture.js.map +1 -1
- package/dist/react/core/widgets/ChatPanel.js.map +1 -1
- package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -1
- package/dist/react/core/widgets/CommandBuilder.js.map +1 -1
- package/dist/react/core/widgets/ConnectionForm.js.map +1 -1
- package/dist/react/core/widgets/FileExplorer.js.map +1 -1
- package/dist/react/core/widgets/HexViewer.js.map +1 -1
- package/dist/react/core/widgets/ImageFrame.js.map +1 -1
- package/dist/react/core/widgets/ImageGallery.js.map +1 -1
- package/dist/react/core/widgets/ImagePanel.js.map +1 -1
- package/dist/react/core/widgets/LogViewer.js.map +1 -1
- package/dist/react/core/widgets/MessageStream.js.map +1 -1
- package/dist/react/core/widgets/MissionCalendar.js.map +1 -1
- package/dist/react/core/widgets/PacketViewer.js.map +1 -1
- package/dist/react/hooks/useCompactMode.js.map +1 -1
- package/dist/react/hooks/useSimulationScene.js.map +1 -1
- package/dist/react/panels/LayerControlPanel.js.map +1 -1
- package/dist/react/panels/ObjectInventoryPanel.js.map +1 -1
- package/dist/react/shared/ErrorBoundary.js.map +1 -1
- package/dist/react/shared/Skeleton.js.map +1 -1
- package/dist/react/theme/ThemeProvider.js +2 -1
- package/dist/react/theme/ThemeProvider.js.map +1 -1
- package/dist/react/theme/cardAccent.js.map +1 -1
- package/dist/react/theme/config.js.map +1 -1
- package/dist/react/theme/types.js.map +1 -1
- package/dist/react/types.js.map +1 -1
- package/dist/react/utils/categoryPalette.js.map +1 -1
- package/dist/react/utils/index.js.map +1 -1
- package/dist/style.css +216 -216
- package/dist/tokens/core.js.map +1 -1
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/palettes/astro.js.map +1 -1
- package/dist/tokens/palettes/index.js.map +1 -1
- package/dist/tokens/palettes/tailwind.js.map +1 -1
- package/dist/tokens/palettes/zendir.js.map +1 -1
- package/dist/tokens/themes.js.map +1 -1
- package/dist/tokens/tokens.css +533 -533
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileExplorer.js","names":[],"sources":["../../../../src/react/core/widgets/FileExplorer.tsx"],"sourcesContent":["/**\n * @zendir/ui - FileExplorer Component\n *\n * Hierarchical file browser for mission data management and file operations.\n * Supports tree navigation, file actions (download, delete, upload),\n * search, breadcrumbs, and multiple selection.\n *\n * Features:\n * - Tree view and list view modes\n * - Breadcrumb navigation\n * - File type icons\n * - Search/filter\n * - Multi-select with checkboxes\n * - File actions (open, download, delete, rename)\n * - Upload dropzone\n * - File size and modified date display\n * - Sort by name, size, date\n *\n * @example\n * ```tsx\n * <FileExplorer\n * files={fileTree}\n * onFileOpen={(file) => console.log('Open:', file)}\n * onFileDownload={(file) => console.log('Download:', file)}\n * />\n * ```\n */\n\nimport React, { useState, useCallback, useMemo, memo } from \"react\";\nimport { useTheme } from \"../../theme\";\nimport { AstroIcon, type AstroIconName } from \"../display/AstroIcon\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface FileNode {\n /** File/folder name */\n name: string;\n /** Full path */\n path: string;\n /** Whether this is a directory */\n isDirectory: boolean;\n /** File size in bytes (for files) */\n size?: number;\n /** Last modified date */\n modified?: string | Date;\n /** MIME type */\n mimeType?: string;\n /** Children (for directories) */\n children?: FileNode[];\n /** Custom icon name */\n icon?: string;\n /** Whether the file is readonly */\n readonly?: boolean;\n}\n\nexport type FileViewMode = \"tree\" | \"list\";\nexport type FileSortBy = \"name\" | \"size\" | \"modified\";\nexport type FileSortDir = \"asc\" | \"desc\";\n\nexport interface FileExplorerProps {\n /** Root file tree */\n files: FileNode[];\n /** Height (default: 500) */\n height?: number | string;\n /** Title */\n title?: string;\n /** View mode (default: 'list') */\n viewMode?: FileViewMode;\n /** Show search bar (default: true) */\n showSearch?: boolean;\n /** Show breadcrumbs (default: true) */\n showBreadcrumbs?: boolean;\n /** Show file details (size, date) (default: true) */\n showDetails?: boolean;\n /** Allow multi-select (default: false) */\n multiSelect?: boolean;\n /** Allow upload (default: false) */\n allowUpload?: boolean;\n /** Allow delete (default: false) */\n allowDelete?: boolean;\n /** Called when a file is opened/double-clicked */\n onFileOpen?: (file: FileNode) => void;\n /** Called when download is requested */\n onFileDownload?: (file: FileNode) => void;\n /** Called when delete is requested */\n onFileDelete?: (files: FileNode[]) => void;\n /** Called when files are uploaded */\n onFileUpload?: (files: File[], targetPath: string) => void;\n /** Called when selection changes */\n onSelectionChange?: (files: FileNode[]) => void;\n /** CSS class */\n className?: string;\n /** Show outer container border (default: true). Set false when embedded inside a Container/card. */\n bordered?: boolean;\n /** Custom style overrides for the outer container */\n style?: React.CSSProperties;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction formatFileSize(bytes?: number): string {\n if (bytes === undefined || bytes === null) return \"—\";\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;\n}\n\nfunction formatDate(date?: string | Date): string {\n if (!date) return \"—\";\n const d = typeof date === \"string\" ? new Date(date) : date;\n if (isNaN(d.getTime())) return String(date);\n return (\n d.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"2-digit\",\n year: \"numeric\",\n }) +\n \" \" +\n d.toLocaleTimeString(\"en-US\", {\n hour12: false,\n hour: \"2-digit\",\n minute: \"2-digit\",\n })\n );\n}\n\nfunction getFileIconName(file: FileNode): AstroIconName {\n if (file.isDirectory) return \"folder\";\n const ext = file.name.split(\".\").pop()?.toLowerCase() ?? \"\";\n const iconMap: Record<string, AstroIconName> = {\n rb: \"code\",\n py: \"code\",\n js: \"code\",\n ts: \"code\",\n jsx: \"code\",\n tsx: \"code\",\n json: \"insert-drive-file\",\n yaml: \"insert-drive-file\",\n yml: \"insert-drive-file\",\n xml: \"insert-drive-file\",\n csv: \"table-chart\",\n txt: \"short-text\",\n md: \"description\",\n log: \"insert-drive-file\",\n gz: \"archive\",\n zip: \"archive\",\n tar: \"archive\",\n bin: \"storage\",\n dat: \"storage\",\n png: \"image\",\n jpg: \"image\",\n gif: \"image\",\n svg: \"image\",\n pdf: \"picture-as-pdf\",\n doc: \"insert-drive-file\",\n xls: \"table-chart\",\n ppt: \"insert-drive-file\",\n };\n return iconMap[ext] || \"insert-drive-file\";\n}\n\nfunction flattenVisible(\n files: FileNode[],\n expanded: Set<string>,\n depth: number = 0,\n): { file: FileNode; depth: number }[] {\n const result: { file: FileNode; depth: number }[] = [];\n for (const f of files) {\n result.push({ file: f, depth });\n if (f.isDirectory && expanded.has(f.path) && f.children) {\n result.push(...flattenVisible(f.children, expanded, depth + 1));\n }\n }\n return result;\n}\n\nfunction filterFiles(files: FileNode[], query: string): FileNode[] {\n const q = query.toLowerCase();\n return files.reduce<FileNode[]>((acc, f) => {\n if (f.isDirectory && f.children) {\n const filteredChildren = filterFiles(f.children, query);\n if (filteredChildren.length > 0 || f.name.toLowerCase().includes(q)) {\n acc.push({ ...f, children: filteredChildren });\n }\n } else if (f.name.toLowerCase().includes(q)) {\n acc.push(f);\n }\n return acc;\n }, []);\n}\n\nfunction sortFiles(\n files: FileNode[],\n sortBy: FileSortBy,\n sortDir: FileSortDir,\n): FileNode[] {\n const sorted = [...files].sort((a, b) => {\n // Directories first\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n let cmp = 0;\n if (sortBy === \"name\") cmp = a.name.localeCompare(b.name);\n else if (sortBy === \"size\") cmp = (a.size ?? 0) - (b.size ?? 0);\n else if (sortBy === \"modified\") {\n const da = a.modified ? new Date(a.modified).getTime() : 0;\n const db = b.modified ? new Date(b.modified).getTime() : 0;\n cmp = da - db;\n }\n return sortDir === \"asc\" ? cmp : -cmp;\n });\n return sorted.map((f) =>\n f.isDirectory && f.children\n ? { ...f, children: sortFiles(f.children, sortBy, sortDir) }\n : f,\n );\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport const FileExplorer = memo(function FileExplorer({\n files,\n height = 500,\n title,\n viewMode: initialViewMode = \"list\",\n showSearch = true,\n showBreadcrumbs = true,\n showDetails = true,\n multiSelect = false,\n allowUpload = false,\n allowDelete = false,\n onFileOpen,\n onFileDownload: _onFileDownload,\n onFileDelete,\n onFileUpload,\n onSelectionChange: _onSelectionChange,\n className = \"\",\n bordered = true,\n style: styleProp,\n}: FileExplorerProps): React.ReactElement {\n const { tokens, theme } = useTheme();\n const isTransparentTheme =\n theme === \"transparent\" ||\n theme === \"transparent-bold\" ||\n theme === \"transparent-minimal\";\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n const [selected, setSelected] = useState<Set<string>>(new Set());\n const [currentPath, setCurrentPath] = useState<string[]>([]);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [sortBy, setSortBy] = useState<FileSortBy>(\"name\");\n const [sortDir, setSortDir] = useState<FileSortDir>(\"asc\");\n const [isDragging, setIsDragging] = useState(false);\n const [scrollTop, setScrollTop] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(240);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const processedFiles = useMemo(() => {\n let result = files;\n if (searchQuery.trim()) result = filterFiles(result, searchQuery);\n return sortFiles(result, sortBy, sortDir);\n }, [files, searchQuery, sortBy, sortDir]);\n\n // Navigate to directory by breadcrumb\n const currentFiles = useMemo(() => {\n let current = processedFiles;\n for (const dir of currentPath) {\n const found = current.find((f) => f.isDirectory && f.name === dir);\n if (found && found.children) current = found.children;\n else break;\n }\n return current;\n }, [processedFiles, currentPath]);\n\n const visibleItems = useMemo(() => {\n if (initialViewMode === \"tree\")\n return flattenVisible(processedFiles, expanded);\n return currentFiles.map((f) => ({ file: f, depth: 0 }));\n }, [processedFiles, expanded, currentFiles, initialViewMode]);\n\n const toggleExpand = useCallback((path: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n }, []);\n\n const toggleSelect = useCallback(\n (path: string) => {\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else {\n if (!multiSelect) next.clear();\n next.add(path);\n }\n return next;\n });\n },\n [multiSelect],\n );\n\n const handleFileClick = useCallback(\n (file: FileNode) => {\n if (file.isDirectory) {\n if (initialViewMode === \"list\") {\n setCurrentPath((prev) => [...prev, file.name]);\n } else {\n toggleExpand(file.path);\n }\n } else {\n toggleSelect(file.path);\n }\n },\n [initialViewMode, toggleExpand, toggleSelect],\n );\n\n const handleFileDoubleClick = useCallback(\n (file: FileNode) => {\n if (!file.isDirectory) onFileOpen?.(file);\n },\n [onFileOpen],\n );\n\n const navigateTo = useCallback((idx: number) => {\n setCurrentPath((prev) => prev.slice(0, idx));\n }, []);\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(false);\n if (!allowUpload || !onFileUpload) return;\n const droppedFiles = Array.from(e.dataTransfer.files);\n if (droppedFiles.length > 0) {\n onFileUpload(droppedFiles, \"/\" + currentPath.join(\"/\"));\n }\n },\n [allowUpload, onFileUpload, currentPath],\n );\n\n const handleSort = useCallback(\n (col: FileSortBy) => {\n if (sortBy === col) setSortDir((d) => (d === \"asc\" ? \"desc\" : \"asc\"));\n else {\n setSortBy(col);\n setSortDir(\"asc\");\n }\n },\n [sortBy],\n );\n\n const selectedFiles = useMemo(\n () =>\n visibleItems.filter((v) => selected.has(v.file.path)).map((v) => v.file),\n [visibleItems, selected],\n );\n const rowHeightPx = Number.parseInt(String(tokens.elementSize.sm), 10) || 32;\n const overscanRows = 12;\n const totalRows = visibleItems.length;\n const startRow = Math.max(\n 0,\n Math.floor(scrollTop / rowHeightPx) - overscanRows,\n );\n const visibleRows =\n Math.ceil(viewportHeight / rowHeightPx) + overscanRows * 2;\n const endRow = Math.min(totalRows, startRow + visibleRows);\n const virtualVisibleItems = useMemo(\n () => visibleItems.slice(startRow, endRow),\n [visibleItems, startRow, endRow],\n );\n\n React.useEffect(() => {\n const el = listRef.current;\n if (!el) return undefined;\n\n const updateHeight = () =>\n setViewportHeight(Math.max(rowHeightPx * 4, el.clientHeight || 240));\n updateHeight();\n\n if (typeof ResizeObserver !== \"undefined\") {\n const observer = new ResizeObserver(updateHeight);\n observer.observe(el);\n return () => observer.disconnect();\n }\n\n window.addEventListener(\"resize\", updateHeight);\n return () => window.removeEventListener(\"resize\", updateHeight);\n }, [rowHeightPx]);\n\n return (\n <div\n className={`file-explorer ${className}`}\n style={{\n height: typeof height === \"number\" ? height : undefined,\n background: tokens.colors.background.base,\n ...(bordered\n ? (tokens.colors.border.cardStyle ?? {\n border: `1px solid ${tokens.colors.border.muted}`,\n })\n : { border: \"none\" }),\n borderRadius: tokens.borderRadius.lg,\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n fontFamily: tokens.typography.fontFamily.primary,\n color: tokens.colors.text.primary,\n backdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\n WebkitBackdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\n ...styleProp,\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface,\n backdropFilter: \"blur(8px)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n }}\n >\n <span\n style={{\n fontWeight: tokens.typography.fontWeight.medium,\n fontSize: tokens.typography.fontSize.sm,\n }}\n >\n {title || \"File Explorer\"}\n </span>\n <div style={{ display: \"flex\", gap: tokens.spacing.sm }}>\n {allowUpload && (\n <button\n aria-label=\"Upload files\"\n style={{\n background: tokens.colors.interactive.default,\n color: tokens.colors.text.inverse,\n border: \"none\",\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: \"pointer\",\n fontWeight: tokens.typography.fontWeight.medium,\n transition: \"opacity 150ms ease\",\n }}\n >\n Upload\n </button>\n )}\n {allowDelete && selectedFiles.length > 0 && (\n <button\n onClick={() => onFileDelete?.(selectedFiles)}\n aria-label={`Delete ${selectedFiles.length} selected files`}\n style={{\n background: `${tokens.colors.status.critical}22`,\n color: tokens.colors.status.critical,\n border: `1px solid ${tokens.colors.status.critical}44`,\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: \"pointer\",\n fontWeight: tokens.typography.fontWeight.medium,\n transition: \"opacity 150ms ease\",\n }}\n >\n Delete ({selectedFiles.length})\n </button>\n )}\n </div>\n </div>\n\n {/* Toolbar: search + breadcrumbs */}\n {(showSearch || showBreadcrumbs) && (\n <div\n style={{\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.sm,\n background: tokens.colors.background.surface,\n }}\n >\n {showBreadcrumbs && initialViewMode === \"list\" && (\n <nav\n aria-label=\"File path\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.xs,\n fontSize: tokens.typography.fontSize.xs,\n flex: 1,\n }}\n >\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Navigate to root\"\n onClick={() => navigateTo(0)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") navigateTo(0);\n }}\n style={{\n cursor: \"pointer\",\n color: tokens.colors.interactive.default,\n fontWeight: tokens.typography.fontWeight.medium,\n }}\n >\n /\n </span>\n {currentPath.map((dir, idx) => (\n <React.Fragment key={idx}>\n <span\n aria-hidden=\"true\"\n style={{ color: tokens.colors.text.muted }}\n >\n /\n </span>\n <span\n role=\"button\"\n tabIndex={0}\n aria-label={`Navigate to ${dir}`}\n onClick={() => navigateTo(idx + 1)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") navigateTo(idx + 1);\n }}\n style={{\n cursor: \"pointer\",\n color:\n idx === currentPath.length - 1\n ? tokens.colors.text.primary\n : tokens.colors.interactive.default,\n fontWeight:\n idx === currentPath.length - 1\n ? tokens.typography.fontWeight.medium\n : tokens.typography.fontWeight.normal,\n }}\n >\n {dir}\n </span>\n </React.Fragment>\n ))}\n </nav>\n )}\n {showSearch && (\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"Search files...\"\n aria-label=\"Search files\"\n style={{\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.xs,\n outline: \"none\",\n minWidth: 150,\n transition: \"border-color 150ms ease\",\n }}\n />\n )}\n </div>\n )}\n\n {/* Column headers */}\n <div\n role=\"row\"\n style={{\n display: \"grid\",\n gridTemplateColumns: showDetails ? \"1fr auto auto\" : \"1fr\",\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface,\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.muted,\n fontWeight: tokens.typography.fontWeight.medium,\n textTransform: \"uppercase\",\n letterSpacing: \"0.5px\",\n }}\n >\n <span\n role=\"columnheader\"\n tabIndex={0}\n aria-label=\"Sort by name\"\n onClick={() => handleSort(\"name\")}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSort(\"name\");\n }}\n style={{ cursor: \"pointer\" }}\n >\n Name {sortBy === \"name\" && (sortDir === \"asc\" ? \"\\u25B2\" : \"\\u25BC\")}\n </span>\n {showDetails && (\n <>\n <span\n role=\"columnheader\"\n tabIndex={0}\n aria-label=\"Sort by size\"\n onClick={() => handleSort(\"size\")}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSort(\"size\");\n }}\n style={{ cursor: \"pointer\", minWidth: 70, textAlign: \"right\" }}\n >\n Size{\" \"}\n {sortBy === \"size\" && (sortDir === \"asc\" ? \"\\u25B2\" : \"\\u25BC\")}\n </span>\n <span\n role=\"columnheader\"\n tabIndex={0}\n aria-label=\"Sort by modified date\"\n onClick={() => handleSort(\"modified\")}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSort(\"modified\");\n }}\n style={{ cursor: \"pointer\", minWidth: 140, textAlign: \"right\" }}\n >\n Modified{\" \"}\n {sortBy === \"modified\" &&\n (sortDir === \"asc\" ? \"\\u25B2\" : \"\\u25BC\")}\n </span>\n </>\n )}\n </div>\n\n {/* File list */}\n <div\n ref={listRef}\n style={{\n flex: 1,\n overflow: \"auto\",\n position: \"relative\",\n border: isDragging\n ? `2px dashed ${tokens.colors.interactive.default}`\n : \"2px solid transparent\",\n }}\n onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}\n onDragOver={\n allowUpload\n ? (e) => {\n e.preventDefault();\n setIsDragging(true);\n }\n : undefined\n }\n onDragLeave={allowUpload ? () => setIsDragging(false) : undefined}\n onDrop={allowUpload ? handleDrop : undefined}\n >\n {isDragging && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n background: `${tokens.colors.interactive.default}11`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: tokens.typography.fontSize.sm,\n color: tokens.colors.interactive.default,\n fontWeight: tokens.typography.fontWeight.medium,\n zIndex: 10,\n }}\n >\n Drop files to upload\n </div>\n )}\n {visibleItems.length === 0 ? (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n color: tokens.colors.text.muted,\n fontSize: tokens.typography.fontSize.sm,\n }}\n >\n {searchQuery ? \"No matching files\" : \"Empty directory\"}\n </div>\n ) : (\n <div\n style={{ height: totalRows * rowHeightPx, position: \"relative\" }}\n >\n <div\n style={{\n position: \"absolute\",\n top: startRow * rowHeightPx,\n left: 0,\n right: 0,\n }}\n >\n {virtualVisibleItems.map(({ file, depth }) => (\n <div\n role=\"treeitem\"\n tabIndex={0}\n aria-label={`${file.isDirectory ? \"Folder\" : \"File\"}: ${file.name}`}\n aria-selected={selected.has(file.path)}\n key={file.path}\n onClick={() => handleFileClick(file)}\n onDoubleClick={() => handleFileDoubleClick(file)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleFileDoubleClick(file);\n if (e.key === \" \") {\n e.preventDefault();\n handleFileClick(file);\n }\n }}\n style={{\n display: \"grid\",\n gridTemplateColumns: showDetails ? \"1fr auto auto\" : \"1fr\",\n padding: `0 ${tokens.spacing.smd}`,\n paddingLeft: `calc(${tokens.spacing.smd} + ${depth * 20}px)`,\n height: tokens.elementSize.sm,\n alignItems: \"center\",\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n cursor: \"pointer\",\n background: selected.has(file.path)\n ? `${tokens.colors.interactive.default}15`\n : \"transparent\",\n transition: \"background 150ms ease\",\n }}\n onMouseEnter={(e) => {\n if (!selected.has(file.path))\n (e.currentTarget as HTMLDivElement).style.background =\n tokens.colors.background.surface;\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLDivElement).style.background =\n selected.has(file.path)\n ? `${tokens.colors.interactive.default}15`\n : \"transparent\";\n }}\n >\n <span\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.sm,\n overflow: \"hidden\",\n }}\n >\n {multiSelect && (\n <input\n type=\"checkbox\"\n checked={selected.has(file.path)}\n onChange={() => toggleSelect(file.path)}\n onClick={(e) => e.stopPropagation()}\n aria-label={`Select ${file.name}`}\n style={{\n accentColor: tokens.colors.interactive.default,\n }}\n />\n )}\n {initialViewMode === \"tree\" && file.isDirectory && (\n <span\n aria-hidden=\"true\"\n style={{\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.muted,\n width: 12,\n textAlign: \"center\",\n }}\n >\n {expanded.has(file.path) ? \"\\u25BC\" : \"\\u25B6\"}\n </span>\n )}\n <span\n aria-hidden=\"true\"\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n fontSize: tokens.typography.fontSize.sm,\n }}\n >\n <AstroIcon\n name={getFileIconName(file)}\n size=\"small\"\n label=\"\"\n style={{ fontSize: \"inherit\" }}\n />\n </span>\n <span\n style={{\n fontSize: tokens.typography.fontSize.xs,\n fontWeight: file.isDirectory\n ? tokens.typography.fontWeight.medium\n : tokens.typography.fontWeight.normal,\n color: file.isDirectory\n ? tokens.colors.interactive.default\n : tokens.colors.text.primary,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {file.name}\n </span>\n </span>\n {showDetails && (\n <>\n <span\n style={{\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.muted,\n textAlign: \"right\",\n minWidth: 70,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n >\n {file.isDirectory ? \"—\" : formatFileSize(file.size)}\n </span>\n <span\n style={{\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.muted,\n textAlign: \"right\",\n minWidth: 140,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n >\n {formatDate(file.modified)}\n </span>\n </>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Status bar */}\n <div\n style={{\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n borderTop: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface,\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.muted,\n display: \"flex\",\n justifyContent: \"space-between\",\n }}\n >\n <span>{visibleItems.length} items</span>\n {selected.size > 0 && <span>{selected.size} selected</span>}\n </div>\n </div>\n );\n});\n\nexport default FileExplorer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwGA,SAAS,eAAe,OAAwB;CAC9C,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;CAClD,IAAI,UAAU,GAAG,OAAO;CACxB,MAAM,QAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;CAAI;CAC1C,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;CACrD,OAAO,IAAI,QAAQ,KAAK,IAAI,MAAM,CAAC,EAAA,CAAG,QAAQ,IAAI,IAAI,IAAI,CAAC,EAAE,GAAG,MAAM;AACxE;AAEA,SAAS,WAAW,MAA8B;CAChD,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,IAAI,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;CACtD,IAAI,MAAM,EAAE,QAAQ,CAAC,GAAG,OAAO,OAAO,IAAI;CAC1C,OACE,EAAE,mBAAmB,SAAS;EAC5B,OAAO;EACP,KAAK;EACL,MAAM;CACR,CAAC,IACD,MACA,EAAE,mBAAmB,SAAS;EAC5B,QAAQ;EACR,MAAM;EACN,QAAQ;CACV,CAAC;AAEL;AAEA,SAAS,gBAAgB,MAA+B;CACtD,IAAI,KAAK,aAAa,OAAO;CA+B7B,OAAO;EA5BL,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,KAAK;EACL,KAAK;EACL,MAAM;EACN,MAAM;EACN,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,IAAI;EACJ,KAAK;EACL,IAAI;EACJ,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;CAEA,EA9BK,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,YAAY,KAAK,OA8BlC;AACzB;AAEA,SAAS,eACP,OACA,UACA,QAAgB,GACqB;CACrC,MAAM,SAA8C,CAAC;CACrD,KAAK,MAAM,KAAK,OAAO;EACrB,OAAO,KAAK;GAAE,MAAM;GAAG;EAAM,CAAC;EAC9B,IAAI,EAAE,eAAe,SAAS,IAAI,EAAE,IAAI,KAAK,EAAE,UAC7C,OAAO,KAAK,GAAG,eAAe,EAAE,UAAU,UAAU,QAAQ,CAAC,CAAC;CAElE;CACA,OAAO;AACT;AAEA,SAAS,YAAY,OAAmB,OAA2B;CACjE,MAAM,IAAI,MAAM,YAAY;CAC5B,OAAO,MAAM,QAAoB,KAAK,MAAM;EAC1C,IAAI,EAAE,eAAe,EAAE,UAAU;GAC/B,MAAM,mBAAmB,YAAY,EAAE,UAAU,KAAK;GACtD,IAAI,iBAAiB,SAAS,KAAK,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,GAChE,IAAI,KAAK;IAAE,GAAG;IAAG,UAAU;GAAiB,CAAC;EAEjD,OAAO,IAAI,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,GACxC,IAAI,KAAK,CAAC;EAEZ,OAAO;CACT,GAAG,CAAC,CAAC;AACP;AAEA,SAAS,UACP,OACA,QACA,SACY;CAcZ,OAbe,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM;EAEvC,IAAI,EAAE,gBAAgB,EAAE,aAAa,OAAO,EAAE,cAAc,KAAK;EACjE,IAAI,MAAM;EACV,IAAI,WAAW,QAAQ,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI;OACnD,IAAI,WAAW,QAAQ,OAAO,EAAE,QAAQ,MAAM,EAAE,QAAQ;OACxD,IAAI,WAAW,YAGlB,OAFW,EAAE,WAAW,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI,MAC9C,EAAE,WAAW,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI;EAG3D,OAAO,YAAY,QAAQ,MAAM,CAAC;CACpC,CACO,CAAA,CAAO,KAAK,MACjB,EAAE,eAAe,EAAE,WACf;EAAE,GAAG;EAAG,UAAU,UAAU,EAAE,UAAU,QAAQ,OAAO;CAAE,IACzD,CACN;AACF;AAMA,IAAa,eAAe,KAAK,SAAS,aAAa,EACrD,OACA,SAAS,KACT,OACA,UAAU,kBAAkB,QAC5B,aAAa,MACb,kBAAkB,MAClB,cAAc,MACd,cAAc,OACd,cAAc,OACd,cAAc,OACd,YACA,gBAAgB,iBAChB,cACA,cACA,mBAAmB,oBACnB,YAAY,IACZ,WAAW,MACX,OAAO,aACiC;CACxC,MAAM,EAAE,QAAQ,UAAU,SAAS;CACnC,MAAM,qBACJ,UAAU,iBACV,UAAU,sBACV,UAAU;CACZ,MAAM,CAAC,UAAU,eAAe,yBAAsB,IAAI,IAAI,CAAC;CAC/D,MAAM,CAAC,UAAU,eAAe,yBAAsB,IAAI,IAAI,CAAC;CAC/D,MAAM,CAAC,aAAa,kBAAkB,SAAmB,CAAC,CAAC;CAC3D,MAAM,CAAC,aAAa,kBAAkB,SAAS,EAAE;CACjD,MAAM,CAAC,QAAQ,aAAa,SAAqB,MAAM;CACvD,MAAM,CAAC,SAAS,cAAc,SAAsB,KAAK;CACzD,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAClD,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC;CAC5C,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,UAAU,MAAM,OAAuB,IAAI;CAEjD,MAAM,iBAAiB,cAAc;EACnC,IAAI,SAAS;EACb,IAAI,YAAY,KAAK,GAAG,SAAS,YAAY,QAAQ,WAAW;EAChE,OAAO,UAAU,QAAQ,QAAQ,OAAO;CAC1C,GAAG;EAAC;EAAO;EAAa;EAAQ;CAAO,CAAC;CAGxC,MAAM,eAAe,cAAc;EACjC,IAAI,UAAU;EACd,KAAK,MAAM,OAAO,aAAa;GAC7B,MAAM,QAAQ,QAAQ,MAAM,MAAM,EAAE,eAAe,EAAE,SAAS,GAAG;GACjE,IAAI,SAAS,MAAM,UAAU,UAAU,MAAM;QACxC;EACP;EACA,OAAO;CACT,GAAG,CAAC,gBAAgB,WAAW,CAAC;CAEhC,MAAM,eAAe,cAAc;EACjC,IAAI,oBAAoB,QACtB,OAAO,eAAe,gBAAgB,QAAQ;EAChD,OAAO,aAAa,KAAK,OAAO;GAAE,MAAM;GAAG,OAAO;EAAE,EAAE;CACxD,GAAG;EAAC;EAAgB;EAAU;EAAc;CAAe,CAAC;CAE5D,MAAM,eAAe,aAAa,SAAiB;EACjD,aAAa,SAAS;GACpB,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI;QAC/B,KAAK,IAAI,IAAI;GAClB,OAAO;EACT,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,MAAM,eAAe,aAClB,SAAiB;EAChB,aAAa,SAAS;GACpB,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI;QAC/B;IACH,IAAI,CAAC,aAAa,KAAK,MAAM;IAC7B,KAAK,IAAI,IAAI;GACf;GACA,OAAO;EACT,CAAC;CACH,GACA,CAAC,WAAW,CACd;CAEA,MAAM,kBAAkB,aACrB,SAAmB;EAClB,IAAI,KAAK,aACP,IAAI,oBAAoB,QACtB,gBAAgB,SAAS,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;OAE7C,aAAa,KAAK,IAAI;OAGxB,aAAa,KAAK,IAAI;CAE1B,GACA;EAAC;EAAiB;EAAc;CAAY,CAC9C;CAEA,MAAM,wBAAwB,aAC3B,SAAmB;EAClB,IAAI,CAAC,KAAK,aAAa,aAAa,IAAI;CAC1C,GACA,CAAC,UAAU,CACb;CAEA,MAAM,aAAa,aAAa,QAAgB;EAC9C,gBAAgB,SAAS,KAAK,MAAM,GAAG,GAAG,CAAC;CAC7C,GAAG,CAAC,CAAC;CAEL,MAAM,aAAa,aAChB,MAAuB;EACtB,EAAE,eAAe;EACjB,cAAc,KAAK;EACnB,IAAI,CAAC,eAAe,CAAC,cAAc;EACnC,MAAM,eAAe,MAAM,KAAK,EAAE,aAAa,KAAK;EACpD,IAAI,aAAa,SAAS,GACxB,aAAa,cAAc,MAAM,YAAY,KAAK,GAAG,CAAC;CAE1D,GACA;EAAC;EAAa;EAAc;CAAW,CACzC;CAEA,MAAM,aAAa,aAChB,QAAoB;EACnB,IAAI,WAAW,KAAK,YAAY,MAAO,MAAM,QAAQ,SAAS,KAAM;OAC/D;GACH,UAAU,GAAG;GACb,WAAW,KAAK;EAClB;CACF,GACA,CAAC,MAAM,CACT;CAEA,MAAM,gBAAgB,cAElB,aAAa,QAAQ,MAAM,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,GACzE,CAAC,cAAc,QAAQ,CACzB;CACA,MAAM,cAAc,OAAO,SAAS,OAAO,OAAO,YAAY,EAAE,GAAG,EAAE,KAAK;CAC1E,MAAM,eAAe;CACrB,MAAM,YAAY,aAAa;CAC/B,MAAM,WAAW,KAAK,IACpB,GACA,KAAK,MAAM,YAAY,WAAW,IAAI,YACxC;CACA,MAAM,cACJ,KAAK,KAAK,iBAAiB,WAAW,IAAI,eAAe;CAC3D,MAAM,SAAS,KAAK,IAAI,WAAW,WAAW,WAAW;CACzD,MAAM,sBAAsB,cACpB,aAAa,MAAM,UAAU,MAAM,GACzC;EAAC;EAAc;EAAU;CAAM,CACjC;CAEA,MAAM,gBAAgB;EACpB,MAAM,KAAK,QAAQ;EACnB,IAAI,CAAC,IAAI,OAAO,KAAA;EAEhB,MAAM,qBACJ,kBAAkB,KAAK,IAAI,cAAc,GAAG,GAAG,gBAAgB,GAAG,CAAC;EACrE,aAAa;EAEb,IAAI,OAAO,mBAAmB,aAAa;GACzC,MAAM,WAAW,IAAI,eAAe,YAAY;GAChD,SAAS,QAAQ,EAAE;GACnB,aAAa,SAAS,WAAW;EACnC;EAEA,OAAO,iBAAiB,UAAU,YAAY;EAC9C,aAAa,OAAO,oBAAoB,UAAU,YAAY;CAChE,GAAG,CAAC,WAAW,CAAC;CAEhB,OACE,qBAAC,OAAD;EACE,WAAW,iBAAiB;EAC5B,OAAO;GACL,QAAQ,OAAO,WAAW,WAAW,SAAS,KAAA;GAC9C,YAAY,OAAO,OAAO,WAAW;GACrC,GAAI,WACC,OAAO,OAAO,OAAO,aAAa,EACjC,QAAQ,aAAa,OAAO,OAAO,OAAO,QAC5C,IACA,EAAE,QAAQ,OAAO;GACrB,cAAc,OAAO,aAAa;GAClC,UAAU;GACV,SAAS;GACT,eAAe;GACf,YAAY,OAAO,WAAW,WAAW;GACzC,OAAO,OAAO,OAAO,KAAK;GAC1B,gBAAgB,qBAAqB,eAAe,KAAA;GACpD,sBAAsB,qBAAqB,eAAe,KAAA;GAC1D,GAAG;EACL;YAnBF;GAsBE,qBAAC,OAAD;IACE,OAAO;KACL,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,cAAc,aAAa,OAAO,OAAO,OAAO;KAChD,YAAY,OAAO,OAAO,WAAW;KACrC,gBAAgB;KAChB,SAAS;KACT,YAAY;KACZ,gBAAgB;IAClB;cATF,CAWE,oBAAC,QAAD;KACE,OAAO;MACL,YAAY,OAAO,WAAW,WAAW;MACzC,UAAU,OAAO,WAAW,SAAS;KACvC;eAEC,SAAS;IACN,CAAA,GACN,qBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,KAAK,OAAO,QAAQ;KAAG;eAAtD,CACG,eACC,oBAAC,UAAD;MACE,cAAW;MACX,OAAO;OACL,YAAY,OAAO,OAAO,YAAY;OACtC,OAAO,OAAO,OAAO,KAAK;OAC1B,QAAQ;OACR,cAAc,OAAO,aAAa;OAClC,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;OAChD,UAAU,OAAO,WAAW,SAAS;OACrC,QAAQ;OACR,YAAY,OAAO,WAAW,WAAW;OACzC,YAAY;MACd;gBACD;KAEO,CAAA,GAET,eAAe,cAAc,SAAS,KACrC,qBAAC,UAAD;MACE,eAAe,eAAe,aAAa;MAC3C,cAAY,UAAU,cAAc,OAAO;MAC3C,OAAO;OACL,YAAY,GAAG,OAAO,OAAO,OAAO,SAAS;OAC7C,OAAO,OAAO,OAAO,OAAO;OAC5B,QAAQ,aAAa,OAAO,OAAO,OAAO,SAAS;OACnD,cAAc,OAAO,aAAa;OAClC,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;OAChD,UAAU,OAAO,WAAW,SAAS;OACrC,QAAQ;OACR,YAAY,OAAO,WAAW,WAAW;OACzC,YAAY;MACd;gBAbF;OAcC;OACU,cAAc;OAAO;MACxB;OAEP;MACF;;IAGH,cAAc,oBACd,qBAAC,OAAD;IACE,OAAO;KACL,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,cAAc,aAAa,OAAO,OAAO,OAAO;KAChD,SAAS;KACT,YAAY;KACZ,KAAK,OAAO,QAAQ;KACpB,YAAY,OAAO,OAAO,WAAW;IACvC;cARF,CAUG,mBAAmB,oBAAoB,UACtC,qBAAC,OAAD;KACE,cAAW;KACX,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK,OAAO,QAAQ;MACpB,UAAU,OAAO,WAAW,SAAS;MACrC,MAAM;KACR;eARF,CAUE,oBAAC,QAAD;MACE,MAAK;MACL,UAAU;MACV,cAAW;MACX,eAAe,WAAW,CAAC;MAC3B,YAAY,MAAM;OAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,CAAC;MACrC;MACA,OAAO;OACL,QAAQ;OACR,OAAO,OAAO,OAAO,YAAY;OACjC,YAAY,OAAO,WAAW,WAAW;MAC3C;gBACD;KAEK,CAAA,GACL,YAAY,KAAK,KAAK,QACrB,qBAAC,MAAM,UAAP,EAAA,UAAA,CACE,oBAAC,QAAD;MACE,eAAY;MACZ,OAAO,EAAE,OAAO,OAAO,OAAO,KAAK,MAAM;gBAC1C;KAEK,CAAA,GACN,oBAAC,QAAD;MACE,MAAK;MACL,UAAU;MACV,cAAY,eAAe;MAC3B,eAAe,WAAW,MAAM,CAAC;MACjC,YAAY,MAAM;OAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM,CAAC;MAC3C;MACA,OAAO;OACL,QAAQ;OACR,OACE,QAAQ,YAAY,SAAS,IACzB,OAAO,OAAO,KAAK,UACnB,OAAO,OAAO,YAAY;OAChC,YACE,QAAQ,YAAY,SAAS,IACzB,OAAO,WAAW,WAAW,SAC7B,OAAO,WAAW,WAAW;MACrC;gBAEC;KACG,CAAA,CACQ,EAAA,GA7BK,GA6BL,CACjB,CACE;QAEN,cACC,oBAAC,SAAD;KACE,MAAK;KACL,OAAO;KACP,WAAW,MAAM,eAAe,EAAE,OAAO,KAAK;KAC9C,aAAY;KACZ,cAAW;KACX,OAAO;MACL,YAAY,OAAO,OAAO,WAAW;MACrC,OAAO,OAAO,OAAO,KAAK;MAC1B,QAAQ,aAAa,OAAO,OAAO,OAAO;MAC1C,cAAc,OAAO,aAAa;MAClC,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;MAChD,UAAU,OAAO,WAAW,SAAS;MACrC,SAAS;MACT,UAAU;MACV,YAAY;KACd;IACD,CAAA,CAEA;;GAIP,qBAAC,OAAD;IACE,MAAK;IACL,OAAO;KACL,SAAS;KACT,qBAAqB,cAAc,kBAAkB;KACrD,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,cAAc,aAAa,OAAO,OAAO,OAAO;KAChD,YAAY,OAAO,OAAO,WAAW;KACrC,UAAU,OAAO,WAAW,SAAS;KACrC,OAAO,OAAO,OAAO,KAAK;KAC1B,YAAY,OAAO,WAAW,WAAW;KACzC,eAAe;KACf,eAAe;IACjB;cAbF,CAeE,qBAAC,QAAD;KACE,MAAK;KACL,UAAU;KACV,cAAW;KACX,eAAe,WAAW,MAAM;KAChC,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM;KAC1C;KACA,OAAO,EAAE,QAAQ,UAAU;eAR7B,CASC,SACO,WAAW,WAAW,YAAY,QAAQ,MAAW,IACvD;QACL,eACC,qBAAA,UAAA,EAAA,UAAA,CACE,qBAAC,QAAD;KACE,MAAK;KACL,UAAU;KACV,cAAW;KACX,eAAe,WAAW,MAAM;KAChC,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM;KAC1C;KACA,OAAO;MAAE,QAAQ;MAAW,UAAU;MAAI,WAAW;KAAQ;eAR/D;MASC;MACM;MACJ,WAAW,WAAW,YAAY,QAAQ,MAAW;KAClD;QACN,qBAAC,QAAD;KACE,MAAK;KACL,UAAU;KACV,cAAW;KACX,eAAe,WAAW,UAAU;KACpC,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,UAAU;KAC9C;KACA,OAAO;MAAE,QAAQ;MAAW,UAAU;MAAK,WAAW;KAAQ;eARhE;MASC;MACU;MACR,WAAW,eACT,YAAY,QAAQ,MAAW;KAC9B;MACN,EAAA,CAAA,CAED;;GAGL,qBAAC,OAAD;IACE,KAAK;IACL,OAAO;KACL,MAAM;KACN,UAAU;KACV,UAAU;KACV,QAAQ,aACJ,cAAc,OAAO,OAAO,YAAY,YACxC;IACN;IACA,WAAW,MAAM,aAAa,EAAE,cAAc,SAAS;IACvD,YACE,eACK,MAAM;KACL,EAAE,eAAe;KACjB,cAAc,IAAI;IACpB,IACA,KAAA;IAEN,aAAa,oBAAoB,cAAc,KAAK,IAAI,KAAA;IACxD,QAAQ,cAAc,aAAa,KAAA;cApBrC,CAsBG,cACC,oBAAC,OAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO;MACP,YAAY,GAAG,OAAO,OAAO,YAAY,QAAQ;MACjD,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,UAAU,OAAO,WAAW,SAAS;MACrC,OAAO,OAAO,OAAO,YAAY;MACjC,YAAY,OAAO,WAAW,WAAW;MACzC,QAAQ;KACV;eACD;IAEI,CAAA,GAEN,aAAa,WAAW,IACvB,oBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,QAAQ;MACR,OAAO,OAAO,OAAO,KAAK;MAC1B,UAAU,OAAO,WAAW,SAAS;KACvC;eAEC,cAAc,sBAAsB;IAClC,CAAA,IAEL,oBAAC,OAAD;KACE,OAAO;MAAE,QAAQ,YAAY;MAAa,UAAU;KAAW;eAE/D,oBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,KAAK,WAAW;OAChB,MAAM;OACN,OAAO;MACT;gBAEC,oBAAoB,KAAK,EAAE,MAAM,YAChC,qBAAC,OAAD;OACE,MAAK;OACL,UAAU;OACV,cAAY,GAAG,KAAK,cAAc,WAAW,OAAO,IAAI,KAAK;OAC7D,iBAAe,SAAS,IAAI,KAAK,IAAI;OAErC,eAAe,gBAAgB,IAAI;OACnC,qBAAqB,sBAAsB,IAAI;OAC/C,YAAY,MAAM;QAChB,IAAI,EAAE,QAAQ,SAAS,sBAAsB,IAAI;QACjD,IAAI,EAAE,QAAQ,KAAK;SACjB,EAAE,eAAe;SACjB,gBAAgB,IAAI;QACtB;OACF;OACA,OAAO;QACL,SAAS;QACT,qBAAqB,cAAc,kBAAkB;QACrD,SAAS,KAAK,OAAO,QAAQ;QAC7B,aAAa,QAAQ,OAAO,QAAQ,IAAI,KAAK,QAAQ,GAAG;QACxD,QAAQ,OAAO,YAAY;QAC3B,YAAY;QACZ,cAAc,aAAa,OAAO,OAAO,OAAO;QAChD,QAAQ;QACR,YAAY,SAAS,IAAI,KAAK,IAAI,IAC9B,GAAG,OAAO,OAAO,YAAY,QAAQ,MACrC;QACJ,YAAY;OACd;OACA,eAAe,MAAM;QACnB,IAAI,CAAC,SAAS,IAAI,KAAK,IAAI,GACzB,EAAG,cAAiC,MAAM,aACxC,OAAO,OAAO,WAAW;OAC/B;OACA,eAAe,MAAM;QACnB,EAAG,cAAiC,MAAM,aACxC,SAAS,IAAI,KAAK,IAAI,IAClB,GAAG,OAAO,OAAO,YAAY,QAAQ,MACrC;OACR;iBAvCF,CAyCE,qBAAC,QAAD;QACE,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK,OAAO,QAAQ;SACpB,UAAU;QACZ;kBANF;SAQG,eACC,oBAAC,SAAD;UACE,MAAK;UACL,SAAS,SAAS,IAAI,KAAK,IAAI;UAC/B,gBAAgB,aAAa,KAAK,IAAI;UACtC,UAAU,MAAM,EAAE,gBAAgB;UAClC,cAAY,UAAU,KAAK;UAC3B,OAAO,EACL,aAAa,OAAO,OAAO,YAAY,QACzC;SACD,CAAA;SAEF,oBAAoB,UAAU,KAAK,eAClC,oBAAC,QAAD;UACE,eAAY;UACZ,OAAO;WACL,UAAU,OAAO,WAAW,SAAS;WACrC,OAAO,OAAO,OAAO,KAAK;WAC1B,OAAO;WACP,WAAW;UACb;oBAEC,SAAS,IAAI,KAAK,IAAI,IAAI,MAAW;SAClC,CAAA;SAER,oBAAC,QAAD;UACE,eAAY;UACZ,OAAO;WACL,SAAS;WACT,YAAY;WACZ,UAAU,OAAO,WAAW,SAAS;UACvC;oBAEA,oBAAC,WAAD;WACE,MAAM,gBAAgB,IAAI;WAC1B,MAAK;WACL,OAAM;WACN,OAAO,EAAE,UAAU,UAAU;UAC9B,CAAA;SACG,CAAA;SACN,oBAAC,QAAD;UACE,OAAO;WACL,UAAU,OAAO,WAAW,SAAS;WACrC,YAAY,KAAK,cACb,OAAO,WAAW,WAAW,SAC7B,OAAO,WAAW,WAAW;WACjC,OAAO,KAAK,cACR,OAAO,OAAO,YAAY,UAC1B,OAAO,OAAO,KAAK;WACvB,UAAU;WACV,cAAc;WACd,YAAY;UACd;oBAEC,KAAK;SACF,CAAA;QACF;WACL,eACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;QACE,OAAO;SACL,UAAU,OAAO,WAAW,SAAS;SACrC,OAAO,OAAO,OAAO,KAAK;SAC1B,WAAW;SACX,UAAU;SACV,YAAY,OAAO,WAAW,WAAW;QAC3C;kBAEC,KAAK,cAAc,MAAM,eAAe,KAAK,IAAI;OAC9C,CAAA,GACN,oBAAC,QAAD;QACE,OAAO;SACL,UAAU,OAAO,WAAW,SAAS;SACrC,OAAO,OAAO,OAAO,KAAK;SAC1B,WAAW;SACX,UAAU;SACV,YAAY,OAAO,WAAW,WAAW;QAC3C;kBAEC,WAAW,KAAK,QAAQ;OACrB,CAAA,CACN,EAAA,CAAA,CAED;SA/HE,KAAK,IA+HP,CACN;KACE,CAAA;IACF,CAAA,CAEJ;;GAGL,qBAAC,OAAD;IACE,OAAO;KACL,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,WAAW,aAAa,OAAO,OAAO,OAAO;KAC7C,YAAY,OAAO,OAAO,WAAW;KACrC,UAAU,OAAO,WAAW,SAAS;KACrC,OAAO,OAAO,OAAO,KAAK;KAC1B,SAAS;KACT,gBAAgB;IAClB;cATF,CAWE,qBAAC,QAAD,EAAA,UAAA,CAAO,aAAa,QAAO,QAAY,EAAA,CAAA,GACtC,SAAS,OAAO,KAAK,qBAAC,QAAD,EAAA,UAAA,CAAO,SAAS,MAAK,WAAe,EAAA,CAAA,CACvD;;EACF;;AAET,CAAC"}
|
|
1
|
+
{"version":3,"file":"FileExplorer.js","names":[],"sources":["../../../../src/react/core/widgets/FileExplorer.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - FileExplorer Component\r\n *\r\n * Hierarchical file browser for mission data management and file operations.\r\n * Supports tree navigation, file actions (download, delete, upload),\r\n * search, breadcrumbs, and multiple selection.\r\n *\r\n * Features:\r\n * - Tree view and list view modes\r\n * - Breadcrumb navigation\r\n * - File type icons\r\n * - Search/filter\r\n * - Multi-select with checkboxes\r\n * - File actions (open, download, delete, rename)\r\n * - Upload dropzone\r\n * - File size and modified date display\r\n * - Sort by name, size, date\r\n *\r\n * @example\r\n * ```tsx\r\n * <FileExplorer\r\n * files={fileTree}\r\n * onFileOpen={(file) => console.log('Open:', file)}\r\n * onFileDownload={(file) => console.log('Download:', file)}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { useState, useCallback, useMemo, memo } from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { AstroIcon, type AstroIconName } from \"../display/AstroIcon\";\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface FileNode {\r\n /** File/folder name */\r\n name: string;\r\n /** Full path */\r\n path: string;\r\n /** Whether this is a directory */\r\n isDirectory: boolean;\r\n /** File size in bytes (for files) */\r\n size?: number;\r\n /** Last modified date */\r\n modified?: string | Date;\r\n /** MIME type */\r\n mimeType?: string;\r\n /** Children (for directories) */\r\n children?: FileNode[];\r\n /** Custom icon name */\r\n icon?: string;\r\n /** Whether the file is readonly */\r\n readonly?: boolean;\r\n}\r\n\r\nexport type FileViewMode = \"tree\" | \"list\";\r\nexport type FileSortBy = \"name\" | \"size\" | \"modified\";\r\nexport type FileSortDir = \"asc\" | \"desc\";\r\n\r\nexport interface FileExplorerProps {\r\n /** Root file tree */\r\n files: FileNode[];\r\n /** Height (default: 500) */\r\n height?: number | string;\r\n /** Title */\r\n title?: string;\r\n /** View mode (default: 'list') */\r\n viewMode?: FileViewMode;\r\n /** Show search bar (default: true) */\r\n showSearch?: boolean;\r\n /** Show breadcrumbs (default: true) */\r\n showBreadcrumbs?: boolean;\r\n /** Show file details (size, date) (default: true) */\r\n showDetails?: boolean;\r\n /** Allow multi-select (default: false) */\r\n multiSelect?: boolean;\r\n /** Allow upload (default: false) */\r\n allowUpload?: boolean;\r\n /** Allow delete (default: false) */\r\n allowDelete?: boolean;\r\n /** Called when a file is opened/double-clicked */\r\n onFileOpen?: (file: FileNode) => void;\r\n /** Called when download is requested */\r\n onFileDownload?: (file: FileNode) => void;\r\n /** Called when delete is requested */\r\n onFileDelete?: (files: FileNode[]) => void;\r\n /** Called when files are uploaded */\r\n onFileUpload?: (files: File[], targetPath: string) => void;\r\n /** Called when selection changes */\r\n onSelectionChange?: (files: FileNode[]) => void;\r\n /** CSS class */\r\n className?: string;\r\n /** Show outer container border (default: true). Set false when embedded inside a Container/card. */\r\n bordered?: boolean;\r\n /** Custom style overrides for the outer container */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n// =============================================================================\r\n// Helpers\r\n// =============================================================================\r\n\r\nfunction formatFileSize(bytes?: number): string {\r\n if (bytes === undefined || bytes === null) return \"—\";\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;\r\n}\r\n\r\nfunction formatDate(date?: string | Date): string {\r\n if (!date) return \"—\";\r\n const d = typeof date === \"string\" ? new Date(date) : date;\r\n if (isNaN(d.getTime())) return String(date);\r\n return (\r\n d.toLocaleDateString(\"en-US\", {\r\n month: \"short\",\r\n day: \"2-digit\",\r\n year: \"numeric\",\r\n }) +\r\n \" \" +\r\n d.toLocaleTimeString(\"en-US\", {\r\n hour12: false,\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n })\r\n );\r\n}\r\n\r\nfunction getFileIconName(file: FileNode): AstroIconName {\r\n if (file.isDirectory) return \"folder\";\r\n const ext = file.name.split(\".\").pop()?.toLowerCase() ?? \"\";\r\n const iconMap: Record<string, AstroIconName> = {\r\n rb: \"code\",\r\n py: \"code\",\r\n js: \"code\",\r\n ts: \"code\",\r\n jsx: \"code\",\r\n tsx: \"code\",\r\n json: \"insert-drive-file\",\r\n yaml: \"insert-drive-file\",\r\n yml: \"insert-drive-file\",\r\n xml: \"insert-drive-file\",\r\n csv: \"table-chart\",\r\n txt: \"short-text\",\r\n md: \"description\",\r\n log: \"insert-drive-file\",\r\n gz: \"archive\",\r\n zip: \"archive\",\r\n tar: \"archive\",\r\n bin: \"storage\",\r\n dat: \"storage\",\r\n png: \"image\",\r\n jpg: \"image\",\r\n gif: \"image\",\r\n svg: \"image\",\r\n pdf: \"picture-as-pdf\",\r\n doc: \"insert-drive-file\",\r\n xls: \"table-chart\",\r\n ppt: \"insert-drive-file\",\r\n };\r\n return iconMap[ext] || \"insert-drive-file\";\r\n}\r\n\r\nfunction flattenVisible(\r\n files: FileNode[],\r\n expanded: Set<string>,\r\n depth: number = 0,\r\n): { file: FileNode; depth: number }[] {\r\n const result: { file: FileNode; depth: number }[] = [];\r\n for (const f of files) {\r\n result.push({ file: f, depth });\r\n if (f.isDirectory && expanded.has(f.path) && f.children) {\r\n result.push(...flattenVisible(f.children, expanded, depth + 1));\r\n }\r\n }\r\n return result;\r\n}\r\n\r\nfunction filterFiles(files: FileNode[], query: string): FileNode[] {\r\n const q = query.toLowerCase();\r\n return files.reduce<FileNode[]>((acc, f) => {\r\n if (f.isDirectory && f.children) {\r\n const filteredChildren = filterFiles(f.children, query);\r\n if (filteredChildren.length > 0 || f.name.toLowerCase().includes(q)) {\r\n acc.push({ ...f, children: filteredChildren });\r\n }\r\n } else if (f.name.toLowerCase().includes(q)) {\r\n acc.push(f);\r\n }\r\n return acc;\r\n }, []);\r\n}\r\n\r\nfunction sortFiles(\r\n files: FileNode[],\r\n sortBy: FileSortBy,\r\n sortDir: FileSortDir,\r\n): FileNode[] {\r\n const sorted = [...files].sort((a, b) => {\r\n // Directories first\r\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\r\n let cmp = 0;\r\n if (sortBy === \"name\") cmp = a.name.localeCompare(b.name);\r\n else if (sortBy === \"size\") cmp = (a.size ?? 0) - (b.size ?? 0);\r\n else if (sortBy === \"modified\") {\r\n const da = a.modified ? new Date(a.modified).getTime() : 0;\r\n const db = b.modified ? new Date(b.modified).getTime() : 0;\r\n cmp = da - db;\r\n }\r\n return sortDir === \"asc\" ? cmp : -cmp;\r\n });\r\n return sorted.map((f) =>\r\n f.isDirectory && f.children\r\n ? { ...f, children: sortFiles(f.children, sortBy, sortDir) }\r\n : f,\r\n );\r\n}\r\n\r\n// =============================================================================\r\n// Component\r\n// =============================================================================\r\n\r\nexport const FileExplorer = memo(function FileExplorer({\r\n files,\r\n height = 500,\r\n title,\r\n viewMode: initialViewMode = \"list\",\r\n showSearch = true,\r\n showBreadcrumbs = true,\r\n showDetails = true,\r\n multiSelect = false,\r\n allowUpload = false,\r\n allowDelete = false,\r\n onFileOpen,\r\n onFileDownload: _onFileDownload,\r\n onFileDelete,\r\n onFileUpload,\r\n onSelectionChange: _onSelectionChange,\r\n className = \"\",\r\n bordered = true,\r\n style: styleProp,\r\n}: FileExplorerProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme =\r\n theme === \"transparent\" ||\r\n theme === \"transparent-bold\" ||\r\n theme === \"transparent-minimal\";\r\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\r\n const [selected, setSelected] = useState<Set<string>>(new Set());\r\n const [currentPath, setCurrentPath] = useState<string[]>([]);\r\n const [searchQuery, setSearchQuery] = useState(\"\");\r\n const [sortBy, setSortBy] = useState<FileSortBy>(\"name\");\r\n const [sortDir, setSortDir] = useState<FileSortDir>(\"asc\");\r\n const [isDragging, setIsDragging] = useState(false);\r\n const [scrollTop, setScrollTop] = useState(0);\r\n const [viewportHeight, setViewportHeight] = useState(240);\r\n const listRef = React.useRef<HTMLDivElement>(null);\r\n\r\n const processedFiles = useMemo(() => {\r\n let result = files;\r\n if (searchQuery.trim()) result = filterFiles(result, searchQuery);\r\n return sortFiles(result, sortBy, sortDir);\r\n }, [files, searchQuery, sortBy, sortDir]);\r\n\r\n // Navigate to directory by breadcrumb\r\n const currentFiles = useMemo(() => {\r\n let current = processedFiles;\r\n for (const dir of currentPath) {\r\n const found = current.find((f) => f.isDirectory && f.name === dir);\r\n if (found && found.children) current = found.children;\r\n else break;\r\n }\r\n return current;\r\n }, [processedFiles, currentPath]);\r\n\r\n const visibleItems = useMemo(() => {\r\n if (initialViewMode === \"tree\")\r\n return flattenVisible(processedFiles, expanded);\r\n return currentFiles.map((f) => ({ file: f, depth: 0 }));\r\n }, [processedFiles, expanded, currentFiles, initialViewMode]);\r\n\r\n const toggleExpand = useCallback((path: string) => {\r\n setExpanded((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(path)) next.delete(path);\r\n else next.add(path);\r\n return next;\r\n });\r\n }, []);\r\n\r\n const toggleSelect = useCallback(\r\n (path: string) => {\r\n setSelected((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(path)) next.delete(path);\r\n else {\r\n if (!multiSelect) next.clear();\r\n next.add(path);\r\n }\r\n return next;\r\n });\r\n },\r\n [multiSelect],\r\n );\r\n\r\n const handleFileClick = useCallback(\r\n (file: FileNode) => {\r\n if (file.isDirectory) {\r\n if (initialViewMode === \"list\") {\r\n setCurrentPath((prev) => [...prev, file.name]);\r\n } else {\r\n toggleExpand(file.path);\r\n }\r\n } else {\r\n toggleSelect(file.path);\r\n }\r\n },\r\n [initialViewMode, toggleExpand, toggleSelect],\r\n );\r\n\r\n const handleFileDoubleClick = useCallback(\r\n (file: FileNode) => {\r\n if (!file.isDirectory) onFileOpen?.(file);\r\n },\r\n [onFileOpen],\r\n );\r\n\r\n const navigateTo = useCallback((idx: number) => {\r\n setCurrentPath((prev) => prev.slice(0, idx));\r\n }, []);\r\n\r\n const handleDrop = useCallback(\r\n (e: React.DragEvent) => {\r\n e.preventDefault();\r\n setIsDragging(false);\r\n if (!allowUpload || !onFileUpload) return;\r\n const droppedFiles = Array.from(e.dataTransfer.files);\r\n if (droppedFiles.length > 0) {\r\n onFileUpload(droppedFiles, \"/\" + currentPath.join(\"/\"));\r\n }\r\n },\r\n [allowUpload, onFileUpload, currentPath],\r\n );\r\n\r\n const handleSort = useCallback(\r\n (col: FileSortBy) => {\r\n if (sortBy === col) setSortDir((d) => (d === \"asc\" ? \"desc\" : \"asc\"));\r\n else {\r\n setSortBy(col);\r\n setSortDir(\"asc\");\r\n }\r\n },\r\n [sortBy],\r\n );\r\n\r\n const selectedFiles = useMemo(\r\n () =>\r\n visibleItems.filter((v) => selected.has(v.file.path)).map((v) => v.file),\r\n [visibleItems, selected],\r\n );\r\n const rowHeightPx = Number.parseInt(String(tokens.elementSize.sm), 10) || 32;\r\n const overscanRows = 12;\r\n const totalRows = visibleItems.length;\r\n const startRow = Math.max(\r\n 0,\r\n Math.floor(scrollTop / rowHeightPx) - overscanRows,\r\n );\r\n const visibleRows =\r\n Math.ceil(viewportHeight / rowHeightPx) + overscanRows * 2;\r\n const endRow = Math.min(totalRows, startRow + visibleRows);\r\n const virtualVisibleItems = useMemo(\r\n () => visibleItems.slice(startRow, endRow),\r\n [visibleItems, startRow, endRow],\r\n );\r\n\r\n React.useEffect(() => {\r\n const el = listRef.current;\r\n if (!el) return undefined;\r\n\r\n const updateHeight = () =>\r\n setViewportHeight(Math.max(rowHeightPx * 4, el.clientHeight || 240));\r\n updateHeight();\r\n\r\n if (typeof ResizeObserver !== \"undefined\") {\r\n const observer = new ResizeObserver(updateHeight);\r\n observer.observe(el);\r\n return () => observer.disconnect();\r\n }\r\n\r\n window.addEventListener(\"resize\", updateHeight);\r\n return () => window.removeEventListener(\"resize\", updateHeight);\r\n }, [rowHeightPx]);\r\n\r\n return (\r\n <div\r\n className={`file-explorer ${className}`}\r\n style={{\r\n height: typeof height === \"number\" ? height : undefined,\r\n background: tokens.colors.background.base,\r\n ...(bordered\r\n ? (tokens.colors.border.cardStyle ?? {\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n })\r\n : { border: \"none\" }),\r\n borderRadius: tokens.borderRadius.lg,\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n color: tokens.colors.text.primary,\r\n backdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\r\n WebkitBackdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\r\n ...styleProp,\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface,\r\n backdropFilter: \"blur(8px)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {title || \"File Explorer\"}\r\n </span>\r\n <div style={{ display: \"flex\", gap: tokens.spacing.sm }}>\r\n {allowUpload && (\r\n <button\r\n aria-label=\"Upload files\"\r\n style={{\r\n background: tokens.colors.interactive.default,\r\n color: tokens.colors.text.inverse,\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n transition: \"opacity 150ms ease\",\r\n }}\r\n >\r\n Upload\r\n </button>\r\n )}\r\n {allowDelete && selectedFiles.length > 0 && (\r\n <button\r\n onClick={() => onFileDelete?.(selectedFiles)}\r\n aria-label={`Delete ${selectedFiles.length} selected files`}\r\n style={{\r\n background: `${tokens.colors.status.critical}22`,\r\n color: tokens.colors.status.critical,\r\n border: `1px solid ${tokens.colors.status.critical}44`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n transition: \"opacity 150ms ease\",\r\n }}\r\n >\r\n Delete ({selectedFiles.length})\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Toolbar: search + breadcrumbs */}\r\n {(showSearch || showBreadcrumbs) && (\r\n <div\r\n style={{\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n background: tokens.colors.background.surface,\r\n }}\r\n >\r\n {showBreadcrumbs && initialViewMode === \"list\" && (\r\n <nav\r\n aria-label=\"File path\"\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n fontSize: tokens.typography.fontSize.xs,\r\n flex: 1,\r\n }}\r\n >\r\n <span\r\n role=\"button\"\r\n tabIndex={0}\r\n aria-label=\"Navigate to root\"\r\n onClick={() => navigateTo(0)}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") navigateTo(0);\r\n }}\r\n style={{\r\n cursor: \"pointer\",\r\n color: tokens.colors.interactive.default,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n }}\r\n >\r\n /\r\n </span>\r\n {currentPath.map((dir, idx) => (\r\n <React.Fragment key={idx}>\r\n <span\r\n aria-hidden=\"true\"\r\n style={{ color: tokens.colors.text.muted }}\r\n >\r\n /\r\n </span>\r\n <span\r\n role=\"button\"\r\n tabIndex={0}\r\n aria-label={`Navigate to ${dir}`}\r\n onClick={() => navigateTo(idx + 1)}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") navigateTo(idx + 1);\r\n }}\r\n style={{\r\n cursor: \"pointer\",\r\n color:\r\n idx === currentPath.length - 1\r\n ? tokens.colors.text.primary\r\n : tokens.colors.interactive.default,\r\n fontWeight:\r\n idx === currentPath.length - 1\r\n ? tokens.typography.fontWeight.medium\r\n : tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n {dir}\r\n </span>\r\n </React.Fragment>\r\n ))}\r\n </nav>\r\n )}\r\n {showSearch && (\r\n <input\r\n type=\"text\"\r\n value={searchQuery}\r\n onChange={(e) => setSearchQuery(e.target.value)}\r\n placeholder=\"Search files...\"\r\n aria-label=\"Search files\"\r\n style={{\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n outline: \"none\",\r\n minWidth: 150,\r\n transition: \"border-color 150ms ease\",\r\n }}\r\n />\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Column headers */}\r\n <div\r\n role=\"row\"\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: showDetails ? \"1fr auto auto\" : \"1fr\",\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.muted,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n textTransform: \"uppercase\",\r\n letterSpacing: \"0.5px\",\r\n }}\r\n >\r\n <span\r\n role=\"columnheader\"\r\n tabIndex={0}\r\n aria-label=\"Sort by name\"\r\n onClick={() => handleSort(\"name\")}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") handleSort(\"name\");\r\n }}\r\n style={{ cursor: \"pointer\" }}\r\n >\r\n Name {sortBy === \"name\" && (sortDir === \"asc\" ? \"\\u25B2\" : \"\\u25BC\")}\r\n </span>\r\n {showDetails && (\r\n <>\r\n <span\r\n role=\"columnheader\"\r\n tabIndex={0}\r\n aria-label=\"Sort by size\"\r\n onClick={() => handleSort(\"size\")}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") handleSort(\"size\");\r\n }}\r\n style={{ cursor: \"pointer\", minWidth: 70, textAlign: \"right\" }}\r\n >\r\n Size{\" \"}\r\n {sortBy === \"size\" && (sortDir === \"asc\" ? \"\\u25B2\" : \"\\u25BC\")}\r\n </span>\r\n <span\r\n role=\"columnheader\"\r\n tabIndex={0}\r\n aria-label=\"Sort by modified date\"\r\n onClick={() => handleSort(\"modified\")}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") handleSort(\"modified\");\r\n }}\r\n style={{ cursor: \"pointer\", minWidth: 140, textAlign: \"right\" }}\r\n >\r\n Modified{\" \"}\r\n {sortBy === \"modified\" &&\r\n (sortDir === \"asc\" ? \"\\u25B2\" : \"\\u25BC\")}\r\n </span>\r\n </>\r\n )}\r\n </div>\r\n\r\n {/* File list */}\r\n <div\r\n ref={listRef}\r\n style={{\r\n flex: 1,\r\n overflow: \"auto\",\r\n position: \"relative\",\r\n border: isDragging\r\n ? `2px dashed ${tokens.colors.interactive.default}`\r\n : \"2px solid transparent\",\r\n }}\r\n onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}\r\n onDragOver={\r\n allowUpload\r\n ? (e) => {\r\n e.preventDefault();\r\n setIsDragging(true);\r\n }\r\n : undefined\r\n }\r\n onDragLeave={allowUpload ? () => setIsDragging(false) : undefined}\r\n onDrop={allowUpload ? handleDrop : undefined}\r\n >\r\n {isDragging && (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n background: `${tokens.colors.interactive.default}11`,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontSize: tokens.typography.fontSize.sm,\r\n color: tokens.colors.interactive.default,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n zIndex: 10,\r\n }}\r\n >\r\n Drop files to upload\r\n </div>\r\n )}\r\n {visibleItems.length === 0 ? (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n height: \"100%\",\r\n color: tokens.colors.text.muted,\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {searchQuery ? \"No matching files\" : \"Empty directory\"}\r\n </div>\r\n ) : (\r\n <div\r\n style={{ height: totalRows * rowHeightPx, position: \"relative\" }}\r\n >\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n top: startRow * rowHeightPx,\r\n left: 0,\r\n right: 0,\r\n }}\r\n >\r\n {virtualVisibleItems.map(({ file, depth }) => (\r\n <div\r\n role=\"treeitem\"\r\n tabIndex={0}\r\n aria-label={`${file.isDirectory ? \"Folder\" : \"File\"}: ${file.name}`}\r\n aria-selected={selected.has(file.path)}\r\n key={file.path}\r\n onClick={() => handleFileClick(file)}\r\n onDoubleClick={() => handleFileDoubleClick(file)}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") handleFileDoubleClick(file);\r\n if (e.key === \" \") {\r\n e.preventDefault();\r\n handleFileClick(file);\r\n }\r\n }}\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: showDetails ? \"1fr auto auto\" : \"1fr\",\r\n padding: `0 ${tokens.spacing.smd}`,\r\n paddingLeft: `calc(${tokens.spacing.smd} + ${depth * 20}px)`,\r\n height: tokens.elementSize.sm,\r\n alignItems: \"center\",\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n cursor: \"pointer\",\r\n background: selected.has(file.path)\r\n ? `${tokens.colors.interactive.default}15`\r\n : \"transparent\",\r\n transition: \"background 150ms ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!selected.has(file.path))\r\n (e.currentTarget as HTMLDivElement).style.background =\r\n tokens.colors.background.surface;\r\n }}\r\n onMouseLeave={(e) => {\r\n (e.currentTarget as HTMLDivElement).style.background =\r\n selected.has(file.path)\r\n ? `${tokens.colors.interactive.default}15`\r\n : \"transparent\";\r\n }}\r\n >\r\n <span\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n overflow: \"hidden\",\r\n }}\r\n >\r\n {multiSelect && (\r\n <input\r\n type=\"checkbox\"\r\n checked={selected.has(file.path)}\r\n onChange={() => toggleSelect(file.path)}\r\n onClick={(e) => e.stopPropagation()}\r\n aria-label={`Select ${file.name}`}\r\n style={{\r\n accentColor: tokens.colors.interactive.default,\r\n }}\r\n />\r\n )}\r\n {initialViewMode === \"tree\" && file.isDirectory && (\r\n <span\r\n aria-hidden=\"true\"\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.muted,\r\n width: 12,\r\n textAlign: \"center\",\r\n }}\r\n >\r\n {expanded.has(file.path) ? \"\\u25BC\" : \"\\u25B6\"}\r\n </span>\r\n )}\r\n <span\r\n aria-hidden=\"true\"\r\n style={{\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n <AstroIcon\r\n name={getFileIconName(file)}\r\n size=\"small\"\r\n label=\"\"\r\n style={{ fontSize: \"inherit\" }}\r\n />\r\n </span>\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: file.isDirectory\r\n ? tokens.typography.fontWeight.medium\r\n : tokens.typography.fontWeight.normal,\r\n color: file.isDirectory\r\n ? tokens.colors.interactive.default\r\n : tokens.colors.text.primary,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {file.name}\r\n </span>\r\n </span>\r\n {showDetails && (\r\n <>\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.muted,\r\n textAlign: \"right\",\r\n minWidth: 70,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n >\r\n {file.isDirectory ? \"—\" : formatFileSize(file.size)}\r\n </span>\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.muted,\r\n textAlign: \"right\",\r\n minWidth: 140,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n >\r\n {formatDate(file.modified)}\r\n </span>\r\n </>\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Status bar */}\r\n <div\r\n style={{\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\r\n borderTop: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.muted,\r\n display: \"flex\",\r\n justifyContent: \"space-between\",\r\n }}\r\n >\r\n <span>{visibleItems.length} items</span>\r\n {selected.size > 0 && <span>{selected.size} selected</span>}\r\n </div>\r\n </div>\r\n );\r\n});\r\n\r\nexport default FileExplorer;\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwGA,SAAS,eAAe,OAAwB;CAC9C,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;CAClD,IAAI,UAAU,GAAG,OAAO;CACxB,MAAM,QAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;CAAI;CAC1C,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;CACrD,OAAO,IAAI,QAAQ,KAAK,IAAI,MAAM,CAAC,EAAA,CAAG,QAAQ,IAAI,IAAI,IAAI,CAAC,EAAE,GAAG,MAAM;AACxE;AAEA,SAAS,WAAW,MAA8B;CAChD,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,IAAI,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;CACtD,IAAI,MAAM,EAAE,QAAQ,CAAC,GAAG,OAAO,OAAO,IAAI;CAC1C,OACE,EAAE,mBAAmB,SAAS;EAC5B,OAAO;EACP,KAAK;EACL,MAAM;CACR,CAAC,IACD,MACA,EAAE,mBAAmB,SAAS;EAC5B,QAAQ;EACR,MAAM;EACN,QAAQ;CACV,CAAC;AAEL;AAEA,SAAS,gBAAgB,MAA+B;CACtD,IAAI,KAAK,aAAa,OAAO;CA+B7B,OAAO;EA5BL,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,KAAK;EACL,KAAK;EACL,MAAM;EACN,MAAM;EACN,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,IAAI;EACJ,KAAK;EACL,IAAI;EACJ,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;CAEA,EA9BK,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,YAAY,KAAK,OA8BlC;AACzB;AAEA,SAAS,eACP,OACA,UACA,QAAgB,GACqB;CACrC,MAAM,SAA8C,CAAC;CACrD,KAAK,MAAM,KAAK,OAAO;EACrB,OAAO,KAAK;GAAE,MAAM;GAAG;EAAM,CAAC;EAC9B,IAAI,EAAE,eAAe,SAAS,IAAI,EAAE,IAAI,KAAK,EAAE,UAC7C,OAAO,KAAK,GAAG,eAAe,EAAE,UAAU,UAAU,QAAQ,CAAC,CAAC;CAElE;CACA,OAAO;AACT;AAEA,SAAS,YAAY,OAAmB,OAA2B;CACjE,MAAM,IAAI,MAAM,YAAY;CAC5B,OAAO,MAAM,QAAoB,KAAK,MAAM;EAC1C,IAAI,EAAE,eAAe,EAAE,UAAU;GAC/B,MAAM,mBAAmB,YAAY,EAAE,UAAU,KAAK;GACtD,IAAI,iBAAiB,SAAS,KAAK,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,GAChE,IAAI,KAAK;IAAE,GAAG;IAAG,UAAU;GAAiB,CAAC;EAEjD,OAAO,IAAI,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,GACxC,IAAI,KAAK,CAAC;EAEZ,OAAO;CACT,GAAG,CAAC,CAAC;AACP;AAEA,SAAS,UACP,OACA,QACA,SACY;CAcZ,OAbe,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM;EAEvC,IAAI,EAAE,gBAAgB,EAAE,aAAa,OAAO,EAAE,cAAc,KAAK;EACjE,IAAI,MAAM;EACV,IAAI,WAAW,QAAQ,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI;OACnD,IAAI,WAAW,QAAQ,OAAO,EAAE,QAAQ,MAAM,EAAE,QAAQ;OACxD,IAAI,WAAW,YAGlB,OAFW,EAAE,WAAW,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI,MAC9C,EAAE,WAAW,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,IAAI;EAG3D,OAAO,YAAY,QAAQ,MAAM,CAAC;CACpC,CACO,CAAA,CAAO,KAAK,MACjB,EAAE,eAAe,EAAE,WACf;EAAE,GAAG;EAAG,UAAU,UAAU,EAAE,UAAU,QAAQ,OAAO;CAAE,IACzD,CACN;AACF;AAMA,IAAa,eAAe,KAAK,SAAS,aAAa,EACrD,OACA,SAAS,KACT,OACA,UAAU,kBAAkB,QAC5B,aAAa,MACb,kBAAkB,MAClB,cAAc,MACd,cAAc,OACd,cAAc,OACd,cAAc,OACd,YACA,gBAAgB,iBAChB,cACA,cACA,mBAAmB,oBACnB,YAAY,IACZ,WAAW,MACX,OAAO,aACiC;CACxC,MAAM,EAAE,QAAQ,UAAU,SAAS;CACnC,MAAM,qBACJ,UAAU,iBACV,UAAU,sBACV,UAAU;CACZ,MAAM,CAAC,UAAU,eAAe,yBAAsB,IAAI,IAAI,CAAC;CAC/D,MAAM,CAAC,UAAU,eAAe,yBAAsB,IAAI,IAAI,CAAC;CAC/D,MAAM,CAAC,aAAa,kBAAkB,SAAmB,CAAC,CAAC;CAC3D,MAAM,CAAC,aAAa,kBAAkB,SAAS,EAAE;CACjD,MAAM,CAAC,QAAQ,aAAa,SAAqB,MAAM;CACvD,MAAM,CAAC,SAAS,cAAc,SAAsB,KAAK;CACzD,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAClD,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC;CAC5C,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,UAAU,MAAM,OAAuB,IAAI;CAEjD,MAAM,iBAAiB,cAAc;EACnC,IAAI,SAAS;EACb,IAAI,YAAY,KAAK,GAAG,SAAS,YAAY,QAAQ,WAAW;EAChE,OAAO,UAAU,QAAQ,QAAQ,OAAO;CAC1C,GAAG;EAAC;EAAO;EAAa;EAAQ;CAAO,CAAC;CAGxC,MAAM,eAAe,cAAc;EACjC,IAAI,UAAU;EACd,KAAK,MAAM,OAAO,aAAa;GAC7B,MAAM,QAAQ,QAAQ,MAAM,MAAM,EAAE,eAAe,EAAE,SAAS,GAAG;GACjE,IAAI,SAAS,MAAM,UAAU,UAAU,MAAM;QACxC;EACP;EACA,OAAO;CACT,GAAG,CAAC,gBAAgB,WAAW,CAAC;CAEhC,MAAM,eAAe,cAAc;EACjC,IAAI,oBAAoB,QACtB,OAAO,eAAe,gBAAgB,QAAQ;EAChD,OAAO,aAAa,KAAK,OAAO;GAAE,MAAM;GAAG,OAAO;EAAE,EAAE;CACxD,GAAG;EAAC;EAAgB;EAAU;EAAc;CAAe,CAAC;CAE5D,MAAM,eAAe,aAAa,SAAiB;EACjD,aAAa,SAAS;GACpB,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI;QAC/B,KAAK,IAAI,IAAI;GAClB,OAAO;EACT,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,MAAM,eAAe,aAClB,SAAiB;EAChB,aAAa,SAAS;GACpB,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI;QAC/B;IACH,IAAI,CAAC,aAAa,KAAK,MAAM;IAC7B,KAAK,IAAI,IAAI;GACf;GACA,OAAO;EACT,CAAC;CACH,GACA,CAAC,WAAW,CACd;CAEA,MAAM,kBAAkB,aACrB,SAAmB;EAClB,IAAI,KAAK,aACP,IAAI,oBAAoB,QACtB,gBAAgB,SAAS,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;OAE7C,aAAa,KAAK,IAAI;OAGxB,aAAa,KAAK,IAAI;CAE1B,GACA;EAAC;EAAiB;EAAc;CAAY,CAC9C;CAEA,MAAM,wBAAwB,aAC3B,SAAmB;EAClB,IAAI,CAAC,KAAK,aAAa,aAAa,IAAI;CAC1C,GACA,CAAC,UAAU,CACb;CAEA,MAAM,aAAa,aAAa,QAAgB;EAC9C,gBAAgB,SAAS,KAAK,MAAM,GAAG,GAAG,CAAC;CAC7C,GAAG,CAAC,CAAC;CAEL,MAAM,aAAa,aAChB,MAAuB;EACtB,EAAE,eAAe;EACjB,cAAc,KAAK;EACnB,IAAI,CAAC,eAAe,CAAC,cAAc;EACnC,MAAM,eAAe,MAAM,KAAK,EAAE,aAAa,KAAK;EACpD,IAAI,aAAa,SAAS,GACxB,aAAa,cAAc,MAAM,YAAY,KAAK,GAAG,CAAC;CAE1D,GACA;EAAC;EAAa;EAAc;CAAW,CACzC;CAEA,MAAM,aAAa,aAChB,QAAoB;EACnB,IAAI,WAAW,KAAK,YAAY,MAAO,MAAM,QAAQ,SAAS,KAAM;OAC/D;GACH,UAAU,GAAG;GACb,WAAW,KAAK;EAClB;CACF,GACA,CAAC,MAAM,CACT;CAEA,MAAM,gBAAgB,cAElB,aAAa,QAAQ,MAAM,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,GACzE,CAAC,cAAc,QAAQ,CACzB;CACA,MAAM,cAAc,OAAO,SAAS,OAAO,OAAO,YAAY,EAAE,GAAG,EAAE,KAAK;CAC1E,MAAM,eAAe;CACrB,MAAM,YAAY,aAAa;CAC/B,MAAM,WAAW,KAAK,IACpB,GACA,KAAK,MAAM,YAAY,WAAW,IAAI,YACxC;CACA,MAAM,cACJ,KAAK,KAAK,iBAAiB,WAAW,IAAI,eAAe;CAC3D,MAAM,SAAS,KAAK,IAAI,WAAW,WAAW,WAAW;CACzD,MAAM,sBAAsB,cACpB,aAAa,MAAM,UAAU,MAAM,GACzC;EAAC;EAAc;EAAU;CAAM,CACjC;CAEA,MAAM,gBAAgB;EACpB,MAAM,KAAK,QAAQ;EACnB,IAAI,CAAC,IAAI,OAAO,KAAA;EAEhB,MAAM,qBACJ,kBAAkB,KAAK,IAAI,cAAc,GAAG,GAAG,gBAAgB,GAAG,CAAC;EACrE,aAAa;EAEb,IAAI,OAAO,mBAAmB,aAAa;GACzC,MAAM,WAAW,IAAI,eAAe,YAAY;GAChD,SAAS,QAAQ,EAAE;GACnB,aAAa,SAAS,WAAW;EACnC;EAEA,OAAO,iBAAiB,UAAU,YAAY;EAC9C,aAAa,OAAO,oBAAoB,UAAU,YAAY;CAChE,GAAG,CAAC,WAAW,CAAC;CAEhB,OACE,qBAAC,OAAD;EACE,WAAW,iBAAiB;EAC5B,OAAO;GACL,QAAQ,OAAO,WAAW,WAAW,SAAS,KAAA;GAC9C,YAAY,OAAO,OAAO,WAAW;GACrC,GAAI,WACC,OAAO,OAAO,OAAO,aAAa,EACjC,QAAQ,aAAa,OAAO,OAAO,OAAO,QAC5C,IACA,EAAE,QAAQ,OAAO;GACrB,cAAc,OAAO,aAAa;GAClC,UAAU;GACV,SAAS;GACT,eAAe;GACf,YAAY,OAAO,WAAW,WAAW;GACzC,OAAO,OAAO,OAAO,KAAK;GAC1B,gBAAgB,qBAAqB,eAAe,KAAA;GACpD,sBAAsB,qBAAqB,eAAe,KAAA;GAC1D,GAAG;EACL;YAnBF;GAsBE,qBAAC,OAAD;IACE,OAAO;KACL,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,cAAc,aAAa,OAAO,OAAO,OAAO;KAChD,YAAY,OAAO,OAAO,WAAW;KACrC,gBAAgB;KAChB,SAAS;KACT,YAAY;KACZ,gBAAgB;IAClB;cATF,CAWE,oBAAC,QAAD;KACE,OAAO;MACL,YAAY,OAAO,WAAW,WAAW;MACzC,UAAU,OAAO,WAAW,SAAS;KACvC;eAEC,SAAS;IACN,CAAA,GACN,qBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,KAAK,OAAO,QAAQ;KAAG;eAAtD,CACG,eACC,oBAAC,UAAD;MACE,cAAW;MACX,OAAO;OACL,YAAY,OAAO,OAAO,YAAY;OACtC,OAAO,OAAO,OAAO,KAAK;OAC1B,QAAQ;OACR,cAAc,OAAO,aAAa;OAClC,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;OAChD,UAAU,OAAO,WAAW,SAAS;OACrC,QAAQ;OACR,YAAY,OAAO,WAAW,WAAW;OACzC,YAAY;MACd;gBACD;KAEO,CAAA,GAET,eAAe,cAAc,SAAS,KACrC,qBAAC,UAAD;MACE,eAAe,eAAe,aAAa;MAC3C,cAAY,UAAU,cAAc,OAAO;MAC3C,OAAO;OACL,YAAY,GAAG,OAAO,OAAO,OAAO,SAAS;OAC7C,OAAO,OAAO,OAAO,OAAO;OAC5B,QAAQ,aAAa,OAAO,OAAO,OAAO,SAAS;OACnD,cAAc,OAAO,aAAa;OAClC,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;OAChD,UAAU,OAAO,WAAW,SAAS;OACrC,QAAQ;OACR,YAAY,OAAO,WAAW,WAAW;OACzC,YAAY;MACd;gBAbF;OAcC;OACU,cAAc;OAAO;MACxB;OAEP;MACF;;IAGH,cAAc,oBACd,qBAAC,OAAD;IACE,OAAO;KACL,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,cAAc,aAAa,OAAO,OAAO,OAAO;KAChD,SAAS;KACT,YAAY;KACZ,KAAK,OAAO,QAAQ;KACpB,YAAY,OAAO,OAAO,WAAW;IACvC;cARF,CAUG,mBAAmB,oBAAoB,UACtC,qBAAC,OAAD;KACE,cAAW;KACX,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK,OAAO,QAAQ;MACpB,UAAU,OAAO,WAAW,SAAS;MACrC,MAAM;KACR;eARF,CAUE,oBAAC,QAAD;MACE,MAAK;MACL,UAAU;MACV,cAAW;MACX,eAAe,WAAW,CAAC;MAC3B,YAAY,MAAM;OAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,CAAC;MACrC;MACA,OAAO;OACL,QAAQ;OACR,OAAO,OAAO,OAAO,YAAY;OACjC,YAAY,OAAO,WAAW,WAAW;MAC3C;gBACD;KAEK,CAAA,GACL,YAAY,KAAK,KAAK,QACrB,qBAAC,MAAM,UAAP,EAAA,UAAA,CACE,oBAAC,QAAD;MACE,eAAY;MACZ,OAAO,EAAE,OAAO,OAAO,OAAO,KAAK,MAAM;gBAC1C;KAEK,CAAA,GACN,oBAAC,QAAD;MACE,MAAK;MACL,UAAU;MACV,cAAY,eAAe;MAC3B,eAAe,WAAW,MAAM,CAAC;MACjC,YAAY,MAAM;OAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM,CAAC;MAC3C;MACA,OAAO;OACL,QAAQ;OACR,OACE,QAAQ,YAAY,SAAS,IACzB,OAAO,OAAO,KAAK,UACnB,OAAO,OAAO,YAAY;OAChC,YACE,QAAQ,YAAY,SAAS,IACzB,OAAO,WAAW,WAAW,SAC7B,OAAO,WAAW,WAAW;MACrC;gBAEC;KACG,CAAA,CACQ,EAAA,GA7BK,GA6BL,CACjB,CACE;QAEN,cACC,oBAAC,SAAD;KACE,MAAK;KACL,OAAO;KACP,WAAW,MAAM,eAAe,EAAE,OAAO,KAAK;KAC9C,aAAY;KACZ,cAAW;KACX,OAAO;MACL,YAAY,OAAO,OAAO,WAAW;MACrC,OAAO,OAAO,OAAO,KAAK;MAC1B,QAAQ,aAAa,OAAO,OAAO,OAAO;MAC1C,cAAc,OAAO,aAAa;MAClC,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;MAChD,UAAU,OAAO,WAAW,SAAS;MACrC,SAAS;MACT,UAAU;MACV,YAAY;KACd;IACD,CAAA,CAEA;;GAIP,qBAAC,OAAD;IACE,MAAK;IACL,OAAO;KACL,SAAS;KACT,qBAAqB,cAAc,kBAAkB;KACrD,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,cAAc,aAAa,OAAO,OAAO,OAAO;KAChD,YAAY,OAAO,OAAO,WAAW;KACrC,UAAU,OAAO,WAAW,SAAS;KACrC,OAAO,OAAO,OAAO,KAAK;KAC1B,YAAY,OAAO,WAAW,WAAW;KACzC,eAAe;KACf,eAAe;IACjB;cAbF,CAeE,qBAAC,QAAD;KACE,MAAK;KACL,UAAU;KACV,cAAW;KACX,eAAe,WAAW,MAAM;KAChC,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM;KAC1C;KACA,OAAO,EAAE,QAAQ,UAAU;eAR7B,CASC,SACO,WAAW,WAAW,YAAY,QAAQ,MAAW,IACvD;QACL,eACC,qBAAA,UAAA,EAAA,UAAA,CACE,qBAAC,QAAD;KACE,MAAK;KACL,UAAU;KACV,cAAW;KACX,eAAe,WAAW,MAAM;KAChC,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM;KAC1C;KACA,OAAO;MAAE,QAAQ;MAAW,UAAU;MAAI,WAAW;KAAQ;eAR/D;MASC;MACM;MACJ,WAAW,WAAW,YAAY,QAAQ,MAAW;KAClD;QACN,qBAAC,QAAD;KACE,MAAK;KACL,UAAU;KACV,cAAW;KACX,eAAe,WAAW,UAAU;KACpC,YAAY,MAAM;MAChB,IAAI,EAAE,QAAQ,SAAS,WAAW,UAAU;KAC9C;KACA,OAAO;MAAE,QAAQ;MAAW,UAAU;MAAK,WAAW;KAAQ;eARhE;MASC;MACU;MACR,WAAW,eACT,YAAY,QAAQ,MAAW;KAC9B;MACN,EAAA,CAAA,CAED;;GAGL,qBAAC,OAAD;IACE,KAAK;IACL,OAAO;KACL,MAAM;KACN,UAAU;KACV,UAAU;KACV,QAAQ,aACJ,cAAc,OAAO,OAAO,YAAY,YACxC;IACN;IACA,WAAW,MAAM,aAAa,EAAE,cAAc,SAAS;IACvD,YACE,eACK,MAAM;KACL,EAAE,eAAe;KACjB,cAAc,IAAI;IACpB,IACA,KAAA;IAEN,aAAa,oBAAoB,cAAc,KAAK,IAAI,KAAA;IACxD,QAAQ,cAAc,aAAa,KAAA;cApBrC,CAsBG,cACC,oBAAC,OAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO;MACP,YAAY,GAAG,OAAO,OAAO,YAAY,QAAQ;MACjD,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,UAAU,OAAO,WAAW,SAAS;MACrC,OAAO,OAAO,OAAO,YAAY;MACjC,YAAY,OAAO,WAAW,WAAW;MACzC,QAAQ;KACV;eACD;IAEI,CAAA,GAEN,aAAa,WAAW,IACvB,oBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,QAAQ;MACR,OAAO,OAAO,OAAO,KAAK;MAC1B,UAAU,OAAO,WAAW,SAAS;KACvC;eAEC,cAAc,sBAAsB;IAClC,CAAA,IAEL,oBAAC,OAAD;KACE,OAAO;MAAE,QAAQ,YAAY;MAAa,UAAU;KAAW;eAE/D,oBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,KAAK,WAAW;OAChB,MAAM;OACN,OAAO;MACT;gBAEC,oBAAoB,KAAK,EAAE,MAAM,YAChC,qBAAC,OAAD;OACE,MAAK;OACL,UAAU;OACV,cAAY,GAAG,KAAK,cAAc,WAAW,OAAO,IAAI,KAAK;OAC7D,iBAAe,SAAS,IAAI,KAAK,IAAI;OAErC,eAAe,gBAAgB,IAAI;OACnC,qBAAqB,sBAAsB,IAAI;OAC/C,YAAY,MAAM;QAChB,IAAI,EAAE,QAAQ,SAAS,sBAAsB,IAAI;QACjD,IAAI,EAAE,QAAQ,KAAK;SACjB,EAAE,eAAe;SACjB,gBAAgB,IAAI;QACtB;OACF;OACA,OAAO;QACL,SAAS;QACT,qBAAqB,cAAc,kBAAkB;QACrD,SAAS,KAAK,OAAO,QAAQ;QAC7B,aAAa,QAAQ,OAAO,QAAQ,IAAI,KAAK,QAAQ,GAAG;QACxD,QAAQ,OAAO,YAAY;QAC3B,YAAY;QACZ,cAAc,aAAa,OAAO,OAAO,OAAO;QAChD,QAAQ;QACR,YAAY,SAAS,IAAI,KAAK,IAAI,IAC9B,GAAG,OAAO,OAAO,YAAY,QAAQ,MACrC;QACJ,YAAY;OACd;OACA,eAAe,MAAM;QACnB,IAAI,CAAC,SAAS,IAAI,KAAK,IAAI,GACzB,EAAG,cAAiC,MAAM,aACxC,OAAO,OAAO,WAAW;OAC/B;OACA,eAAe,MAAM;QACnB,EAAG,cAAiC,MAAM,aACxC,SAAS,IAAI,KAAK,IAAI,IAClB,GAAG,OAAO,OAAO,YAAY,QAAQ,MACrC;OACR;iBAvCF,CAyCE,qBAAC,QAAD;QACE,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK,OAAO,QAAQ;SACpB,UAAU;QACZ;kBANF;SAQG,eACC,oBAAC,SAAD;UACE,MAAK;UACL,SAAS,SAAS,IAAI,KAAK,IAAI;UAC/B,gBAAgB,aAAa,KAAK,IAAI;UACtC,UAAU,MAAM,EAAE,gBAAgB;UAClC,cAAY,UAAU,KAAK;UAC3B,OAAO,EACL,aAAa,OAAO,OAAO,YAAY,QACzC;SACD,CAAA;SAEF,oBAAoB,UAAU,KAAK,eAClC,oBAAC,QAAD;UACE,eAAY;UACZ,OAAO;WACL,UAAU,OAAO,WAAW,SAAS;WACrC,OAAO,OAAO,OAAO,KAAK;WAC1B,OAAO;WACP,WAAW;UACb;oBAEC,SAAS,IAAI,KAAK,IAAI,IAAI,MAAW;SAClC,CAAA;SAER,oBAAC,QAAD;UACE,eAAY;UACZ,OAAO;WACL,SAAS;WACT,YAAY;WACZ,UAAU,OAAO,WAAW,SAAS;UACvC;oBAEA,oBAAC,WAAD;WACE,MAAM,gBAAgB,IAAI;WAC1B,MAAK;WACL,OAAM;WACN,OAAO,EAAE,UAAU,UAAU;UAC9B,CAAA;SACG,CAAA;SACN,oBAAC,QAAD;UACE,OAAO;WACL,UAAU,OAAO,WAAW,SAAS;WACrC,YAAY,KAAK,cACb,OAAO,WAAW,WAAW,SAC7B,OAAO,WAAW,WAAW;WACjC,OAAO,KAAK,cACR,OAAO,OAAO,YAAY,UAC1B,OAAO,OAAO,KAAK;WACvB,UAAU;WACV,cAAc;WACd,YAAY;UACd;oBAEC,KAAK;SACF,CAAA;QACF;WACL,eACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;QACE,OAAO;SACL,UAAU,OAAO,WAAW,SAAS;SACrC,OAAO,OAAO,OAAO,KAAK;SAC1B,WAAW;SACX,UAAU;SACV,YAAY,OAAO,WAAW,WAAW;QAC3C;kBAEC,KAAK,cAAc,MAAM,eAAe,KAAK,IAAI;OAC9C,CAAA,GACN,oBAAC,QAAD;QACE,OAAO;SACL,UAAU,OAAO,WAAW,SAAS;SACrC,OAAO,OAAO,OAAO,KAAK;SAC1B,WAAW;SACX,UAAU;SACV,YAAY,OAAO,WAAW,WAAW;QAC3C;kBAEC,WAAW,KAAK,QAAQ;OACrB,CAAA,CACN,EAAA,CAAA,CAED;SA/HE,KAAK,IA+HP,CACN;KACE,CAAA;IACF,CAAA,CAEJ;;GAGL,qBAAC,OAAD;IACE,OAAO;KACL,SAAS,GAAG,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ;KAChD,WAAW,aAAa,OAAO,OAAO,OAAO;KAC7C,YAAY,OAAO,OAAO,WAAW;KACrC,UAAU,OAAO,WAAW,SAAS;KACrC,OAAO,OAAO,OAAO,KAAK;KAC1B,SAAS;KACT,gBAAgB;IAClB;cATF,CAWE,qBAAC,QAAD,EAAA,UAAA,CAAO,aAAa,QAAO,QAAY,EAAA,CAAA,GACtC,SAAS,OAAO,KAAK,qBAAC,QAAD,EAAA,UAAA,CAAO,SAAS,MAAK,WAAe,EAAA,CAAA,CACvD;;EACF;;AAET,CAAC"}
|