narsil 0.3.0

A terminal-based system resource monitor โ€” GPU-aware, Braille charts, per-char label inversion
<div align="center">
  <img src="icon.png" alt="narsil icon" width="128" /><br><br>
  <h1>Narsil</h1>
</div>

> A terminal-based system resource monitor written in Rust โ€” fast, readable, and GPU-aware.

Named after the reforged sword of Aragorn, **narsil** is built to be sharper than the tools that came before it. It targets developers and power users who live in the terminal and need real-time system insight without leaving it.

> **Platform support** โ€” narsil runs on **Linux**, **Windows**, and **macOS**. The GPU tab (AMD/NVIDIA) is Linux-only for now; all other tabs work on every supported OS.

---

## ๐Ÿ“ธ Screenshot

![narsil screenshot](screenshot.png)

---

## โœจ Features

### Current scope (v0.1)

| Tab | What you see | Platform |
|-----|-------------|----------|
| ๐Ÿ—บ๏ธ **Overview** | CPU gauge, RAM gauge, live RX/TX sparklines, top processes (fills available height) | all |
| ๐Ÿง  **CPU** | Global usage history chart (Braille), per-core gauges with colour-coded load | all |
| ๐Ÿ’พ **Memory** | RAM + Swap history charts, GiB usage gauges | all |
| ๐ŸŒ **Network** | Combined RX/TX history chart, per-direction current throughput | all |
| ๐Ÿ’ฟ **Disks** | Per-partition usage bars at fixed height, scrollable when partitions exceed the terminal | all |
| ๐Ÿ”ฌ **Processes** | Process table sorted by CPU, scrollable, fills available height | all |
| ๐ŸŽฎ **GPU** | Per-GPU cards with utilisation + VRAM history charts, gauges, temperature and power draw | **Linux only** |

### ๐Ÿ”‘ Key behaviours

- ๐ŸŽจ **Split-colour gauges** โ€” the percentage label rendered inside every gauge automatically inverts its colour character-by-character at the fill boundary so it is always readable, even when the bar is exactly at 50%.
- ๐Ÿ“œ **Scroll indicators** โ€” any panel that cannot display all items at once shows `โ–ฒ`/`โ–ผ`/`โ–ฒโ–ผ` in its title.
- ๐Ÿ“ **Dynamic sizing** โ€” all panels adapt to the current terminal dimensions; no hard-coded row counts.
- โšก **Configurable refresh rate** โ€” pass `--interval <ms>` on startup (default 1 000 ms) to tune between low-latency and low-CPU usage; key events are processed between ticks with zero busy-waiting.
- โŒจ๏ธ **Keyboard-first navigation**: `Tab` / `Shift+Tab` wrap-around tab switching; `1`โ€“`6` direct jump (`1`โ€“`7` on Linux); `j`/`k` or arrow keys for scrolling; `q` or `Ctrl-C` to quit.
- ๐Ÿ’ฌ **Status bar** โ€” persistent one-line keybinding reference at the bottom, context-aware per tab.

---

## ๐Ÿš€ Installation

### Prerequisites

- **Linux**, **Windows 10+**, or **macOS 12+**
- GPU tab requires Linux with standard `/sys` mounts and `amdgpu` (AMD) or NVIDIA proprietary drivers
- Rust toolchain โ‰ฅ 1.85 โ€” needed only for `cargo install` or source builds (`rustup update stable`)

### Official release channels

| Channel | Platforms | Standard | NVIDIA variant |
|---------|-----------|----------|----------------|
| **crates.io** | all | `cargo install narsil` | `cargo install narsil --features nvidia` |
| **AUR** | Arch Linux | `narsil` ยท `narsil-bin` | `narsil-nvidia` ยท `narsil-nvidia-bin` |
| **AppImage** | Linux x86_64 | `narsil-{ver}-x86_64.AppImage` | `narsil-nvidia-{ver}-x86_64.AppImage` |
| **Windows zip** | Windows x86_64 | `narsil-{ver}-x86_64-windows.zip` | `narsil-nvidia-{ver}-x86_64-windows.zip` |
| **Linux tarball** | Linux x86_64 | `narsil-{ver}-x86_64.tar.gz` | `narsil-nvidia-{ver}-x86_64.tar.gz` |
| **Source** | all | `cargo build --release` | `cargo build --release --features nvidia` |

AppImage, Windows zip, and Linux tarball are attached to every [GitHub release](https://github.com/Pommersche92/narsil/releases).

### crates.io

```bash
cargo install narsil                      # standard
cargo install narsil --features nvidia    # with NVIDIA GPU support (Linux only)
```

### AUR (Arch Linux)

Four packages are published to the AUR:

| Package | Type | Description |
|---------|------|-------------|
| [`narsil`]https://aur.archlinux.org/packages/narsil | source | standard, compiled from the crates.io source tarball |
| [`narsil-nvidia`]https://aur.archlinux.org/packages/narsil-nvidia | source | like `narsil` but built with `--features nvidia` |
| [`narsil-bin`]https://aur.archlinux.org/packages/narsil-bin | binary | standard, installs prebuilt tarball from GitHub Releases |
| [`narsil-nvidia-bin`]https://aur.archlinux.org/packages/narsil-nvidia-bin | binary | NVIDIA variant, installs prebuilt tarball from GitHub Releases |

```bash
yay -S narsil              # standard, built from source
yay -S narsil-nvidia       # with NVIDIA GPU support, built from source
yay -S narsil-bin          # standard, prebuilt binary
yay -S narsil-nvidia-bin   # with NVIDIA GPU support, prebuilt binary
```

### AppImage (Linux x86_64)

Download from the [latest GitHub release](https://github.com/Pommersche92/narsil/releases/latest), make executable and run:

```bash
chmod +x narsil-*-x86_64.AppImage
./narsil-*-x86_64.AppImage
```

Two variants per release: `narsil-{version}-x86_64.AppImage` (standard) and `narsil-nvidia-{version}-x86_64.AppImage` (with NVIDIA support).

### Windows (x86_64)

Download `narsil-{version}-x86_64-windows.zip` or `narsil-nvidia-{version}-x86_64-windows.zip` from the [latest GitHub release](https://github.com/Pommersche92/narsil/releases/latest), extract, and run `narsil.exe` in PowerShell or cmd.

> The GPU tab is not compiled into the Windows build.

### Linux tarball (x86_64)

Download from the [latest GitHub release](https://github.com/Pommersche92/narsil/releases/latest):

```bash
tar xzf narsil-{version}-x86_64.tar.gz
./narsil-{version}/narsil
```

Both standard and NVIDIA variants are available.

### Build from source

```bash
git clone https://github.com/Pommersche92/narsil
cd narsil
cargo build --release
./target/release/narsil        # Linux / macOS
.\target\release\narsil.exe    # Windows

# With NVIDIA GPU support (Linux only):
cargo build --release --features nvidia
```

---

## ๐ŸŽฎ GPU support matrix

> GPU monitoring is **Linux-only**. On Windows and macOS the GPU tab is not compiled in; all other tabs work normally.

| Vendor | Driver | Detected | Utilisation | Memory | Temperature | Power |
|--------|--------|----------|-------------|--------|-------------|-------|
| ๐Ÿ”ด AMD discrete | `amdgpu` | โœ… | โœ… `gpu_busy_percent` | โœ… VRAM | โœ… hwmon | โœ… hwmon |
| ๐Ÿ”ด AMD iGPU (APU) | `amdgpu` | โœ… | โœ… | โœ… GTT (shared RAM) | โœ… | โœ… |
| ๐ŸŸข NVIDIA | proprietary + `--features nvidia` | โœ… | โœ… NVML | โœ… NVML | โœ… NVML | โœ… NVML |
| ๐Ÿ”ต Intel iGPU | `i915` / `xe` | โŒ | โ€” | โ€” | โ€” | โ€” |
| ๐Ÿ”ต Intel Arc discrete | `xe` | โŒ | โ€” | โ€” | โ€” | โ€” |

> โœ… **AMD APU note**: VRAM figures for APUs reflect GTT memory (system RAM dynamically assigned to the GPU). narsil detects this automatically and labels the memory panel **GTT** rather than VRAM.

> ๐Ÿ—“๏ธ **Intel note**: Intel GPU support is planned โ€” see Roadmap below.

---

## โŒจ๏ธ Keybindings

| Key | Action | Platform |
|-----|--------|----------|
| `Tab` / `Shift+Tab` | Next / previous tab (wraps around) | all |
| `1` โ€“ `6` | Jump directly to tab | all |
| `7` | Jump to GPU tab | Linux only |
| `โ†’` / `l` | Next tab | all |
| `โ†` / `h` | Previous tab | all |
| `โ†“` / `j` | Scroll down (Disks, Processes; + GPU on Linux) | all |
| `โ†‘` / `k` | Scroll up | all |
| `q` / `Ctrl-C` | Quit | all |

---

## ๐ŸŒ Localisation

narsil ships with translations for four languages. The UI language is selected automatically from the OS locale and can be overridden at startup.

| Language | Code | Auto-detected from |
|----------|------|--------------------|
| English  | `en` | default fallback |
| German   | `de` | `LANG=de_DE.UTF-8`, `LANGUAGE=de`, โ€ฆ |
| French   | `fr` | `LANG=fr_FR.UTF-8`, `LANGUAGE=fr`, โ€ฆ |
| Spanish  | `es` | `LANG=es_ES.UTF-8`, `LANGUAGE=es`, โ€ฆ |

### Detection priority

1. `--lang <code>` CLI flag
2. `LANGUAGE` environment variable (colon-separated preference list, GNU extension)
3. `LC_ALL` โ†’ `LC_MESSAGES` โ†’ `LANG`
4. Native OS locale API (`GetUserDefaultLocaleName` on Windows, `CFLocale` on macOS)
5. English fallback

```bash
narsil --lang de    # force German
narsil --lang fr    # force French
```

### Adding a new language

Translation strings live in plain TOML files under `locales/` โ€” no Rust knowledge needed to translate. See [locales/README.md](locales/README.md) for step-by-step instructions.

---

## ๐Ÿ—๏ธ Architecture

```
src/
โ”œโ”€โ”€ main.rs               โ€” terminal setup, raw-mode lifecycle, event + tick loop
โ”œโ”€โ”€ app.rs                โ€” App state dispatcher: calls each metrics::refresh on every tick
โ”œโ”€โ”€ metrics/
โ”‚   โ”œโ”€โ”€ mod.rs            โ€” HISTORY_LEN constant, push_history helper, re-exports
โ”‚   โ”œโ”€โ”€ cpu.rs            โ€” CpuState, per-core + global history
โ”‚   โ”œโ”€โ”€ memory.rs         โ€” MemState, RAM + swap
โ”‚   โ”œโ”€โ”€ network.rs        โ€” NetState, RX/TX rates and history
โ”‚   โ”œโ”€โ”€ disks.rs          โ€” DiskState, per-partition usage
โ”‚   โ”œโ”€โ”€ processes.rs      โ€” ProcessEntry, top-100 by CPU
โ”‚   โ””โ”€โ”€ gpu/
โ”‚       โ”œโ”€โ”€ mod.rs        โ€” GpuEntry, vendor dispatch
โ”‚       โ”œโ”€โ”€ amd.rs        โ€” sysfs-based AMD metrics
โ”‚       โ””โ”€โ”€ nvidia.rs     โ€” NVML-based NVIDIA metrics (feature-gated)
โ””โ”€โ”€ ui/
    โ”œโ”€โ”€ mod.rs            โ€” draw() entry point
    โ”œโ”€โ”€ helpers.rs        โ€” format_bytes, usage_color, scroll_indicator
    โ”œโ”€โ”€ statusbar.rs      โ€” persistent keybinding bar
    โ”œโ”€โ”€ tab_bar.rs        โ€” tab header row
    โ”œโ”€โ”€ widgets/
    โ”‚   โ””โ”€โ”€ split_gauge.rs โ€” SplitGauge custom widget
    โ””โ”€โ”€ tabs/
        โ”œโ”€โ”€ overview.rs   โ€” combined overview tab
        โ”œโ”€โ”€ cpu.rs
        โ”œโ”€โ”€ memory.rs
        โ”œโ”€โ”€ network.rs
        โ”œโ”€โ”€ disks.rs
        โ”œโ”€โ”€ processes.rs
        โ””โ”€โ”€ gpu.rs
```

Data flows in one direction:

```
app.on_tick()  โ†’  App (shared state)  โ†’  ui::draw()  โ†’  ratatui frame
```

There is no async runtime; `crossterm::event::poll` provides the non-blocking event check.

---

## ๐Ÿงช Testing

The test suite lives in `src/tests/` and is compiled only in test builds (`#[cfg(test)]`). It covers the full public API: metric structs, refresh functions, UI helpers, and the `SplitGauge` widget.

```bash
cargo test
```

### Test coverage overview

| Module | What is tested |
|---|---|
| `tests::push_history` | Ring-buffer eviction, growth to capacity, length invariant |
| `tests::helpers` | `format_bytes` SI boundaries, `usage_color`/`_f64` thresholds, `scroll_indicator` states |
| `tests::cpu` | `CpuState::new` zeroed state & history dimensions; `refresh` valid range & history cap |
| `tests::memory` | `MemState::new` zeroed state; `refresh` `used โ‰ค total` + history cap |
| `tests::network` | `NetState::new` zeroed state; `refresh` history cap & rate consistency |
| `tests::disks` | `DiskState` field storage; `refresh` non-empty result, `used โ‰ค total`, non-empty names/mounts |
| `tests::processes` | `ProcessEntry` field storage; `refresh` โ‰ค 100 entries, CPU-descending sort, non-empty names |
| `tests::gpu` | `GpuEntry::new` zeroed fields, `mem_is_gtt` initial value, history lengths; `amd::refresh` smoke test & invariants | Linux only |
| `tests::split_gauge` | Ratio clamping, full/empty/half fill, label centring, block inner area, zero-size no-panic |
| `tests::i18n` | `primary_code` subtag parsing, `is_bundled` case-insensitive lookup, `get_translations` for all 4 locales + region qualifiers + unknown fallback, all fields non-empty, parse no-panic, `detect_lang_code` smoke test |

### Running with NVIDIA feature

```bash
cargo test --features nvidia
```

---

## ๐Ÿ“ฆ Dependencies

| Crate | Purpose |
|-------|---------|
| `ratatui` | TUI layout and widget rendering |
| `crossterm` | Cross-platform terminal control, raw mode, event stream |
| `sysinfo` | CPU, RAM, swap, network, disk, process data |
| `anyhow` | Ergonomic error handling |
| `nvml-wrapper` *(optional)* | NVIDIA GPU metrics via NVML |

---

## ๐Ÿ—บ๏ธ Roadmap

Items are loosely ordered by priority.

### ๐Ÿ”œ Near-term

- ๐Ÿ”ต **Intel GPU support** โ€” utilisation via GT frequency ratio (`i915`/`xe` sysfs), LMEM for Intel Arc cards, temperature via hwmon; shown with appropriate caveats for iGPUs
- ~~๐Ÿท๏ธ **AMD APU label fix** โ€” distinguish GTT (shared) from dedicated VRAM and label accordingly~~ โœ…
- ~~โฑ๏ธ **Configurable refresh rate** โ€” CLI flag `--interval <ms>` to tune between low-latency and low-CPU usage~~ โœ…
- ๐ŸŽจ **Colour themes** โ€” built-in dark/light/high-contrast theme switcher

### ๐Ÿ”ง Medium-term

- ๐Ÿ”ฌ **Per-process GPU attribution** โ€” show which processes hold GPU memory (via NVML or `fdinfo` on the DRM driver)
- ๐ŸŒก๏ธ **Temperature history charts** โ€” per-core CPU and GPU temperature sparklines, not just current values
- ๐Ÿ’จ **Fan speed** โ€” hwmon fan RPM display in the GPU card and a new thermal overview section
- ๐ŸŒ **Network per-interface breakdown** โ€” drill-down view listing each interface (eth0, wlan0, loโ€ฆ) separately with its own sparkline
- ๐Ÿ’ฝ **Disk I/O throughput** โ€” read/write MB/s per device, not just partition usage percentages
- ๐Ÿ”‹ **Battery / power panel** โ€” laptop-focused: charge level, rate of charge/discharge, estimated time remaining

### ๐Ÿš€ Long-term / differentiators

- ๐Ÿ“‹ **Log tail panel** โ€” a dedicated tab that tails systemd journal or a user-specified log file in real time, with regex highlight rules; something `htop` and `gotop` completely lack
- ๐Ÿšจ **Alert rules** โ€” user-defined thresholds (e.g. CPU > 90% for > 5 s, VRAM > 80%) that flash the affected panel border red and optionally send a desktop or webhook notification
- ๐Ÿ”Œ **Plugin / script hooks** โ€” allow arbitrary shell scripts or Rust dynamic libraries to provide custom metric panels, making narsil extensible without a fork
- ๐Ÿ“ผ **Session recording & replay** โ€” record a metric session to a compact binary file and replay it later for post-mortem analysis
- ๐Ÿ–ฅ๏ธ **SSH-aware remote mode** โ€” connect to a remote host via SSH and display its metrics locally in the same TUI, without needing narsil installed on the remote
- ๐Ÿ–ฑ๏ธ **Mouse support** โ€” click tabs and scroll panels with the mouse alongside the existing keyboard navigation
- ๐Ÿ“Š **Export** โ€” one-shot `--json` / `--prometheus` output mode for integration with external dashboards (Grafana etc.)

---

## โš–๏ธ Comparison with existing tools

| Feature | `top` | `htop` | `gotop` | **narsil** |
|---------|-------|--------|---------|-----------|
| Language | C | C | Go | ๐Ÿฆ€ **Rust** |
| GPU metrics | โŒ | โŒ | partial | **โœ… AMD + NVIDIA (Linux)** |
| Braille charts | โŒ | โŒ | โœ… | **โœ…** |
| Per-char label inversion | โŒ | โŒ | โŒ | **โœ…** |
| Disk usage bars | โŒ | โŒ | โœ… | **โœ…** |
| Status bar with keybindings | โŒ | โŒ | โŒ | **โœ…** |
| Localised UI | โŒ | โŒ | โŒ | **โœ… EN DE FR ES** |
| Log tail panel | โŒ | โŒ | โŒ | ๐Ÿ—“๏ธ planned |
| Alert rules | โŒ | โŒ | โŒ | ๐Ÿ—“๏ธ planned |
| Remote mode | โŒ | โŒ | โŒ | ๐Ÿ—“๏ธ planned |
| Session replay | โŒ | โŒ | โŒ | ๐Ÿ—“๏ธ planned |

---

## ๐Ÿ“„ License

GPL-3.0 โ€” see [LICENSE](LICENSE).