gosh-dl-cli 0.3.2

Command-line interface for the gosh-dl download engine
# gosh-dl-cli Improvement Plan

*Generated by 3-agent research team: bug auditor, engine researcher, TUI/UX designer.*
*Final version targets alignment with gosh-dl engine v0.2.5.*

---

## Phase 1: Critical Stability Fixes (v0.2.3)

### 1.1 Install TUI panic hook
- **Severity**: CRITICAL
- **File**: `tui/app.rs:130-173`
- **Problem**: If anything panics during the TUI render loop, `restore_terminal()` is never called. The terminal is left in raw mode with alternate screen active — completely bricked.
- **Fix**: Add `std::panic::set_hook` before `setup_terminal()` that calls `disable_raw_mode()` + `LeaveAlternateScreen`. ~10 lines. Use a guard struct with `Drop` impl as an alternative.
- **Must be a standalone commit. Unblocks all subsequent TUI work.**

### 1.2 Fix UTF-8 cursor panic in AddUrl dialog
- **Severity**: CRITICAL
- **File**: `tui/app.rs:191-209`
- **Problem**: Cursor is a `usize` byte index. `input.insert(*cursor, c)` and `input.remove(*cursor)` panic on non-char-boundary positions. Right arrow increments by 1 byte, which can land mid-codepoint on multi-byte UTF-8 input.
- **Fix**: Track cursor as character index, convert to byte offset via `char_indices()` for string operations.

### 1.3 Fix broadcast Lagged handling in direct mode
- **Severity**: MEDIUM
- **File**: `direct.rs:155-206` (line 201)
- **Problem**: `Err(_) => break` treats `RecvError::Lagged` as channel-closed. Downloads appear incomplete even when finished.
- **Fix**: Match `Lagged(n)` separately → `debug!("Missed {} events", n); continue;`

### 1.4 Fix `config set` ignoring --config path
- **Severity**: HIGH
- **File**: `commands/config.rs:74-148`
- **Problem**: `set_config_value()` always loads from and saves to the default config path via `CliConfig::load(None)` / `config.save(None)`, ignoring `--config /custom/path`.
- **Fix**: Pass the config path through to `set_config_value()` and use it for both load and save.

### 1.5 Warn on invalid headers (missing colon)
- **Severity**: HIGH
- **Files**: `direct.rs:252`, `commands/add.rs:156`
- **Problem**: `--header "X-Token abc"` (missing colon) is silently dropped. User thinks auth is sent when it's not.
- **Fix**: Add `else { bail!("Invalid header format '{}'. Expected 'Name: Value'", header); }`

### 1.6 Deduplicate parse_speed and parse_checksum
- **Severity**: HIGH (maintenance/divergence risk)
- **Files**: `commands/add.rs:205-227` duplicates `util.rs:94-106` and `direct.rs:301-309`
- **Fix**: Remove duplicates in `commands/add.rs`, import from `util.rs`. Move `parse_checksum` to `util.rs`.

### 1.7 Config value validation
- **Severity**: MEDIUM
- **File**: `commands/config.rs:74-148`, `config.rs`
- **Problems**:
  - `max_concurrent_downloads` can be set to 0 (breaks engine)
  - `refresh_rate_ms` can be set to 0 (busy-loop in TUI)
  - `seed_ratio` can be negative
  - `start_hour`/`end_hour` are `u8` (0-255, no 0-23 validation)
  - Invalid schedule day names silently dropped (`config.rs:276-288`)
- **Fix**: Add validation in `to_engine_config()`, `config set`, and log warnings for unrecognized day names.

### 1.8 Minor fixes batch
- Remove dead `_gid` slice in `tui/ui.rs:150` — panics if GID < 8 chars
- Replace `unreachable!()` with `Ok(())` in `main.rs:165`
- Fix help dialog "any key to close" — accept any key, or update text to list actual keys
- Fix `truncate_str` for `max_len < 3` returning "..." (longer than max_len)
- Fix `format_duration` returning "--" for exactly 0 seconds
- Use `last_visible_height` for page up/down instead of hardcoded 10
- Fix URL auto-detection false positives (`file.txt``https://file.txt`)

---

## Phase 2: TUI Visual Overhaul (v0.2.4)
*No new dependencies for 2.1-2.7 — all built into ratatui 0.30*

### 2.1 Rewrite theme.rs — Catppuccin Mocha palette
- **What**: Replace 13-field role-based Theme struct with ~25-field palette-based design.
- **Palettes**: Catppuccin Mocha (dark default), Latte (light), Macchiato (alt dark).
- **Palette structure**:
  - Background layers: `bg` (Base #1e1e2e), `bg_dim` (Mantle #181825), `bg_deep` (Crust #11111b)
  - Surfaces: `surface[0..2]` (#313244, #45475a, #585b70)
  - Text hierarchy: `text` (#cdd6f4), `subtext[0..1]`, `overlay[0..2]`
  - Semantic: `accent` (Blue #89b4fa), `success` (Green #a6e3a1), `error` (Red #f38ba8), `warning` (Yellow #f9e2af), `info` (Sapphire #74c7ec)
  - Extended: pink, mauve, peach, teal, sky, lavender, flamingo, rosewater
- **Also fix**: Pass `&Theme` to render functions instead of re-fetching via `app.theme()`. Remove dead `_theme` on `ui.rs:15`.

### 2.2 Rounded borders everywhere
- Switch all `Block::bordered()` to `BorderType::Rounded` (╭╮╰╯ corners).
- Use `BorderType::Double` (═║╔╗╚╝) for dialog overlays.

### 2.3 Multi-line download items with Gauge progress bars
- Replace single-line format strings with 2-3 line items:
  - Line 1: Name + state icon + status
  - Line 2: `LineGauge` progress bar + speed + ETA
- Progress bar color changes by percentage:
  - 0-30%: Red (#f38ba8)
  - 30-70%: Peach (#fab387)
  - 70-99%: Blue (#89b4fa)
  - 100%: Green (#a6e3a1)
- **This is the single biggest layout improvement.**

### 2.4 Tabs widget for view modes
- Replace "All / Active / Completed" text header with ratatui `Tabs` widget with active highlight.

### 2.5 Scrollbar widget on download list
- Add ratatui's built-in `Scrollbar` to the right edge.

### 2.6 Split details panel with Sparkline widgets
- Left side: metadata (size, speed, peers, ETA) as styled `Paragraph` with `Line`/`Span`.
- Right side: ratatui `Sparkline` widgets for download/upload speed history.

### 2.7 Status icons and typography
- State icons: `` downloading, `` seeding, `` paused, `` queued, `` connecting, `` completed (green), `` error (red)
- Use `` and `` separators instead of `|` and `-`
- Colored speed indicators: download in Teal (#94e2d5), upload in Peach (#fab387)

### 2.8 Clean up dead widget code
- Delete `download_list.rs` and `help_dialog.rs` entirely (duplicated logic, never used).
- Keep `progress_bar.rs` and `speed_graph.rs` shells, rewrite for new design.
- Move `sparkline_string()` to `format.rs` as fallback.

### 2.9 Redesign AddUrl dialog (combined with Phase 1 cursor fix)
- Distinct input field with Surface 0 background and `BorderType::Rounded`.
- Blinking cursor via `frame.set_cursor_position()`.
- Button-style `[Yes]`/`[No]` indicators.
- Shadow effect: 1-cell offset dark rectangle behind dialog.
- Dimmed background behind open dialogs.

---

## Phase 3: TUI Animations & Polish (v0.2.5-rc)
*New dependencies: `tachyonfx` ~0.11, `throbber-widgets-tui` ~0.8*

### 3.1 tachyonfx effects
- `fade_from()` on TUI startup (fade from black, ~500ms)
- `slide_in()` / `slide_out()` for dialog open/close
- Dimmed background behind modals via `paint_bg` + `fade_to()`

### 3.2 Animated spinners
- `throbber-widgets-tui` for connecting/downloading state indicators.
- Replaces static ``/`` icons with animated spinners for active states.

### 3.3 Toast notifications
- Auto-dismissing notification in top-right corner when downloads complete.
- Fade-out via tachyonfx after ~3 seconds.

### 3.4 Connection quality indicators
- Peer-count bars: `▰▰▰▰▰` (>50), `▰▰▰▰▱` (20-50), `▰▰▰▱▱` (5-20), `▰▰▱▱▱` (1-5), `▰▱▱▱▱` (0/HTTP)

### 3.5 Empty state enhancement
- Centered, styled message when no downloads exist:
  ```
       No downloads yet

     Press  a  to add a download
     or drag a .torrent file here
  ```

---

## Phase 4: Engine Upgrade & Release (v0.2.5)
*Version-aligned with gosh-dl engine 0.2.5*

### 4.1 Bump gosh-dl 0.2.2 → 0.2.5
- **Change**: `Cargo.toml` line 23: `gosh-dl = "0.2.5"`
- **Zero breaking API changes.** Pure drop-in replacement.
- **Free bug fixes from engine**:
  - Large file downloads (>400MB) no longer killed by 60s total timeout (`.timeout()``.read_timeout()`)
  - Segment-level retry with exponential backoff (one segment failing no longer kills entire download)
  - Torrent completion event now correctly fires (`DownloadEvent::Completed`)
  - Pause/resume works without database configured (in-memory segment cache fallback)
  - SQLite `busy_timeout(5s)` prevents `SQLITE_BUSY` with concurrent readers
  - WebSeed bounded channel prevents unbounded memory growth
  - Rate limiter values > u32::MAX clamp instead of wrapping to zero
  - Torrent `max_pending_requests` increased 16 → 64 (faster torrent speeds)
  - WebSeed connections included in peer count
  - Atomic `save_segments()` transaction (prevents corrupt resume data on crash)

### 4.2 Fix Resumed state transition
- **File**: `tui/app.rs:338-342`
- **Problem**: Hard-codes `DownloadState::Downloading` on resume. Engine actually sends `Connecting` first.
- **Fix**: Remove the manual state override; let `StateChanged` events handle it.

### 4.3 Add Resync on Lagged in TUI event handler
- **File**: `tui/event.rs:60-63`
- **Problem**: `Lagged` converts to `Tick` which only calls `update_stats()`, not `refresh_downloads()`. Missed `Completed` events don't show until user interacts.
- **Fix**: Add `AppEvent::Resync` variant triggering `refresh_downloads()`, or make `Tick` periodically do a full refresh.

### 4.4 Final release: v0.2.5
- Bump `gosh-dl-cli` version to 0.2.5 in `Cargo.toml`
- Update CHANGELOG.md
- Tag and release

---

## Dependency Changes Summary

| Change | Phase | Risk |
|--------|-------|------|
| `gosh-dl` 0.2.2 → 0.2.5 | Phase 4 | None (drop-in, no API changes) |
| `tachyonfx` ~0.11 (new) | Phase 3 | Low (ratatui org crate) |
| `throbber-widgets-tui` ~0.8 (new) | Phase 3 | Low (established crate) |

## Version Roadmap

| Version | Phase | Theme | New Deps |
|---------|-------|-------|----------|
| **v0.2.3** | Phase 1 | Critical fixes & bug batch | None |
| **v0.2.4** | Phase 2 | TUI visual overhaul | None |
| **v0.2.5-rc** | Phase 3 | Animations & polish | tachyonfx, throbber-widgets-tui |
| **v0.2.5** | Phase 4 | Engine upgrade & release | gosh-dl 0.2.5 |

## Issue Tally

| Severity | Count | Resolved In |
|----------|-------|-------------|
| CRITICAL | 2 | Phase 1 |
| HIGH | 5 | Phase 1 |
| MEDIUM | 8 | Phases 1, 2, 4 |
| LOW | 11 | Phases 1, 2 |
| Engine bugs (free fixes) | 9 | Phase 4 |
| TUI improvements | 10 | Phases 2, 3 |

**Total: 26 bugs fixed + 9 engine fixes + 10 TUI improvements = 45 items across 4 phases.**