# How the Superfile Sidebar Displays Pinned Directories and Disk Information

> Discover how the Superfile sidebar shows pinned directories and disk info by merging JSON PinnedManager data with OS mounted partitions into a unified view.

- Repository: [Yorukot/superfile](https://github.com/yorukot/superfile)
- Tags: internals
- Published: 2026-07-30

---

**The Superfile sidebar renders pinned directories from a JSON-backed `PinnedManager` and disk information from OS-mounted partitions, merging both with well-known directories into a unified slice that the render pipeline divides with visual separators.**

The Superfile terminal file manager provides a sidebar that combines user-defined shortcuts with live system storage information. Understanding exactly **how the superfile sidebar displays pinned directories and disk information** requires examining the internal `Model` structure and its data assembly pipeline in the `yorukot/superfile` repository.

## The Sidebar Model and Data Flow

The sidebar is built around a **`Model`** struct that maintains a slice of **`directory`** objects. When instantiated via `sidebar.New()`, the model calls **`getDirectories`** in [`src/internal/ui/sidebar/directory_utils.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/sidebar/directory_utils.go) (lines 44‑52) to populate its state.

This function merges three logical groups based on the user's **`variable.SidebarSections`** configuration:

- **Home / Well-known directories**: Static XDG user directories (Home, Desktop, Downloads) filtered by existence via `getWellKnownDirectories`
- **Pinned directories**: User-saved shortcuts loaded via `getPinnedDirectoriesWithIcon` 
- **Disk information**: Live mounted partitions discovered via `getExternalMediaFolders`

Each group is guarded by a section flag, allowing users to customize which categories appear in their sidebar.

## How Pinned Directories Are Loaded and Managed

Pinned directories persist to a JSON file managed by the **`PinnedManager`** type in [`src/internal/ui/sidebar/pinned.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/sidebar/pinned.go).

**Storage and Lifecycle**

The manager operates on `variable.PinnedFile` (typically `~/.config/superfile/pinned.json`) and provides four core operations:

- **`Load()`**: Unmarshals the JSON file into `[]directory`, then calls `Clean` to purge non-existent paths
- **`Save()`**: Persists the current slice back to disk
- **`Toggle(path)`**: Adds a directory if absent, removes it if present, then triggers `Save`
- **`Clean()`**: Validates entries against the filesystem and drops stale records

**Icon Resolution**

When the UI requests pinned items via `getPinnedDirectoriesWithIcon` (lines 83‑88 in [`directory_utils.go`](https://github.com/yorukot/superfile/blob/main/directory_utils.go)), each entry receives an appropriate Nerd-font icon via `common.GetDirectoryIcon`. The UI never caches the raw pinned slice; every render cycle calls this function to ensure icons reflect the current theme configuration (`common.Config.Nerdfont`).

```go
// Create the manager pointing to the pinned JSON file
pinnedMgr := sidebar.NewPinnedFileManager(config.Variable.PinnedFile)

// Toggle pinning for a specific directory
err := pinnedMgr.Toggle("/path/to/project")
if err != nil {
    log.Fatalf("cannot pin directory: %v", err)
}

```

## Disk Information Discovery and Filtering

Disk entries populate dynamically using the **gopsutil** library to probe the operating system for mounted partitions.

**Discovery Process**

The function **`getExternalMediaFolders`** in [`src/internal/ui/sidebar/disk_utils.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/sidebar/disk_utils.go) (lines 15‑36) calls `disk.Partitions(false)` from gopsutil/v4 to retrieve all mounted filesystems. Each candidate passes through **`shouldListDisk`**, which implements OS-specific filtering rules:

- **Windows**: Lists every drive letter automatically
- **Unix/Linux/macOS**: Always includes root `/` and any mount point under `/mnt`, `/media`, `/run/media`, or `/Volumes`
- **Exclusions**: Filters out Time Machine snapshots and other system-generated mount points

**Display Data Construction**

For each valid partition, the code constructs a `directory` struct with three computed fields:
- **`diskIcon`**: The visual indicator for storage devices
- **`diskName`**: A shortened display name (e.g., "C:" on Windows)
- **`diskLocation`**: The fully-qualified path ready for navigation

```go
// Example: Extending the disk filter to include /srv mounts
func shouldListDisk(mountPoint string) bool {
    // existing checks...
    return strings.HasPrefix(mountPoint, "/mnt") ||
           strings.HasPrefix(mountPoint, "/media") ||
           strings.HasPrefix(mountPoint, "/run/media") ||
           strings.HasPrefix(mountPoint, "/Volumes") ||
           strings.HasPrefix(mountPoint, "/srv") // added custom prefix
}

```

## Rendering the Sidebar with Visual Dividers

The render pipeline lives in [`src/internal/ui/sidebar/render.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/sidebar/render.go) within the **`Render`** method signature:

```go
func (s *Model) Render(sidebarFocused bool, currentFilePanelLocation string) string

```

**Divider Injection**

The `directoriesRender` helper iterates over `s.directories` looking for sentinel divider values defined in [`consts.go`](https://github.com/yorukot/superfile/blob/main/consts.go):
- **`pinnedDividerDir`**: Triggers insertion of `common.SideBarPinnedDivider`
- **`diskDividerDir`**: Triggers insertion of `common.SideBarDisksDivider`

These sentinels (lines 60‑63 in [`render.go`](https://github.com/yorukot/superfile/blob/main/render.go)) create the visual separation between Home, Pinned, and Disk sections automatically.

**Selection Styling**

Each directory line renders with conditional styling:
- **Normal entries**: Use `common.SidebarStyle`
- **Selected entry**: Uses `common.SidebarSelectedStyle` when the path matches the current file panel's location
- **Cursor**: Displays via `icon.Cursor` when `sidebarFocused` is true

The render logic skips empty sections, so dividers appear only when their respective group contains at least one valid item.

## Summary

- The **sidebar Model** aggregates three data sources (well-known, pinned, disks) through `getDirectories` in [`directory_utils.go`](https://github.com/yorukot/superfile/blob/main/directory_utils.go)
- **Pinned directories** persist to `~/.config/superfile/pinned.json` and are managed by `PinnedManager` with automatic invalid entry cleaning
- **Disk information** updates live via gopsutil's `disk.Partitions`, filtered by `shouldListDisk` to exclude system mounts while showing removable drives
- **Visual organization** relies on sentinel divider values (`pinnedDividerDir`, `diskDividerDir`) injected during the render loop in [`render.go`](https://github.com/yorukot/superfile/blob/main/render.go)
- The entire pipeline respects `variable.SidebarSections` configuration, allowing users to disable entire categories

## Frequently Asked Questions

### Where does Superfile store pinned directory data?

Superfile stores pinned directories in a JSON file located at `~/.config/superfile/pinned.json` (defined as `variable.PinnedFile`). The `PinnedManager` in [`src/internal/ui/sidebar/pinned.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/sidebar/pinned.go) handles all read/write operations, automatically validating paths against the filesystem during load to remove entries pointing to deleted directories.

### How does Superfile decide which disks appear in the sidebar?

The `shouldListDisk` function in [`src/internal/ui/sidebar/disk_utils.go`](https://github.com/yorukot/superfile/blob/main/src/internal/ui/sidebar/disk_utils.go) filters mounted partitions based on the operating system. On Windows, all drive letters are shown; on Unix systems, only root `/` and mounts under `/mnt`, `/media`, `/run/media`, or `/Volumes` are included, excluding Time Machine snapshots and other system-generated mount points.

### Can I customize which sections appear in the Superfile sidebar?

Yes. The sidebar respects the `variable.SidebarSections` configuration array, which controls whether Home/Well-known, Pinned, and Disks sections are populated. If you remove a section from this configuration, `getDirectories` will skip that group entirely, and the render pipeline will automatically omit the corresponding visual divider.

### Why do some pinned directories disappear from the sidebar after restarting Superfile?

The `PinnedManager.Clean()` method automatically removes pinned entries that no longer exist on the filesystem when the JSON file loads. If a directory was pinned but subsequently deleted or moved, Superfile silently drops it from the list during the next application startup to prevent navigation errors.