Repository navigation
Flux Project Wiki
This comprehensive wiki documentation outlines the complete engineering specification, module mechanics, architectural layout, and configurations for Flux—the advanced, keyboard-driven file manager built in Rust.
- Home: Project Overview
- Architecture Layer Guide
- Module & Codebase Map
- Virtual Terminal & Core Services Subsystem
- Configuration & Context Menu DSL Specification
- Keyboard Shortcut & Input Hub Reference
Flux is a performance-first, entirely keyboard-driven file manager built in Rust using the modern GTK4 and Libadwaita widget eco-systems alongside the reactive Relm4 framework. It targets advanced users, power-users, and developers who seek a rapid, keyboard-centric mechanism to interface with their file hierarchies without breaking flow, imitating navigation systems found in tools like Vim or Emacs.
- Not a Clone: Flux draws functional inspiration from foundational file managers like Nautilus, Thunar, and Dolphin, but it does not duplicate their mouse-first layout philosophies.
- Not a Toy App: It is a production-grade codebase with thousands of lines of highly concurrent, thread-safe system logic.
- Not a Shell Wrapper: It does not simply wrap a generic terminal emulator command line; it implements a low-level, high-fidelity native virtual terminal subsystem with custom parsing and rendering loops.
- Performance First: No background file operation, parsing script, or metadata extraction may ever stall or block the primary UI loop. Startup layout latencies must remain sub-200ms.
- Keyboard Absolute: Every visual action, state permutation, filter rule, or layout transition must be tied to a predictable, zero-latency keyboard shortcut.
- Predictable State: Implements a strict, single source of truth layout using the declarative Model-View-Update (MVU) pattern.
-
No-Compile Extensibility: Custom context operations and custom external menu entries must be dynamically generated from configuration scripts (
menu.rs) at runtime without compilation requirements. - Standards Compliant: Inherits and enforces GNOME Human Interface Guidelines (HIG), FreeDesktop XDG standard specs, and synchronous GLib I/O (GIO) interfaces.
Flux relies on the strict separation of application data, render templates, and mutator pipelines modeled by the Relm4 framework.
┌─────────────────────────────────────────────────────────────────────────────┐
│ MODEL (FluxApp) │
│ - Holds ALL application state │
│ - Contains: grid data, sidebar, terminal state, config, history, etc. │
│ - The ONLY source of truth │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ 1. Model changes
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ VIEW (view.rs) │
│ - Declarative UI description using relm4's view! macro │
│ - [watch] attributes bind widget properties to model fields │
│ - Widget signals (clicked, activated) → AppMsg variants │
│ - Reactively updates when model changes │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ 2. User interaction
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ UPDATE (update.rs) │
│ - handle_update() processes AppMsg variants │
│ - Mutates FluxApp state │
│ - Dispatches side effects: I/O, spawn tasks, show toasts │
│ - The "brains" of the application │
└─────────────────────────────────────────────────────────────────────────────┘
▲
│ 3. New state
│
└──────────────────────────────────────┘
-
The Model (
FluxApp): The absolute data store. It models state tracking parameters including: active directory grid contexts, sidebar list buffers, integrated virtual terminal structures, system keymaps, and active background job queues. -
The View (
view.rs): A strictly declarative layer written via the Relm4view!macro syntax. Properties are reactive; utilizing#[watch]annotations to bind widget attributes to core model entries. Native GTK events map to unified message types. -
The Update (
update.rs): The logic router. Its primary entry functionhandle_update()accepts an incoming message enum, alters model parameters via exclusive mutable references, and schedules asynchronous background worker loops.
Flux manages application events using over 80 discrete enum variations. Key operations include:
-
Navigation:
Maps(PathBuf),GoBack,GoForward,Refresh. -
File Operations:
Copy,Cut,Paste,Delete,PerformRename(PathBuf, String). -
View Control:
ToggleHidden,CycleSort,ToggleSortOrder,Zoom(f64). -
Terminal System:
ToggleTerminal,SetTerminalHeight(i32),SetTerminalFont(String). -
Sidebar Management:
RefreshSidebar,RemoveFromSidebar(PathBuf),ReorderSidebar { from: PathBuf, to: PathBuf }. -
Exclusive List Engine:
AddExclusive(Option<PathBuf>),ClearExclusive,NextExclusive. -
Context Interaction:
PrepareContextMenu(f64, f64, Option<PathBuf>),ShowContextMenu. -
Background Jobs:
TaskProgress,TaskCompleted,CancelAllTasks.
To ensure the UI can handle large directories with thousands of entries smoothly, Flux avoids creating isolated heavy widgets for individual rows. Instead, it uses Relm4's FactoryComponent paradigm. This lightweight approach applies to:
-
FileItem: Represents a listing in the grid area (TypedGridView). -
SidebarPlace: Models bookmark rows in the navigation sidebar panel (ListBox). -
PathSegment: Controls individual breadcrumb items in the location address row.
All structural collections utilize FactoryVecDeque wrappers to guarantee
The repository architecture contains the following core structural units:
-
Cargo.toml: Controls system dependency trees and optimization definitions. -
build.rs: Standardizes automated workspace configurations and asset deployments during local compilation runs. -
src/main.rs: The application entry point. Handles core command-line argument checking, config directory monitors, and initiates the runtime application instance. -
src/model.rs: Holds global state, defining configurations, structures, and messaging variants. -
src/i18n.rs: Sets up nativegettextlocalizations via direct C bindings for low-overhead translation routing. -
src/ui/view.rs: Contains the comprehensive UI layout, constructed using declarative Relm4 markup. -
src/ui/init.rs: Manages step-by-step startup tasks, maps GIO actions, and configures external hardware volume listener monitors. -
src/ui/update.rs: Implements the core application update state loop. -
src/ui/inputs.rs: Groups input events, mapping gestures and scrolls directly to unified messages. -
src/ui/help.rs: Renders the integrated shortcut documentation overlay window. -
src/ui/menu_editor.rs: Formulates an integrated management workspace for adding, reordering, and deleting user context menu DSL rows. -
src/ui/properties.rs: Evaluates individual file structures to compute file statistics, text word lists, ELF details, and Git revision history metrics. -
src/services/loader.rs: Controls background filesystem parsing and Rayon-driven directory sorting. -
src/services/terminal.rs: Contains the custom virtual terminal emulator, drawing loop, and PTY task coordinator. -
src/services/db.rs: SQLite abstraction layer used to persist unique view rules across folder locations. -
src/services/thumbnails.rs: Generates thread-safe media preview textures that comply with XDG specifications. -
src/services/tasks.rs: Manages background worker threads and reports overall operation updates to the status bar. -
src/utils/core.rs: Exposes utility helpers for locating platform paths, loading config documents, and managing system disk mounts. -
src/utils/path.rs: Resolves filesystem path notations, including home directory tilde (~) expansions. -
src/utils/media.rs: Probes image asset sizes and utilizesffprobeto determine media playback lengths.
[package]
name = "flux"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[dependencies]
relm4 = { version = "0.11.0", features = ["libadwaita"] }
adw = { package = "libadwaita", version = "0.9.1" }
gtk = { package = "gtk4", version = "0.11.2" }
dirs = "6.0"
rayon = "1.10.0"
tokio = { version = "1.40", features = ["rt", "rt-multi-thread"] }
futures = "0.3"
serde = { version = "1.0", features = ["derive"] }
toml = "1.0"
chrono = "0.4.43"
sha2 = "0.11.0"
xattr = "1.6.1"
imagesize = "0.14.0"
md5 = "0.8.0"
rusqlite = { version = "0.39", features = ["bundled"] }
gio-unix = { version = "0.22.0", package = "gio-unix" }
gdk-pixbuf = "0.22.0"
libc = "0.2.186"
#vte4 = "0.10.0"
pango = "0.22.6"
pangocairo = "0.22.8"
vte = "0.15.0"
glib = "0.22.8"
gio = "0.22.8"
nix = "0.31.3"
[dev-dependencies]
tempfile = "3"
[[bin]]
name = "flux-fm"
path = "src/main.rs"A critical optimization design pattern is implemented inside src/main.rs to maintain sub-200ms launch times:
let base_app = adw::Application::builder().build();
assert!(
base_app.application_id().is_none(),
"[flux] STARTUP REGRESSION: application_id is set on the main adw::Application..."
);
assert!(
!base_app.flags().contains(gio::ApplicationFlags::NON_UNIQUE),
"[flux] STARTUP REGRESSION: NON_UNIQUE flag is set on the main adw::Application..."
);-
The Problem: Passing a unique string identifier via
.application_id(...)or omitting theNON_UNIQUEflag triggers synchronous D-Bus name registrations and Wayland compositor handshakes on launch. This blocks the main thread, adding approximately 200ms of latency. - The Solution: Flux uses compile-time structural assertions to guarantee these constraints are never violated, forcing developers to handle multi-instance management asynchronously via customized runtime parameters.
Flux features an integrated terminal engine that avoids external terminal wrappers or heavy IPC mechanisms. It relies on a custom implementation built around low-level PTY master handles and the VTE processing stack.
-
Data Structure Layout (
src/services/terminal.rs):pub struct Terminal { drawing_area: DrawingArea, // Native GTK4 rendering surface state: Arc<Mutex<TerminalState>>, // Thread-safe state cell context _pty_reader: Option<JoinHandle>, // PTY background monitoring handle draw_sender: UnboundedSender<()>, // Asynchronous draw queue trigger pending_dir: Arc<Mutex<Option<String>>>, // Debounced location update storage }
-
Buffer Management:
TerminalStatemaintains cell matrices representing column and row data, font sizes via Pango, alternate display screens, bracketed paste triggers, scrollback history vectors, and DECAWM wrap states. -
Escape Code Processing Engine: Flux processes incoming bytes using the
vtecrate's state machine, handling ANSI escape sequences by implementing thevte::Performtrait. It natively supports raw text output, cursor adjustments, clear operations, alternate screen switches, bracketed pasting, and standard text styles (including bold, italic, underline, strikethrough, dim, and reverse video modes). - Color System: Full fidelity color space processing maps 16 baseline ANSI registers, bright variants, a 256-index color cube, and true-color 24-bit RGB directives.
-
PTY Integration Logic: Creates matching master and slave file descriptors using
openpty()via thenixcrate. It dynamically passes resize instructions viaTIOCSWINSZioctl events and watches the running process group withTIOCGPGRPto verify shell states. Working directory paths sync automatically across panes using the OSC 7 escape sequence standard (\x1b]7;file://...).
The directory listing pipeline decouples disk parsing operations from the user interface thread.
[load_path]
│
├── Step 1: Query SQLite State DB for directory view rule configurations.
│
├── Step 2: Establish a low-level GIO File Enumerator for batch attribute ingestion.
│
├── Step 3: Accumulate file metadata: Vec<(name, display_name, is_dir, size, mtime, uid)>.
│
└── Step 4: Dispatch data block to a Rayon Worker Thread Pool:
├── Filter out hidden files / dotfiles (if configured).
├── Formulate structural FileLoadContext elements.
└── Run par_sort_unstable_by utilizing customized sorting parameters.
│
├── Step 5: Main Thread Reconciliation:
│ ├── Map and insert sorted elements into the TypedGridView.
│ └── Trigger Asynchronous Thumbnail Generation loops for visual items.
│
└── Step 6: Attach a GIO File Monitor to the path for live directory tracking.
The parallel sorting system uses Rayon to perform multi-threaded directory sorting with zero UI lag:
items.par_sort_unstable_by(|a, b| {
// 1. Evaluate directory grouping priority constraints
if a.is_dir != b.is_dir {
return if folders_first {
b.is_dir.cmp(&a.is_dir) // Folders appear first
} else {
a.is_dir.cmp(&b.is_dir) // Mixed item layout
};
}
// 2. Evaluate active primary sorting rule (Name, Date, Size, or Type)
// 3. Fallback tie-breaker: sort alphabetically by name
});To remember directory configurations across restarts, Flux integrates an embedded SQLite database engine. Folder attributes are managed via a persistent state table:
CREATE TABLE folder_settings (
path TEXT PRIMARY KEY, -- Absolute directory path string
sort_col TEXT, -- Active sort column ("Name", "Date", "Size", "Type")
sort_reversed BOOLEAN, -- Sort ordering tracking flag (True = Descending)
icon_size INTEGER, -- Pixels for icon dimensions
folders_first BOOLEAN -- Flag determining folder grouping priority
);Database maintenance routines clean up the storage file automatically on startup by pruning records for paths that no longer exist on disk, following up with a VACUUM process to recover space.
Visual assets and media formats generate preview thumbnails on separate threads using a coordinated scheduling loop.
-
Concurrency Control: To prevent excessive disk I/O and resource starvation, Flux throttles background image processing using a fixed Tokio
Semaphorepool, capping active worker allocations. -
XDG Cache Protocol Compliance: Thumbnail images are written into user storage locations under
$XDG_CACHE_HOME/thumbnails/. Filenames match the MD5 hex hash of the source item's formal RFC 2396 URI syntax (file://...). - Validation and Eviction Loop: Before pulling an existing thumbnail texture from the cache, Flux validates its integrity. It checks if the source file's modification time is newer than the cache asset's timestamp or if the file size is zero. If the validation check fails, the stale cache asset is evicted, and a new thumbnail is generated automatically.
-
Generation Stack: Flux handles image extraction natively using
gdk-pixbufscaling functions. For video formats, it invokesffmpegas an isolated subprocess, passing precise seeking arguments to extract frames efficiently:ffmpeg -y -loglevel panic -i <INPUT_PATH> -ss 00:00:01.000 -vframes 1 -vf scale=<THUMB_SIZE>:-1 <OUTPUT_CACHE_PATH>
The configuration layout defines global application preferences:
[ui]
default_icon_size = 96 # Base icon resolution mapping (pixels)
startup_window_width = 1280 # Base window width on launch
startup_window_height = 800 # Base window height on launch
sidebar_width = 200 # Structural spacing for sidebar tray (pixels)
single_click = false # Single-click interaction activation toggle
show_xdg_dirs = false # Flag determining default sidebar bookmarks
default_sort = "Name" # Initial sort state rule choice
show_hidden_by_default = false # Flag to render dotfiles on startup
folders_first = true # Folders always sorted above files
theme = "default" # Applied CSS stylesheet identifier
show_csd = false # Client-Side Decorations rendering toggle
start_maximized = true # Launch window maximized
[ui.terminal]
height = 50 # Allocated character lines for terminal pane
fg_color = "#E5E5E5" # Terminal text color hex code
bg_color = "#1A1A1A" # Terminal layout background hex code
font = "JetBrains Mono 11" # Font asset definition string
[shortcuts]
back = "BackSpace" # linear navigation history back trigger
forward = "<Alt>Right" # linear navigation history forward trigger
open = "Return" # Primary selection confirmation mapping
delete = "Delete" # Item removal execution binding
refresh = "F5" # Force directory reload binding
search = "<Primary>f" # Search query input activation shortcut
toggle_hidden = "<Primary>h" # Dotfile visualization toggle binding
[[sidebar]] # Customized user bookmark items list
name = "Projects"
icon = "folder-development-symbolic"
path = "~/Development/Rust"Flux uses a custom Domain-Specific Language (DSL) in menu.rs to configure right-click and context menus. This allows users to add custom entries and processing pipelines without modifying or recompiling the application.
-
Syntax Rules:
"Label" => "MIME Filters", "Shell Execution Blueprint String", "Optional Status Feedback Message" -
Supported Placeholders:
-
%p: Expands to the absolute filesystem path of the selected file. -
%d: Expands to the path of the parent directory enclosing the selection. -
%f: Expands to the plain filename string without its directory path context.
-
# --- Core File Actions ---
" Copy" => "all", "builtin::copy", "Copied selection to clipboard"
" Paste" => "all", "builtin::paste", "Pasted items successfully"
" Open With..." => "file", "builtin::open_with"
" Move to Trash" => "all", "gio trash %p", "Item moved to trash location"
# --- Advanced Operations & Utilities ---
" Open Terminal Here" => "directory", "alacritty --working-directory=%p"
" Open in VSCode" => "text/all, application/all", "code %p"
" Extract Archive Content" => "application/zip, application/x-7z-compressed", "7z x %p -o%p_extracted", "Extraction loop started"
# --- Automated FFmpeg Pipelines ---
" Convert to MP4 Container" => "video/all", "ffmpeg -i %p -codec copy %p.mp4", "Processing video conversion..."
" Extract Audio MP3 Layer" => "video/all", "ffmpeg -i %p -vn -acodec libmp3lame -q:a 2 %p.mp3", "Extracting audio track..."
# --- Image Management Actions ---
" Set Desktop Wallpaper" => "image/all", "swww img %p", "System wallpaper updated"
To protect against command-injection vulnerabilities through malformed file names, Flux processes shell actions safely within src/utils/helpers.rs:
let p_arg = format!("'{}'", path_str.replace("'", "'\\''"));
let d_arg = format!("'{}'", parent.replace("'", "'\\''"));
let f_arg = format!("'{}'", filename.replace("'", "'\\''"));All expanded parameter tokens are automatically wrapped in escaped single quotes before execution. This structure treats arguments strictly as literal string values, neutralising attempts at nested shell evaluation or argument injection.
The following table details the core hotkey mappings defined across src/ui/inputs.rs, src/utils/helpers.rs, and the shortcut controller stack:
| Key Combination | Message Reference / Operation Target | Functional System Description |
|---|---|---|
F1 |
AppMsg::ShowHelp |
Opens the graphical keyboard shortcut overlay window. |
F2 |
AppMsg::TriggerRenameSelection |
Puts the selected item into inline name editing mode. |
F3 |
AppMsg::ShowIconPicker |
Opens a custom icon picker dialog for the current directory. |
F4 |
AppMsg::ToggleTerminal |
Toggles the visibility of the embedded virtual terminal panel. |
F5 |
AppMsg::Refresh |
Forces a full reload of the current directory from disk. |
F8 |
AppMsg::ToggleSidebar |
Toggles the left navigation sidebar tray. |
F9 |
app.open-menu-editor |
Spawns the runtime context menu configuration editor window. |
F10 |
app.open-settings |
Launches the central preference settings interface panel. |
Return / KP_Enter
|
AppMsg::Activate |
Opens selected files or navigates into selected folders. |
Backspace |
AppMsg::GoBack |
Navigates back one step in the directory history log. |
Alt + Right |
AppMsg::GoForward |
Moves forward one step in the directory history stack. |
Ctrl + f |
AppMsg::SwitchHeader("search") |
Focuses the entry line and switches the interface to filtering mode. |
Ctrl + h |
AppMsg::ToggleHidden |
Toggles the visibility of hidden files and folders. |
Ctrl + s |
AppMsg::CycleSort |
Rotates through the available sorting criteria. |
Ctrl + Shift + s |
AppMsg::ToggleSortOrder |
Toggles between ascending and descending sort order. |
Shift + s |
AppMsg::CycleFolderPriority |
Toggles whether folders are grouped above files when sorting. |
Insert |
AppMsg::AddExclusive(None) |
Adds the selected path to the temporary quick list cache. |
Tab |
AppMsg::NextExclusive |
Cycles forward to the next folder in the temporary quick list. |
Ctrl + Tab |
AppMsg::PrevExclusive |
Cycles backward to the previous folder in the temporary quick list. |
Ctrl + Insert |
AppMsg::AddToSidebarPermanent |
Pins the current directory to the configuration bookmarks permanently. |
Ctrl + Delete |
AppMsg::ClearExclusive |
Clears all entries from the temporary quick list cache. |
To allow users to find files quickly without needing to explicitly focus search bars, Flux captures keystrokes using a focus bypass pattern within src/utils/helpers.rs:
// Verify active widget context state parameters
let is_editable = ctrl
.widget()
.and_then(|w| w.root())
.and_then(|r| r.focus())
.map(|f| f.type_().is_a(gtk::Editable::static_type()))
.unwrap_or(false);
// If an editable field already has focus, allow text input to proceed normally
if is_editable && (header_view == "search" || header_view == "entry") {
if keyval == gdk::Key::Escape {
sender.input(AppMsg::UpdateFilter(String::new()));
sender.input(AppMsg::SwitchHeader("path".to_string()));
return glib::Propagation::Stop;
}
return glib::Propagation::Proceed;
}
// Global Capture: If no modifiers are held, alphanumeric keypresses activate search
if modifiers.is_empty() {
if let Some(c) = keyval.to_unicode() {
if c.is_ascii_alphanumeric() {
sender.input(AppMsg::SwitchHeader("search".to_string()));
sender.input(AppMsg::SearchInput(c));
return glib::Propagation::Stop;
}
}
}- Mechanics: This system captures alphabetical or numerical keystrokes directly on the main window container.
- Behavior: Typing any valid character automatically switches the header panel to search mode, transfers input focus to the filter entry bar, and appends the character—ensuring zero-click filtering.
Flux maps complex mouse and input actions to specific operations using specialized event controllers:
-
GestureDrag(Rubberbanding): Click and drag across the item grid area selects multiple files dynamically. -
EventControllerScroll(Ctrl + Scroll): HoldingCtrlwhile scrolling adjusts icon resolutions dynamically via theAppMsg::Zoommessage loop. -
GestureClick(Middle-Click Window Spawns): Middle-clicking an item or holdingCtrlwhile double-clicking spawns a separate window instance rooted at that folder path. -
DropTarget(Drag and Drop Intercepts): Dragging items across independent window borders packages elements using the standardizedtext/uri-listprotocol, allowing file moves across individual running processes.