Architecture
A map of the codebase for contributors. Read it before the how-to guides: Adding a right panel, Adding an archive backend, Adding a menu action, Adding a search filter and Adding a keybinding.
Crates and entry points
Cargo.toml defines three targets:
| Target | Entry point | Purpose |
|---|---|---|
library flux | src/lib.rs | Argument parsing, the doc generators, and the code that the integration tests link against. |
binary flux-fm | src/main.rs, which calls cli::run() | The application. |
binary gen-docs | src/bin/gen-docs.rs | Regenerates the derived documentation. |
src/main.rs declares its own mod model, mod services, mod ui and mod utils. The same files are therefore compiled twice, once into the library and once into the binary. This has consequences:
cargo fmt --checkandcargo clippyreport a problem once per crate.- A crate-level lint such as
#![allow(clippy::result_large_err)]must appear in bothlib.rsandmain.rs. - A type that lives in
modelexists in both crates and the two are not interchangeable.flux::args::WindowOptionsis the library's type, andcli/launch.rscopies it field by field into the binary's own.
Startup
cli::run resolves the command line with flux::args::resolve_startup_action and then picks one of these paths:
- Commands that need no display run first and exit:
--selection,--toggle,--showand--hide. They are cheap enough to be called from every$Fexpansion in a shell. - The IPC server for
--cwdstarts before any client can connect. If another window already owns the socket,--cwdacts as a client and exits. - One-shot commands such as
--theme,--set-iconand--reset-iconchangeconfig.tomland exit without a window. - Everything else reaches
cli/launch.rs, which builds anAppInitand runs the Relm4 componentFluxApp.
Message flow
The UI follows Relm4's model, view, update pattern.
FluxAppinsrc/model/app.rsis the model. It owns the state and the widget references.AppMsginsrc/model/msg.rsis every event the app can receive. Each variant is doc-commented.FluxApp::handle_updateinsrc/ui/update/mod.rsis the single entry point for messages. It passes the message through a chain of routers in this order:navigation,loading,view,thumbnails,search,file_ops,dialogs,context_menu,network,git,windowandsidebar.- Each router exposes
handle(app, message, sender) -> Result<(), AppMsg>.Ok(())means it consumed the message.Err(message)hands the message to the next router. - A message that no router claims is logged as
[flux] unhandled AppMsg. A newAppMsgvariant that you never routed shows up there, not as a compile error.
Routers stay thin. The real work lives in handle_* methods on FluxApp under src/ui/ops/ and src/ui/dialog/.
Threads
GTK objects belong to the main thread. Everything that can block runs elsewhere and reports back with an AppMsg.
- Background threads reach the app through
model::SENDER, a globalrelm4::Sender<AppMsg>. The--cwdserver, thegotowatcher and the selection server all use it. - Questions that need the UI are forwarded as a message with a reply channel. The selection server sends
AppMsg::QuerySelection { reply }and waits up to two seconds for the main thread to answer. - Directory loading runs on a dedicated Rayon pool in
src/services/loader/pool.rs. It has between 4 and 8 threads with 2 MiB stacks, so the first large folder does not commit the global pool's much larger stacks. Results reach the grid in batches ofui.loader_batch_sizeitems. - Subprocesses, network work and transfers use Tokio, and short blocking jobs use
relm4::spawn_blocking. A dispatch arm or message handler on the main thread should return in microseconds.
Persistence
config.tomlis read throughutils::load_config, which keeps a cached copy.save_configprimes that cache andinvalidate_config_cachedrops it. A config watcher insrc/cli/watcher.rsreloads the file while Flux runs. Migrations of renamed keys run when the file is loaded, for exampleui.terminal_in_sidebar, and are listed in Configuration.- SQLite state lives behind
StateManagerinsrc/services/db/. It runs in WAL mode and holds per-folder view settings, location history and file tags, with one module per area:views,history,tags,iconsandmaintenance. - Shell bridge files under
$XDG_CACHE_HOME/flux/are described in Shell integration.
Source map
| Path | Contents |
|---|---|
src/args.rs, src/cli/ | Argument parsing and the non-GUI commands. |
src/ipc.rs | The --cwd Unix socket server and client. |
src/shell_functions.rs | The fish helper source, shared by the terminal snippet and gen-docs. |
src/model/ | FluxApp, AppMsg, shared types, config/ structs and their defaults. |
src/ui/update/ | The message routers. |
src/ui/ops/ | The handle_* methods that do the work, grouped by area: files, paste, view, tags, tasks, undo, terminal. |
src/ui/panels/ | Right-side panels: tag, search, diff, location, connect, preview, actions and terminal. |
src/ui/dialog/, src/ui/properties/ | Dialogs and the properties window. |
src/ui/menu/ | The context menu, the Ctrl+right-click menu and the menu editor. |
src/ui/inputs/, src/ui/keymap/ | Keyboard handling and the configurable shortcut table. |
src/ui/components/ | The file card and list row, its builder and its bind. |
src/ui/settings/, src/ui/help/ | The settings window and the help window. |
src/ui/startup/ | Building the window, sidebar, tabs and grid. |
src/ui/debug/ | The debug window. |
src/services/loader/ | Directory, archive, network and Recents loading. |
src/services/archive/ | The ArchiveBackend trait and one backend per format. |
src/services/search/ | Content search, the file indexer and the recursive walk. |
src/services/thumbnails/, src/services/tasks/ | Thumbnail generation and the file-operation task queue. |
src/services/terminal/ | The embedded terminal: PTY, emulator, drawing, and the shell bridge. |
src/services/ (others) | db, git, inspector, luks, mounts, network, trash. |
src/utils/ | Config loading, path and glob helpers, media and icon helpers, search parsers and hwga. |
Debug and performance tooling
hit!()adds a call counter to a function.hit!("name")gives it a stable name. In release builds both compile to nothing. Thehwgamodule insrc/utils/hwga/collects the counts and flags calls on the main thread that take longer than 16 ms, one 60 Hz frame.- Tests can read the counters.
tests/hwga_regression_test.rsuseshwga::countto assert that icon lookups do not rescan directories again and again. Such tests only exist in debug builds. - Debug builds time every call to
handle_updateand report slow messages. Ctrl+Shift+F7, bound toshortcuts.open_debug_window, opens a debug window with process and memory diagnostics.
Build script
build.rs compiles the GResource bundle from data/resources.gresource.xml and exports the short git hash as FLUX_GIT_HASH. When you build with the local profile and set FLUX_SYNC_ASSETS, it also runs cargo fmt and make install with BIN_PATH pinned to target/local/flux-fm. It skips both when SOURCE_DATE_EPOCH is set, which is how packaging tools such as Arch's makepkg build.
Generated files
cargo run --bin gen-docs writes every file below. A test in the generating module fails when the committed copy is stale. See Testing and CI.
| Output | Generated from |
|---|---|
docs/cli.md | src/doc_cli.rs |
docs/config.md | src/model/doc_config.rs and the config structs |
docs/css-variables.md | src/ui/doc_css_variables.rs and src/ui/style.css |
docs/actions.md | The BUILTINS table in src/ui/menu/editor/builtins.rs |
docs/search.md | src/utils/search/doc.rs |
completions/flux-fm.fish | The CLI_OPTIONS table in src/args.rs, through src/doc_fish.rs |
data/fish/flux-integration.fish | src/shell_functions.rs, through src/doc_fish.rs |