How the Superfile Sidebar Displays Pinned Directories and Disk Information

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 (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.

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), 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).

// 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 (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
// 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 within the Render method signature:

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:

  • pinnedDividerDir: Triggers insertion of common.SideBarPinnedDivider
  • diskDividerDir: Triggers insertion of common.SideBarDisksDivider

These sentinels (lines 60‑63 in 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
  • 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
  • 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →