--- title: Explorer Architecture sidebarTitle: Explorer --- The Explorer is Spacedrive's primary file browsing interface. It displays files, sources, volumes, and devices using a unified system that treats all entities as navigable directories. The architecture centers on a single source of truth for navigation: URL query parameters. ## Window layout Choose your window layout in Settings > Appearance > Layout. Inset content is the default. Navigation sits on the window background, and the explorer and inspector share a bordered sheet. Floating panels places navigation and the inspector in separate rounded panels over the window background. The preference is independent of the color theme. It is stored in this client's local storage and updates other windows on the same origin, including the desktop Settings window. It does not change library or daemon configuration. `ShellLayout` uses one mounted component tree for both modes. The workspace wrapper changes its surface, spacing, and containing block without replacing the routed explorer or portal hosts. Keep providers and component keys stable when extending these modes so layout changes do not reset local view state. Quick Preview and Size view use the shared insets from `ShellGeometryContext`. Their portal hosts cover the window in Floating panels mode and the sheet in Inset content mode. The embedded inspector remains above these layers. Size view measures its SVG viewport and positions thumbnails relative to its container. Video timeline previews use the controls' width instead of the window width. Popout inspectors keep their separate window framing. ## Navigation System The Explorer uses URL query parameters instead of route paths to manage navigation state. This design allows the sidebar, path bar, and Explorer view to stay synchronized without prop drilling or complex state management. ```typescript // Path-based navigation /explorer?path={"Physical":{"device_slug":"home","path":"/Documents"}} // View-based navigation (virtual listings) /explorer?view=device&id=abc123 ``` When you navigate to a folder from the sidebar, the URL updates with the new path. The Explorer reads this URL parameter and displays the corresponding directory. The sidebar highlights the active item by comparing its own path to the URL parameter. The URL is the single source of truth. All navigation actions update the URL first, then components react to URL changes. ### History Management Navigation history stores a union type that supports both real paths and virtual views: ```typescript type NavigationEntry = | { type: "path"; path: SdPath } | { type: "view"; view: string; id?: string; params?: Record }; ``` The `ExplorerContext` maintains a history stack and current position. When you click back or forward, it restores the previous entry and updates the URL accordingly. This works seamlessly across both file system paths and virtual device listings. ## Virtual Listings Virtual listings display non-file entities (devices, sources, volumes) as if they were files in a directory. This allows the Explorer to reuse all existing view components (grid, list, column) without special cases. ### Mapping to Files The `virtualFiles.ts` module converts backend entities into `File` objects with a special `_virtual` metadata field: ```typescript export function mapSourceToFile( source: SourceInfo, deviceSlug: string, iconUrl?: string, ): File { return { id: `virtual:source:${source.id}`, kind: "Directory", name: source.name, sd_path: { Physical: { device_slug: deviceSlug, path: source.root ?? "/" } }, // ... standard File fields _virtual: { type: "source", data: source, iconUrl, } }; } ``` The `_virtual` field marks this as a display-only entity. Virtual files cannot be copied, moved, or deleted. The `isVirtualFile()` helper checks for this field before allowing file operations. ### View Detection The `useVirtualListing` hook detects virtual view parameters in the URL and fetches the appropriate data: ```typescript // Example: Device view if (view === "device" && id) { // Fetch sources and volumes for this device const sources = sourcesByDevice.get(id) ?? []; const volumes = volumesData?.volumes.filter( vol => vol.device_id === id ); // Map to virtual files return sources.map(source => mapSourceToFile(source, device.slug)) .concat(volumes.map(mapVolumeToFile)); } ``` This returns an array of virtual files that the Explorer views consume like regular directory listings. ### Column View Integration Column view presents a unique challenge for virtual listings. When you select a virtual source, the next column should show that source's real contents. The system handles this by conditionally rendering columns: ```typescript if (isVirtualView && virtualFiles) { const selectedDirectory = selectedFiles.length === 1 && selectedFiles[0].kind === "Directory" ? selectedFiles[0] : null; return (
{/* Virtual column */} {/* Real content column when directory selected */} {selectedDirectory && ( )}
); } ``` The first column displays virtual files. When you select one, the second column queries the backend for real directory contents using the virtual file's `sd_path`. Virtual files must never be passed to file operations like copy, move, or delete. Always check `isVirtualFile()` before backend mutations. ## Safety Guards The system includes multiple layers of protection against accidental operations on virtual files: **Context Menu**: Copy and delete menu items are hidden when virtual files are selected. The `getTargetFiles()` function filters out virtual files before operations. **Drag and Drop**: The `useDraggableFile` hook disables dragging for virtual files by setting `disabled: true` on the draggable configuration. **Type Checking**: Functions that access `_virtual` metadata use optional chaining to handle undefined files gracefully: ```typescript const virtualMetadata = file?._virtual; const isVolume = virtualMetadata?.type === "volume"; ``` ## Path Bar Navigation The path bar displays breadcrumbs for the current path. For physical paths, the device icon is clickable and navigates to that device's virtual view: ```typescript const handleDeviceClick = () => { if (device) { navigateToView("device", device.id); } }; ``` This creates a seamless transition from browsing a file system path to viewing all sources and volumes on that device. For virtual views, a separate `VirtualPathBar` component renders appropriate breadcrumbs like "Devices → My Device". ## Enum Handling The backend returns Rust enum variants that can be objects like `{ Other: "value" }`. These cannot be rendered directly in React. Always convert enums to strings before displaying: ```typescript const volumeTypeStr = typeof volume.volume_type === "string" ? volume.volume_type : (volume.volume_type as any)?.Other || JSON.stringify(volume.volume_type); ``` This pattern extracts the `Other` variant value when present, falls back to JSON stringification, or uses the string directly if it's already a string. Apply this pattern consistently to `file_system`, `disk_type`, `volume_type`, `form_factor`, and any other backend enum fields. ## Component Communication The `ExplorerContext` provides methods for navigation that other components use: ```typescript const { navigateToPath, navigateToView, goBack, goForward } = useExplorer(); // Navigate to a file system path navigateToPath({ Physical: { device_slug: "home", path: "/Documents" }}); // Navigate to a virtual view navigateToView("device", deviceId); ``` These methods update both the internal history stack and the URL. Components that render navigation UI (sidebar, path bar) use `useLocation()` to read the current URL and derive their active state. The sidebar calculates `isActive` by comparing its item's path or view parameters to the current URL parameters. This ensures the active state always matches what's visible in the Explorer. ## Inspector Integration The Inspector treats a virtual file like any other selection and renders the `FileInspector` for it. A virtual source or volume carries its root as its `sd_path`, so the inspector describes that directory. ## View Modes All three view modes (grid, list, column) consume the same file array. When `useVirtualListing` returns virtual files, the views render them using the same components as real files. Icon overrides in `Thumb.tsx` check for `_virtual.iconUrl` and display custom icons for sources and volumes. The grid view conditionally renders a volume capacity bar when displaying virtual volumes: ```typescript const isVolume = isVirtualFile(file) && file._virtual?.type === "volume"; if (isVolume) { return ; } ``` This adds visual information without requiring volume-specific components or branching logic in the core Explorer.