Adding a Right Panel
Every right-side panel in Flux, Tag, Search, Diff, Location, Connect,
Preview, Actions, Terminal, is wired through the same set of files.
The terminal panel (src/ui/panels/terminal.rs and the
RightPanelType::Terminal arms in src/utils/helpers/sidebar.rs) is the
smallest complete example, because it exercises every hook: config flag,
message variants, lazy build, spawn-on-reveal, kill-on-hide, sidebar
stash, focus skip. Read those two files before starting.
Checklist
Work through these in order. Each line names the file, the change, and why that file is on the list. Missing any of them is either a compile error or a silent bug.
-
src/model/config/ui.rs- add the enable flag (boolwith#[serde(default)]). If the panel has a width or height, add ani32field defaulted via a helper insrc/model/defaults.rs. -
src/utils/config/defaults.rs- mirror the field in the parse-failureUIConfig { ... }literal. Both branches must stay in sync or a corruptconfig.tomlsilently drops the field. -
docs/config.md- runcargo run --bin gen-docs. Do not edit by hand. -
src/model/types.rs- add a variant toRightPanelType, grouped with the other panel variants. -
src/model/app.rs- add three fields minimum:Option<gtk::Revealer>,bool visible,bool initialized. If the panel hides the left sidebar (Terminal, Preview), addOption<bool>to stash the prior visibility. Doc-comment each field so theDebugderive stays readable. -
src/ui/startup/components.rs- initialize the newFluxAppfields in the struct literal.None/falseunless the panel is open at startup. -
src/ui/view/component.rs- add agtk::Revealerin theview!macro with#[name = "<panel>_panel_revealer"],SlideLeft,transition_duration: 200,reveal_child: false,visible: false. Match the neighboring panels. -
src/ui/view/init.rs- instore_widget_refs, store the revealer on the model. If the panel owns a subprocess (shell, mpv), add aroot.connect_close_requestthat tears it down, mirroring the terminal block. -
src/utils/helpers/sidebar.rs- three edits insidetoggle_sidebar_right_panel:- add the variant to
hide_other_right_panelsso opening another panel closes this one; - add a match arm in the
(revealer, initialized, visible, is_search)tuple; - add a match arm in the panel-construction
matchthat returns thegtk::Boxfrombuild_<panel>_panel(...).
If the panel needs focus (Location, Connect, Search), leave it out of
skip_focus. Otherwise add it toskip_focusso arrow keys keep driving the grid. - add the variant to
-
src/ui/panels/mod.rs-pub mod <panel>;andpub use <panel>::build_<panel>_panel;. -
src/ui/panels/<panel>.rs- exposepub fn build_<panel>_panel(initial_width: i32, sender: AsyncComponentSender<FluxApp>) -> gtk::Box. Useheader::panel_header,resize::resizable_panel, andspec::PanelSpec. Addconst fn <panel>()tospec.rswith the min / max / default widths. -
src/model/msg.rsandsrc/ui/update/*.rs- if the panel has its ownAppMsgvariants, add them doc-commented and route them in the closest domain file. -
po/pt_BR.po- new strings, grouped under# src/ui/panels/<panel>.rs.
Lifecycle rules
Panels are lazy: the widget tree is built on first open and kept alive afterwards. These five rules are what keeps close/reopen correct.
-
Spawn on reveal, kill on hide. Subprocesses (shell, media player) are spawned in the
now_visiblebranch and killed in the!now_visiblebranch oftoggle_sidebar_right_panel. Do not reuse a process across toggles - the fd, pipe, or socket will be stale. -
Do not grab focus by default. Preview, Actions and Terminal add themselves to
skip_focus. Do the same unless the panel's purpose is text entry (Location, Connect, Search). Grabbing focus breaks arrow-key scrubbing in the grid. -
Stash the left sidebar if the panel is wide. Terminal and Preview do this: store
Some(self.sidebar_visible)on first reveal, restore on hide. Do not touchconfig.ui.sidebar_visible- that is a persisted preference, not session state. -
Session-token every async load. Bump an
Arc<AtomicU64>on open, capture its value, drop results whose token no longer matches. This is what prevents old contents from flashing into a newly opened panel on rapid switching. -
Stop media on close. If the panel plays video or audio, call
stop_media()(or equivalent) in the!now_visiblebranch. Otherwise the GStreamer pipeline and its fds keep running in the background.
Verifying the result
Once wired up, the panel should:
- open and close on
F<key>without touching any other panel's state; - survive a config reload (
F10on the settings window, then close it) with its flag preserved; - keep the file grid responsive to arrow keys while it is open;
- leave no subprocess behind after the window closes
(
ps -ef | grep -E 'mvp|sh -l'should not show orphans); - not flash stale contents when re-opened after the underlying data changed on disk.