Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<Package>, 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<PackageInfo, ManagerError>;
pub trait PackageManager: Send + Sync {
fn name(&self) -> &str;
fn search(&self, query: &str) -> Vec<Package>;
fn get_installed(&self) -> HashSet<String>;
fn get_installed_details(&self) -> Vec<Package>;
fn get_updates(&self) -> Vec<Package>;
fn get_details(&self, pkg: &str, provider: &str) -> Option<HashMap<String, String>>;
fn install(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet<String>) -> Result<(), Box<dyn std::error::Error>>;
fn remove(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet<String>) -> Result<(), Box<dyn std::error::Error>>;
fn system_upgrade(&self, terminal: &mut DefaultTerminal) -> Result<(), Box<dyn std::error::Error>>;
fn refresh_databases(&self, terminal: &mut DefaultTerminal) -> Result<(), Box<dyn std::error::Error>>;
}
```

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 36 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -70,7 +72,7 @@ sudo cp target/release/trx /usr/local/bin/
### Cargo

```bash
cargo install trx
cargo install trx-cli
```

---
Expand All @@ -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 |

---
Expand All @@ -102,57 +105,64 @@ 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<Vec<Package>, 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<Vec<Package>, ManagerError>;
fn get_updates(&self) -> Result<Vec<Package>, ManagerError>;
fn get_details(&self, name: &str, provider: &str) -> Result<DetailsState, ManagerError>;
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<Package>;
fn get_installed(&self) -> HashSet<String>;
fn get_installed_details(&self) -> Vec<Package>;
fn get_updates(&self) -> Vec<Package>;
fn get_details(&self, pkg: &str, provider: &str) -> Option<HashMap<String, String>>;
fn install(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet<String>) -> Result<(), Box<dyn std::error::Error>>;
fn remove(&self, terminal: &mut DefaultTerminal, pkgs: &HashSet<String>) -> Result<(), Box<dyn std::error::Error>>;
fn system_upgrade(&self, terminal: &mut DefaultTerminal) -> Result<(), Box<dyn std::error::Error>>;
fn refresh_databases(&self, terminal: &mut DefaultTerminal) -> Result<(), Box<dyn std::error::Error>>;
}
```

### 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**.

---

## Supported Package Managers

| 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
Expand Down
4 changes: 3 additions & 1 deletion book/src/backends/apt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <pkg>` and parses the colon-separated RFC 822-style output into a `HashMap<String, String>`. Keys include `Package`, `Version`, `Description`, `Depends`, `Homepage`, and others.
`get_details` runs `apt-cache show <pkg>` 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.

---

Expand Down
6 changes: 3 additions & 3 deletions book/src/backends/aur-yay.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ aur_helper = "paru"

## Search

`search_aur` calls `<helper> -Ss <query>` 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 `<helper> -Si <pkg>` 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 `<helper> -S <packages>` 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 `<helper> -S <packages> --needed` via `execute_external_command`. It strips any provider prefixes (like `aur/`) before calling the helper.

---

Expand Down
15 changes: 8 additions & 7 deletions book/src/backends/homebrew.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <pkg>` 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 <pkg>` 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**

---

Expand Down
27 changes: 7 additions & 20 deletions book/src/backends/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
<name> <version> [flags...]
<description>
<name> <version> ...
<description>
```

`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<Mutex<HashMap<String, HashMap<String, String>>>>` 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.

---

Expand Down
21 changes: 13 additions & 8 deletions book/src/backends/pacman.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Package> {
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<Package>> = 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 <query>` and parses the alternating-line output via `parse_alternating_lines`.
Expand Down
75 changes: 61 additions & 14 deletions book/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"`).
3 changes: 3 additions & 0 deletions book/src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

---
Expand All @@ -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
Loading
Loading