From cbaa8231fbdf4d7397a2194f1f4d1a93ab3d202a Mon Sep 17 00:00:00 2001 From: pie-314 Date: Sun, 17 May 2026 22:21:09 +0530 Subject: [PATCH] docs: sync documentation and fix CLI mouse capture --- CONTRIBUTING.md | 17 +++++--- Cargo.lock | 2 +- README.md | 62 +++++++++++++++++------------ book/src/backends/apt.md | 4 +- book/src/backends/aur-yay.md | 6 +-- book/src/backends/homebrew.md | 15 +++---- book/src/backends/index.md | 27 ++++--------- book/src/backends/pacman.md | 21 ++++++---- book/src/configuration.md | 75 ++++++++++++++++++++++++++++------- book/src/introduction.md | 3 ++ book/src/roadmap.md | 6 ++- book/src/usage.md | 24 ++++++++++- src/main.rs | 32 ++++++++------- 13 files changed, 191 insertions(+), 103 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55f961e..c0cc812 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -157,12 +157,17 @@ PRs should not include unrelated formatting changes. To add a new provider, implement the trait: ```rust -pub trait PackageManager { - fn search(&self, query: &str) -> Result, ManagerError>; - fn install(&self, pkg: &str) -> Result<(), ManagerError>; - fn remove(&self, pkg: &str) -> Result<(), ManagerError>; - fn update(&self, pkg: &str) -> Result<(), ManagerError>; - fn info(&self, pkg: &str) -> Result; +pub trait PackageManager: Send + Sync { + fn name(&self) -> &str; + fn search(&self, query: &str) -> Vec; + fn get_installed(&self) -> HashSet; + fn get_installed_details(&self) -> Vec; + fn get_updates(&self) -> Vec; + fn get_details(&self, pkg: &str, provider: &str) -> Option>; + fn install(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet) -> Result<(), Box>; + fn remove(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet) -> Result<(), Box>; + fn system_upgrade(&self, terminal: &mut DefaultTerminal) -> Result<(), Box>; + fn refresh_databases(&self, terminal: &mut DefaultTerminal) -> Result<(), Box>; } ``` diff --git a/Cargo.lock b/Cargo.lock index 0a58dd8..bae6e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1852,7 +1852,7 @@ dependencies = [ [[package]] name = "trx-cli" -version = "0.1.9" +version = "0.1.10" dependencies = [ "color-eyre", "directories", diff --git a/README.md b/README.md index 5872693..163271a 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,18 @@ Search 50,000+ packages in under 50ms. Install, remove, and update without leavi TRX is a terminal UI built on top of your existing package manager. It gives you a unified, keyboard-first interface for searching, inspecting, and managing packages, whether you're on macOS with Homebrew, Arch with Pacman + AUR, or Debian/Ubuntu with APT. -No daemon. No config file. Just run `trx`. +No daemon required. Fully configurable via `config.toml` or the in-app Settings tab. --- ## Features * Renderer built on `ratatui` with deterministic layout and minimal redraw overhead -* Fully non-blocking event loop using `tokio` +* Fully non-blocking event loop using **OS threads** and **mpsc channels** (no async runtime overhead) * Unified command model for package managers with pluggable backend architecture * In-built fuzzy matcher optimized for substring scoring and ranking +* **Settings & Themes** – Configure keybindings, themes (Nord, Dracula, etc.), and UI styles in-app +* **Mouse Support** – Full navigation and interaction via mouse * **Self-updating mechanism** – Checks for new releases on startup and updates automatically * Stateless backend operations executed via system calls with structured output parsing * Extensible design suitable for adding new package managers without modifying the core engine @@ -70,7 +72,7 @@ sudo cp target/release/trx /usr/local/bin/ ### Cargo ```bash -cargo install trx +cargo install trx-cli ``` --- @@ -92,7 +94,8 @@ trx | `x` | Remove selected | | `U` | System upgrade | | `R` | Refresh databases | -| `Tab` | Switch tab (Search → Installed → Updates) | +| `Tab` | Switch tab (Search → Installed → Updates → Settings) | +| `?` | Toggle help overlay | | `q` / `Esc` | Quit / exit current mode | --- @@ -102,36 +105,41 @@ trx ``` src/ ├── main.rs # Entry point, terminal setup +├── config.rs # Configuration and Theme management +├── updater.rs # Self-update logic ├── ui/ │ ├── app.rs # App state, event loop, channel polling -│ └── input.rs # Input mode, debounce logic +│ ├── draw.rs # UI rendering and layout +│ └── input.rs # Input mode handling ├── managers/ │ ├── mod.rs # PackageManager trait, shared parsing -│ ├── arch.rs # Pacman + AUR (yay) backend +│ ├── arch.rs # Parallel Pacman + AUR (RPC API) backend │ ├── apt.rs # APT backend │ └── brew.rs # Homebrew backend └── fuzzy/ - └── mod.rs # Scoring engine + └── mod.rs # Substring-optimized scoring engine ``` ### PackageManager trait ```rust -pub trait PackageManager { - fn search(&self, query: &str) -> Result, ManagerError>; - fn install(&self, terminal: &mut DefaultTerminal, pkgs: &[String]) -> Result<(), ManagerError>; - fn remove(&self, terminal: &mut DefaultTerminal, pkgs: &[String]) -> Result<(), ManagerError>; - fn get_installed(&self) -> Result, ManagerError>; - fn get_updates(&self) -> Result, ManagerError>; - fn get_details(&self, name: &str, provider: &str) -> Result; - fn system_upgrade(&self, terminal: &mut DefaultTerminal) -> Result<(), ManagerError>; - fn refresh_databases(&self) -> Result<(), ManagerError>; +pub trait PackageManager: Send + Sync { + fn name(&self) -> &str; + fn search(&self, query: &str) -> Vec; + fn get_installed(&self) -> HashSet; + fn get_installed_details(&self) -> Vec; + fn get_updates(&self) -> Vec; + fn get_details(&self, pkg: &str, provider: &str) -> Option>; + fn install(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet) -> Result<(), Box>; + fn remove(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet) -> Result<(), Box>; + fn system_upgrade(&self, terminal: &mut DefaultTerminal) -> Result<(), Box>; + fn refresh_databases(&self, terminal: &mut DefaultTerminal) -> Result<(), Box>; } ``` ### Concurrency model -Search, list loads, and detail fetches all run on **OS threads** communicating via `std::sync::mpsc`. The main loop polls keyboard input with a short timeout, redraws each frame, and non-blockingly drains result channels. +Search, list loads, and detail fetches all run on **OS threads** communicating via `std::sync::mpsc`. The main loop polls keyboard/mouse input with a short timeout, redraws each frame, and non-blockingly drains result channels. Large searches (like Arch + AUR) are parallelized using **Rayon**. --- @@ -139,20 +147,22 @@ Search, list loads, and detail fetches all run on **OS threads** communicating v | Manager | Platform | Status | |---------|----------|--------| -| Pacman | Arch Linux | Implemented | -| yay (AUR) | Arch Linux | Implemented | -| APT | Debian / Ubuntu | Implemented | -| Homebrew | macOS | Implemented | -| dnf / yum | Fedora / RHEL | Planned | -| zypper | openSUSE | Planned | -| winget / scoop | Windows | Planned | +| Pacman | Arch Linux | ✅ Implemented | +| yay (AUR) | Arch Linux | ✅ Implemented | +| APT | Debian / Ubuntu | ✅ Implemented | +| Homebrew | macOS / Linux | ✅ Implemented | +| dnf / yum | Fedora / RHEL | 🔜 Planned | +| zypper | openSUSE | 🔜 Planned | +| winget / scoop | Windows | 🔜 Planned | --- ## Roadmap -- [ ] Configurable keybindings via config file -- [ ] Pluggable themes and renderer settings +- [x] Configurable keybindings via config file +- [x] Pluggable themes and renderer settings +- [x] Settings Tab for in-app configuration +- [x] Mouse support - [ ] Transaction history and rollback - [ ] Batch mode for scripting / CI use - [ ] Dependency graph visualizer diff --git a/book/src/backends/apt.md b/book/src/backends/apt.md index 7e4e38c..6d5489b 100644 --- a/book/src/backends/apt.md +++ b/book/src/backends/apt.md @@ -24,7 +24,9 @@ The name is extracted, fuzzy-scored against the query, and packages with score ## Package Details -`get_details` runs `apt-cache show ` and parses the colon-separated RFC 822-style output into a `HashMap`. Keys include `Package`, `Version`, `Description`, `Depends`, `Homepage`, and others. +`get_details` runs `apt-cache show ` and returns the raw RFC 822-style output in the detail panel. This includes fields like `Package`, `Version`, `Description`, `Depends`, and `Homepage`. + +Results are cached in `DETAILS_CACHE` to avoid repeated subprocess calls. --- diff --git a/book/src/backends/aur-yay.md b/book/src/backends/aur-yay.md index c4d97c8..cc8e74b 100644 --- a/book/src/backends/aur-yay.md +++ b/book/src/backends/aur-yay.md @@ -17,19 +17,19 @@ aur_helper = "paru" ## Search -`search_aur` calls ` -Ss ` and parses the alternating-line output via `parse_alternating_lines`. Packages receive the provider string `"aur"`. +`search_aur` calls the **AUR RPC API** (`v5`) directly via `reqwest`. This avoids spawning a subprocess for searching the AUR, significantly improving performance. Packages receive the provider string `"aur"`. --- ## Package Details -`aur_details` runs ` -Si ` and parses the colon-separated output into a `HashMap`. The `Maintainer`, `URL`, `Votes`, and `Popularity` fields typically appear in AUR results. +`aur_details` also uses the **AUR RPC API** (`info` type) to fetch detailed package metadata. The response JSON is mapped to TRX's internal detail structure, including fields like `Maintainer`, `URL`, `Votes`, and `Popularity`. --- ## Install -`aur_install` runs ` -S ` via `execute_external_command`, handing control of the terminal to the helper's interactive output (which typically presents a PKGBUILD review step). +`aur_install` runs the configured ` -S --needed` via `execute_external_command`. It strips any provider prefixes (like `aur/`) before calling the helper. --- diff --git a/book/src/backends/homebrew.md b/book/src/backends/homebrew.md index e16af54..513eca9 100644 --- a/book/src/backends/homebrew.md +++ b/book/src/backends/homebrew.md @@ -20,13 +20,14 @@ The Homebrew backend is implemented in `src/managers/brew.rs` as `BrewManager` ( ## Package Details -`get_details` calls `brew info --json=v2 ` and parses the JSON response. Key fields surfaced in the TUI sidebar include: - -- `name` -- `desc` -- `homepage` -- `versions.stable` -- `installed` (list of installed versions) +`get_details` calls `brew info ` and parses the text output. Key fields surfaced in the TUI sidebar include: + +- **Name** and **Version** (from the first line) +- **Description** (from the second line) +- **URL** (from the third line) +- **License** +- **Caveats** +- **Analytics** --- diff --git a/book/src/backends/index.md b/book/src/backends/index.md index 102c858..acf9442 100644 --- a/book/src/backends/index.md +++ b/book/src/backends/index.md @@ -46,33 +46,20 @@ pub trait PackageManager: Send + Sync { ## Backend Selection -`get_system_manager` in `src/managers/mod.rs` uses a simple priority order: +`get_system_manager` in `src/managers/mod.rs` selects backends based on system availability and user configuration: -1. `OS == "macos"` → `BrewManager` -2. `pacman --version` succeeds → `ArchManager` (Pacman + optional AUR helper) -3. `apt --version` succeeds → `AptManager` -4. Fallback → `ArchManager` (with default `yay` AUR helper) +1. Checks for installed package managers (`brew`, `pacman`, `apt`). +2. Filters based on `enabled_managers` in `config.toml`. +3. If multiple managers are enabled and available, returns a `CombinedManager`. +4. The `CombinedManager` multiplexes search, install, and update commands across all active backends. --- ## Shared Utilities -### `parse_alternating_lines` +### `SEARCH_CACHE` & `DETAILS_CACHE` -Many package manager CLI tools output results in alternating-line format: - -``` - [flags...] - - ... - -``` - -`parse_alternating_lines` parses this format, calls `fuzzy_match` on each package name, drops scores ≤ 0.01, and returns results sorted by score. - -### `DETAILS_CACHE` - -A global `Arc>>>` used by all backends to cache detail lookups (the `get_details` call). This avoids repeated subprocess invocations when the user scrolls back to a previously inspected package. +TRX uses global thread-safe caches for both search results and package details to ensure the UI remains snappy even when navigating back and forth. --- diff --git a/book/src/backends/pacman.md b/book/src/backends/pacman.md index 46bb977..8aff9fa 100644 --- a/book/src/backends/pacman.md +++ b/book/src/backends/pacman.md @@ -23,16 +23,21 @@ It implements `PackageManager` by delegating to `pacman::*` and `yay::*` functio ## Search -`ArchManager::search` merges results from both `pacman -Ss` and the AUR helper, sorts by fuzzy score, and truncates to **50 results**: +`ArchManager::search` executes searches in parallel using **Rayon**. It merges results from both `pacman -Ss` and the configured AUR helper, sorts by fuzzy score, and truncates to the limit set in `config.toml` (default: 50): ```rust -fn search(&self, query: &str) -> Vec { - let mut all = pacman::search_pacman(query); - all.extend(yay::search_aur(query, &self.aur_helper)); - all.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Equal)); - all.truncate(50); - all -} +let results: Vec> = vec![0, 1] + .into_par_iter() + .map(|i| { + if i == 0 && show_pacman { + pacman::search_pacman(query) + } else if i == 1 && show_yay { + yay::search_aur(query, &self.aur_helper) + } else { + Vec::new() + } + }) + .collect(); ``` `search_pacman` calls `pacman -Ss ` and parses the alternating-line output via `parse_alternating_lines`. diff --git a/book/src/configuration.md b/book/src/configuration.md index 0f06da9..76ff77f 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -18,24 +18,71 @@ The config file follows the [XDG Base Directory](https://specifications.freedesk ## Options -```toml -# ~/.config/trx/config.toml +The `config.toml` is divided into sections: -# The AUR helper to use on Arch Linux systems. -# Any helper with yay-compatible CLI flags works (e.g. "paru", "aura"). -# Default: "yay" -aur_helper = "yay" -``` +### General Options + +| Option | Description | Default | +|--------|-------------|---------| +| `aur_helper` | The AUR helper to use (e.g. `yay`, `paru`, `aura`) | `"yay"` | +| `theme_name` | The active theme name | `"Default"` | + +### Settings Section (`[settings]`) + +| Option | Description | Default | +|--------|-------------|---------| +| `search_debounce_ms` | Delay before search triggers after typing | `200` | +| `auto_update_check` | Check for new TRX versions on startup | `true` | +| `auto_cleanup` | Automatically clean up package caches | `false` | +| `default_tab` | Tab to open on startup (`Search`, `Installed`, `Updates`, `Settings`) | `"Search"` | +| `max_search_results` | Maximum results to display | `50` | +| `enabled_managers` | List of package managers to use | `["pacman", "yay", "brew", "apt"]` | +| `border_style` | Style of UI borders (`Plain`, `Rounded`, `Double`, `Thick`) | `"Rounded"` | +| `spinner_type` | Style of loading spinner (`Dots`, `Bars`, `Pulse`, `Classic`) | `"Dots"` | + +### Keybindings Section (`[keys]`) + +All keys are configurable: + +| Option | Default | +|--------|---------| +| `quit` | `"q"` | +| `install` | `"i"` | +| `remove` | `"x"` | +| `search_edit` | `"e"` | +| `toggle_select` | `" "` | +| `tab_next` | `"Tab"` | +| `tab_prev` | `"BackTab"` | +| `system_upgrade` | `"U"` | +| `refresh_db` | `"R"` | +| `help` | `"?"` | --- -## Future Options +## Themes + +TRX comes with several built-in themes: + +- `Default` +- `Nord` +- `Dracula` +- `OneDark` +- `Gruvbox` +- `Solarized` +- `Custom` -The following options are planned for future releases: +### Custom Theme -- **Keybindings** — remap any key to a different action -- **Theme** — colour scheme selection (dark / light / custom) -- **Search limit** — maximum number of results per tab -- **Metadata cache TTL** — how long `DETAILS_CACHE` entries remain valid +When `theme_name = "Custom"` is set, TRX looks for a `[custom_theme]` section: + +```toml +[custom_theme] +border_color = "blue" +highlight_color = "yellow" +success_color = "green" +error_color = "red" +text_primary = "white" +text_secondary = "cyan" +``` -See the [Roadmap](./roadmap.md) for status. +Colors can be standard names (e.g. `"blue"`) or hex codes (e.g. `"#81A1C1"`). diff --git a/book/src/introduction.md b/book/src/introduction.md index b322b69..8c91ad7 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -21,6 +21,8 @@ No daemon. No config file required. Just run `trx`. | **Unified interface** | Same keybindings across all package managers | | **Non-blocking** | All I/O on OS threads — UI never freezes | | **Self-updating** | Checks GitHub releases on startup | +| **Themes** | Built-in Nord, Dracula, Gruvbox, and Custom themes | +| **Mouse Support** | Full navigation and interaction via mouse | | **Extensible** | Pluggable backend trait — add a new PM in one file | --- @@ -43,5 +45,6 @@ No daemon. No config file required. Just run `trx`. - [Installation](./installation.md) — get TRX running in 30 seconds - [Usage](./usage.md) — keybindings and daily workflow +- [Web Version](https://trx.sh) — try the interactive demo on the web - [Architecture](./architecture/overview.md) — how TRX is structured internally - [Adding a Backend](./backends/new-backend.md) — extend TRX to support a new package manager diff --git a/book/src/roadmap.md b/book/src/roadmap.md index 637909a..5d3d983 100644 --- a/book/src/roadmap.md +++ b/book/src/roadmap.md @@ -18,8 +18,6 @@ This page tracks planned and in-progress features for TRX. | Feature | Description | |---------|-------------| -| **Configurable keybindings** | Remap any key via `config.toml` | -| **Pluggable themes** | Colour scheme selection and custom themes via config | | **Transaction history** | Log of installs/removes with rollback support | | **Batch / scripting mode** | Non-interactive mode for CI and shell scripts | | **Dependency graph visualiser** | Visual view of package dependency trees | @@ -38,6 +36,10 @@ This page tracks planned and in-progress features for TRX. | Homebrew backend | v0.1.0 | | Self-updating mechanism | v0.1.2 | | Binary releases via GitHub Actions | v0.1.4 | +| Configurable keybindings | v0.1.5 | +| Built-in and Custom Themes | v0.1.6 | +| Mouse Support | v0.1.7 | +| Settings Tab | v0.1.8 | --- diff --git a/book/src/usage.md b/book/src/usage.md index ba1a804..da0aeab 100644 --- a/book/src/usage.md +++ b/book/src/usage.md @@ -19,6 +19,7 @@ TRX has three tabs, cycled with `Tab` / `Shift+Tab`: | **Search** | Fuzzy-search all available packages | | **Installed** | Browse packages currently installed on the system | | **Updates** | Packages with a newer version available | +| **Settings** | Configure TRX, themes, and backends in-app | --- @@ -39,8 +40,10 @@ TRX has three tabs, cycled with `Tab` / `Shift+Tab`: |-----|--------| | `↑` / `k` | Move selection up | | `↓` / `j` | Move selection down | +| `h` / `l` | Scroll details (in Search/Installed/Updates) or change values (in Settings) | +| `Home` / `End` | Jump to top or bottom of list | -### Package Operations +### Package Operations (Search/Installed/Updates Tabs) | Key | Action | |-----|--------| @@ -57,6 +60,25 @@ TRX has three tabs, cycled with `Tab` / `Shift+Tab`: | `e` | Enter search / editing mode | | `Esc` | Exit search mode (return to normal navigation) | +### Settings Tab + +| Key | Action | +|-----|--------| +| `Space` / `Enter` | Toggle boolean settings or enter editing for values | +| `h` / `l` | Cycle through themes, border styles, and spinners | +| `k` / `j` | Navigate through settings options | + +--- + +## Mouse Support + +TRX features full mouse support for a hybrid workflow: + +- **Tabs**: Click on a tab name to switch to it. +- **Scrolling**: Use the scroll wheel to navigate package lists or the details panel. +- **Selection**: Click a package in the list to select it; click the checkbox area to toggle its selection for operations. +- **Settings**: Click any setting to focus it; click the value area to toggle or edit. + --- ## Workflow Example diff --git a/src/main.rs b/src/main.rs index 5f98b42..71bf808 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use color_eyre::Result; use managers::Package; use ratatui::crossterm::{ cursor::{Hide, Show}, - event::DisableMouseCapture, + event::{DisableMouseCapture, EnableMouseCapture}, execute, terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}, }; @@ -26,22 +26,26 @@ fn main() -> Result<()> { return Ok(()); } "--help" | "-h" => { + let config = config::Config::load(); + let keys = &config.keys; println!("trx - A Modern Cross-Platform Package Manager TUI"); println!("\nUsage: trx [OPTIONS]"); println!("\nOptions:"); println!(" -v, --version Print version information"); println!(" -h, --help Print help information"); - println!("\nKeybindings (inside TUI):"); - println!(" q Quit"); - println!(" Tab Switch between Search, Installed, and Updates tabs"); - println!(" Shift+Tab Switch backwards between tabs"); - println!(" e Edit search query (Search tab)"); - println!(" Space Select/unselect packages"); - println!(" i Install selected packages"); - println!(" x Remove selected packages"); - println!(" U Full system upgrade"); - println!(" R Refresh package databases"); - println!(" ? Toggle help overlay"); + println!("\nKeybindings (current configuration):"); + println!(" {:<16} Quit", keys.quit); + println!(" {:<16} Switch to next tab", format!("{}/Tab", keys.tab_next)); + println!(" {:<16} Switch to previous tab", format!("{}/Shift+Tab", keys.tab_prev)); + println!(" {:<16} Edit search query (Search tab)", keys.search_edit); + println!(" {:<16} Toggle package selection or settings", if keys.toggle_select == " " { "Space".to_string() } else { keys.toggle_select.clone() }); + println!(" {:<16} Install selected packages", keys.install); + println!(" {:<16} Remove selected packages", keys.remove); + println!(" {:<16} Full system upgrade", keys.system_upgrade); + println!(" {:<16} Refresh package databases", keys.refresh_db); + println!(" {:<16} Toggle help overlay", keys.help); + println!("\nMouse Support:"); + println!(" Full navigation, tab switching, and scrolling are supported."); return Ok(()); } _ => {} @@ -50,7 +54,7 @@ fn main() -> Result<()> { color_eyre::install()?; let mut terminal = init(); - execute!(std::io::stdout(), DisableMouseCapture)?; + execute!(std::io::stdout(), EnableMouseCapture)?; let (result_tx, result_rx): (mpsc::Sender<(String, Vec)>, mpsc::Receiver<(String, Vec)>) = mpsc::channel(); let app_result = App::new(result_tx.clone(), result_rx).run(&mut terminal); @@ -106,7 +110,7 @@ pub fn execute_external_command( let mut input = String::new(); io::stdin().read_line(&mut input)?; - execute!(terminal.backend_mut(), EnterAlternateScreen)?; + execute!(terminal.backend_mut(), EnterAlternateScreen, EnableMouseCapture)?; terminal::enable_raw_mode()?; execute!(terminal.backend_mut(), Hide)?; terminal.clear()?;