Background File Loading
April 11, 2026 · View on GitHub
Overview
Files larger than 5 MB are loaded in a background thread with a progress indicator, preventing the UI from freezing during I/O. Smaller files continue to use synchronous loading (fast enough to be imperceptible).
Architecture
open_file_smart()
├── < 5 MB → synchronous std::fs::read (existing path)
└── ≥ 5 MB → AppState::open_file_loading() → Tab::new_loading()
+ spawn_file_loader() background thread
↓ FileLoadMsg::Progress (per 1 MB chunk)
↓ FileLoadMsg::Complete | FileLoadMsg::Error
↓ (via std::sync::mpsc channel)
poll_file_load_messages() in update()
→ Tab::finish_loading() | Tab::fail_loading()
Key Types
| Type | Location | Purpose |
|---|---|---|
TabContent | state.rs | Enum: Loading(LoadingProgress), Ready, Error(String) |
LoadingProgress | state.rs | Tracks path, bytes_loaded, total_size |
FileLoadMsg | app/types.rs | Channel message: Progress, Complete, Error |
Fields on FerriteApp
| Field | Type | Purpose |
|---|---|---|
file_load_tx | mpsc::Sender<FileLoadMsg> | Cloned into each background thread |
file_load_rx | mpsc::Receiver<FileLoadMsg> | Polled each frame in update() |
loading_tasks | HashMap<usize, JoinHandle<()>> | Active loaders keyed by tab ID |
Tab Lifecycle
Tab::new_loading(id, path, total_size)— creates placeholder withTabContent::Loading- Background thread reads in 1 MB chunks, sends
FileLoadMsg::Progress - On complete, sends
FileLoadMsg::Complete { bytes } poll_file_load_messages()callstab.finish_loading(bytes, ...)→ decodes encoding, sets content, transitions toTabContent::Ready- On error or binary detection,
tab.fail_loading(error)→TabContent::Error
Cancellation
When a loading tab is closed, cleanup_tab_state() removes the JoinHandle from loading_tasks. The background thread continues briefly but its channel sends will fail silently (receiver dropped).
UI
- Loading state: Centered spinner + progress bar showing MB loaded / total + percentage
- Error state: Warning icon + error message
- Tab title:
⏳ filenameduring load,⚠ filenameon error
Constants
| Constant | Value | Location |
|---|---|---|
BACKGROUND_LOAD_THRESHOLD | 5 MB | app/file_ops.rs |
LOAD_CHUNK_SIZE | 1 MB | app/file_ops.rs |
Entry Points
open_file_smart() on FerriteApp (in file_ops.rs) is the unified entry point. It's called from:
- File > Open dialog
- File tree click
- Drag and drop
- CLI arguments
- Quick switcher (Ctrl+P)
- Recent files
- Secondary instance protocol
Internal navigation paths (search, wikilinks, backlinks) continue to use the synchronous AppState::open_file() since those files are typically small markdown documents.