How Superfile Handles File Copy Operations with Progress Tracking

Superfile implements file copy operations through a five-layer architecture that separates pure filesystem I/O from UI rendering, using a processbar.Process struct to track completion counts and charm.land/bubbles/v2/progress to render real-time percentage bars.

Superfile, a terminal file manager written in Go by yorukot/superfile, provides granular progress feedback during copy and move operations. The implementation delegates recursive file walking to pasteDir in src/internal/file_operations.go while maintaining thread-safe progress updates through a dedicated process bar model. Understanding how superfile handles file copy operations with progress tracking reveals a clean architectural pattern that balances performance with accurate visual feedback.

The Five-Layer Copy Architecture

Superfile's file copy implementation follows strict separation of concerns across five distinct layers. Each layer owns a specific responsibility, from raw disk I/O to terminal UI rendering.

Filesystem Logic Layer

The foundation resides in src/internal/file_operations.go, which contains pure I/O functions that never interact with the UI. Key functions include:

  • copyElement – Entry point that dispatches to file, directory, or symlink handlers
  • copyFile – Performs byte-stream copying between source and destination
  • copyDir – Recursively creates directory structures
  • copyLinkFile – Handles symbolic link replication

These routines execute blocking filesystem calls and return only when the operation completes or fails.

Paste Orchestration Layer

The pasteDir function, also in file_operations.go, coordinates complex operations. It accepts two critical parameters for progress tracking:

func pasteDir(src, dst string, p *processbar.Process, cut bool, processBarModel *processbar.Model) error

This function determines whether a fast rename is possible (same-partition cut) or if a full copy-plus-delete cycle is required. While traversing the source tree, it invokes actualPasteOperation for each entry.

Per-File Operation Layer

The actualPasteOperation function bridges filesystem work and progress reporting. After successfully copying a file or creating a directory, it updates the process state:

p.CurrentFile = fileName
p.Done++
processBarModel.TrySendingUpdateProcessMsg(*p)

This ensures the UI receives updates atomically after each file completes, preventing progress bar jitter or loss of synchronization.

Progress Bar UI Layer

The visual feedback system operates through src/internal/ui/processbar/model.go and src/internal/ui/processbar/process.go. The Process struct tracks:

  • Total – Total number of files to process
  • Done – Number of completed operations
  • CurrentFile – Name of the file currently being processed
  • State – Operating, Finished, or Error

The processbar.Model calculates the percentage:

progressPercentage := float64(p.Done) / float64(p.Total)

It then renders the bar using the progress component from charm.land/bubbles/v2/progress, producing output like:


copy-operation ┃ ███████░░░  57%  (file.txt)

High-Level Command Handling

User commands enter through src/internal/handle_file_operations.go. The copySingleItem and copyMultipleItem functions:

  1. Validate source items via PruneInaccessibleItemsAndGet()
  2. Count total files using getTotalFilesCnt(copyItems)
  3. Initialize a processbar.Process with a UUID and total count
  4. Invoke executePasteOperation, which ultimately calls pasteDir

Step-by-Step Progress Tracking Workflow

When you execute a copy command, the following sequence ensures accurate progress reporting:

  1. Initialization – copySingleItem creates a Process with Total = getTotalFilesCnt(copyItems) and registers it with processbar.Model
  2. Traversal – pasteDir walks the source directory tree, skipping inaccessible items
  3. Execution – For each entry, actualPasteOperation performs the filesystem operation
  4. Update – Post-operation, it increments p.Done, sets p.CurrentFile, and calls TrySendingUpdateProcessMsg
  5. Rendering – The UI thread calls Model.Render, recalculating the percentage and redrawing the terminal bar
  6. Completion – Upon finishing, Process.State transitions to Finished and the entry cleans up

Practical Code Examples

Copying a Single File

Copy a file to the current panel's destination:

spf cp src.txt

Under the hood, copySingleItem(false) creates a processbar.Process with Total = 1. The progress bar flashes briefly to 100% as pasteDir completes the single actualPasteOperation call.

Copying an Entire Directory

Recursively copy a folder to a target path:

spf cp myfolder/ /tmp/target/

The Go implementation handles this recursively:

func (m *model) copyMultipleItem(cut bool) {
    copyItems := m.clipboard.PruneInaccessibleItemsAndGet()
    reqID := uuid.New().String()
    // Total is set to the recursive file count
    m.executePasteOperation(&m.processBarModel, "/tmp/target/", copyItems, cut, reqID)
}

During execution, the UI displays incremental updates:


copy-operation ┃ ████░░░░░░  30%  (file1.txt)

Cross-Partition Move Operations

Move data across different filesystems:

spf cut myfolder/ /mnt/otherdisk/

When source and destination reside on different partitions, pasteDir detects that a fast rename is impossible and falls back to copy-then-delete. The progress bar tracks only the copy phase; deletion of the source tree occurs after the bar reaches 100%, ensuring you never see incomplete data in the destination.

Key Source Files

File Purpose Key Components
src/internal/file_operations.go Core copy logic and orchestration pasteDir, actualPasteOperation, copyFile
src/internal/ui/processbar/process.go Progress state definition Process struct (Total, Done, CurrentFile)
src/internal/ui/processbar/model.go UI rendering and process management Model, TrySendingUpdateProcessMsg, rendering logic
src/internal/handle_file_operations.go Command entry points copySingleItem, copyMultipleItem, executePasteOperation
src/internal/trash/trash_windows.go Windows-native progress sink IFileOperationProgressSink COM implementation

Summary

  • Superfile separates filesystem I/O from UI rendering through a five-layer architecture implemented in yorukot/superfile.
  • pasteDir in file_operations.go orchestrates recursive copying while accepting *processbar.Process for state updates.
  • actualPasteOperation updates Process.Done and CurrentFile after each file, triggering TrySendingUpdateProcessMsg.
  • Progress percentage is calculated as float64(Done)/float64(Total) and rendered via charm.land/bubbles/v2/progress.
  • Cross-partition moves fall back to copy-then-delete, with progress tracking covering only the copy phase.

Frequently Asked Questions

How does superfile calculate the percentage for copy progress?

Superfile calculates the percentage in processbar.Model using the ratio of completed versus total files: float64(p.Done)/float64(p.Total). The Process struct tracks Total (set during initialization by getTotalFilesCnt) and Done (incremented by actualPasteOperation after each successful file operation). This ratio drives the bubbles/v2/progress component to render the visual bar.

Why doesn't the progress bar appear to move smoothly during large file copies?

The progress bar updates per file, not per byte. If you copy a directory containing one small file and one multi-gigabyte file, the bar jumps to 50% after the small file completes, then stalls until the large file finishes. This is by design in actualPasteOperation, which calls TrySendingUpdateProcessMsg only after os.Copy or directory creation returns.

What happens when cutting and pasting across different filesystems?

When pasteDir detects that the source and destination are on different partitions (where os.Rename would fail with an EXDEV error), it executes a fallback path: it copies the entire tree using copyElement, updates the progress bar to 100%, and only then deletes the source files. This ensures data integrity—if the copy fails, the source remains intact.

Does superfile use native Windows progress dialogs?

On Windows, superfile optionally leverages the COM interface IFileOperationProgressSink defined in src/internal/trash/trash_windows.go for operations involving the system trash. However, standard copy operations between directories use the same Go-based processbar implementation as Linux and macOS, ensuring consistent terminal-based progress reporting across all platforms.

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 →