Commit Graph
48 Commits
Author SHA1 Message Date
michel aa8f4182d3 feat(tui): hierarchical browse tree with file/folder downloads
Wire browsing a user's shared files into the TUI as a modal tree popup:
- press 'b' on a highlighted search result to browse that result's owner, or
  'b' elsewhere to type a username (new CommandBarMode::Browse);
- a new models/browse.rs synthesizes the flat SharedDirectory list into a
  nested, collapsible tree (intermediate folders reconstructed from the
  backslash paths; file download paths kept byte-exact from the original names),
  with expand/collapse state and a flatten-to-rows renderer — fully unit-tested;
- the popup polls take_browse_result each tick (with a timeout), navigates with
  arrows/hjkl, expands/collapses folders, and queues downloads: Enter on a file,
  'd' on a file or an entire folder subtree (recursively) — all funneled into
  the existing Downloads pane;
- shortcuts bar advertises browse keys and surfaces our own sharing status.
2026-07-19 19:27:05 +02:00
michel 7d8a435d25 feat(cli): add 'browse <username>' to list a user's shared files
soulseek-rs browse <username> connects, logs in, requests the peer's file
list and prints the directories and files it shares.
2026-07-19 18:57:14 +02:00
michel 05b06ec2ec feat(tui): compose and read private messages
Add private messaging to the TUI:
- 'm' opens the command bar in message mode; type '<recipient> <text>' and
  Enter sends it via the client;
- incoming messages are polled each tick into an inbox;
- 'i' toggles a centered inbox popup listing sent/received messages.

The shared command bar now carries a mode (search vs message) so the prefix,
submit action and shortcut hints follow what is being typed.
2026-07-19 13:38:36 +02:00
michel f6127411d7 feat(cli): add 'message' subcommand to send private messages
`soulseek-rs message <username> <text>` connects, logs in, sends a private
message and exits. Smoke-tested against a local soulfind server.
2026-07-19 13:32:53 +02:00
michel a127dd97bf ci: run soulfind e2e tests on GitHub Actions
Extend the existing CI workflow with an ubuntu-only "End-to-end (soulfind)"
job that builds soulfind from source (LDC + `dub build :server`), points
SOULFIND_BIN at it, and runs the e2e suite with SOULSEEK_E2E_REQUIRED=1.
Also widen the clippy job to the whole workspace.

The e2e harness gains SOULSEEK_E2E_REQUIRED: when set, a missing/unstartable
server is a hard failure instead of a silent skip, so CI genuinely exercises
the client end-to-end. Add e2e coverage for two existing behaviours a single
server can verify: wrong-password rejection and concurrent logins.
2026-07-19 13:19:46 +02:00
michel 1328736550 feat: configure download & upload (shared) directories
Add a --shared-dir / SOULSEEK_SHARED_DIR option and a SOULSEEK_DOWNLOAD_DIR
env for the download directory. The shared directory is resolved and
validated up front (tilde-expanded, must exist and be a directory); a
misconfigured value is a warning, not a fatal error.

The library gains an optional shared_directory on ClientSettings/Client
with a getter, keeping soulseek-rs-lib dependency-free. Resolution lives
in a new soulseek-rs/src/directories.rs with unit tests.
2026-07-19 13:12:44 +02:00
michel e72c8a5c07 style: crank clippy to pedantic+nursery and fix all lints
Enable the full clippy all + pedantic + nursery groups as workspace warnings,
with a small documented allow-list for the pedantic/nursery lints this project
intentionally does not follow (rustdoc-style requirements, deliberate numeric
casts in the protocol/UI layers, and a few noisy nursery lints). Also enable
the rust unused_lifetimes and unsafe_op_in_unsafe_fn lints.

Fixed every resulting warning (527 -> 0) mechanically per clippy's suggestions:
inline format args, const fns, item ordering, let-else, redundant clones,
map_or, cast_lossless, Eq derives, associated-fn conversions for methods that
never used self, and removing unnecessary Result wrappers whose call sites could
never propagate an error. No runtime behavior changed; cargo fmt applied.

cargo clippy --workspace --all-targets: 0 warnings. All tests pass.
2026-07-19 00:09:17 +02:00
michel b149a8ecf2 fix: always restore the terminal when the TUI loop errors
MainTui::run only disabled mouse capture and called ratatui::restore() after
the event loop exited normally; any error propagated by ? from draw/poll/read
returned early and left the terminal in raw mode, on the alternate screen, with
mouse capture still enabled. The loop body moved into a helper whose result is
returned only after the terminal is unconditionally restored.
2026-07-18 14:37:35 +02:00
michel bdaeb33a4f fix: TUI selection underflow and filtered-results desync
Three related TUI correctness bugs:

- download_selector navigation (select_previous/next/last) computed
  items.len() - 1, panicking with an arithmetic underflow when the list was
  empty (e.g. a filter matched nothing). Index math is now in wrap-safe
  helpers that return no selection for an empty list.

- update_search_results overwrote the filtered results view with the full
  unfiltered list on every ~100ms poll, so an active filter silently stopped
  working as new results streamed in. It now re-derives the filtered view
  from the current query.

- The results pane rendered selection checkboxes by testing the display-row
  index against a selection set that stores unfiltered indices, so under an
  active filter the wrong rows showed as checked. It now maps the display
  index back through the filtered-index table.

Filtering and index-mapping logic extracted into pure, unit-tested helpers.
2026-07-18 14:30:02 +02:00
michel daac398f68 chore: update Rust toolchain and dependencies to latest
- clap 4.5.51 -> 4.6.2
- ratatui 0.29.0 -> 0.30.2
- cargo update for transitive deps
- toolchains updated: stable 1.97.1, nightly 1.99.0
2026-07-18 12:24:03 +02:00
michel d06aa2d3de style: apply cargo fmt 2026-04-26 17:28:43 +02:00
michel 9d6a33da34 refactor: centralize BYTES_PER_MB and tidy peer IP parsing
- Add pub const BYTES_PER_MB to ui/utils.rs and replace 7 uses of the
  bare 1_048_576.0 literal across the UI modules. Also pick up
  uninlined-format-args cleanup in the same file while we're here.
- Replace Vec<i32> IP-byte accumulator in Peer::new_from_message with a
  [u8; 4]; the .abs() calls on u8.into() were no-ops on always-positive
  values. Network byte order ([3].[2].[1].[0]) preserved; tests cover
  the wire format.
2026-04-26 17:20:45 +02:00
michel e0218b2433 fix: use u16 for listen port end-to-end
Port numbers are 16-bit; the codebase had listen_port/listener_port
typed as u32, inconsistent with PeerAddress.port (already u16).
Migrate the type all the way through the CLI, config, ClientSettings,
Client, ServerActor, and the listener entry point. The wire format is
preserved: build_set_wait_port_message now takes u16 and widens to u32
via .into() before write_int32. Also rename DEFALT_LISTEN_PORT to
DEFAULT_LISTEN_PORT.
2026-04-26 17:20:45 +02:00
michel 574833bf61 feat: add download info pane to TUI
Splits the downloads area 60/40 with a new info pane that adapts to the
selected download's status: header (filename, user, size, status, save
path, bitrate, length) plus queued-specific fields (queue position, peer
slots, peer upload speed) or live progress/speed/ETA when transferring.

Plumbs queue position via a new ClientOperation::PlaceInQueueUpdate so
PlaceInQueueResponse messages from peers reach DownloadStore, and threads
search-time metadata (bitrate, length, peer slots/speed) onto Download
through a new download_with_metadata entry point.
2026-04-26 17:19:26 +02:00
michel 2722552e35 refactor: replace lock unwraps with poison-aware helpers
Add SoulseekRs::LockPoisoned variant and a small RwLockExt/MutexExt
helper module exposing read_safe/write_safe/lock_safe that map
PoisonError to that variant. Walk every non-test lock acquisition
across client, peer/server actors, peer registry, listener and the
thread pool: propagate via ? where the function returns Result, log
and degrade to a sensible default (return early, false, None, empty)
where it does not. Client::connect now returns Result so the CLI can
surface the failure instead of panicking.
2026-04-26 12:32:46 +02:00
michel e1ee3ac493 fix clippy collapsible match warnings 2026-04-26 12:18:00 +02:00
Michel de Graaf 6a63703f82 Merge pull request #2 from adalessandro/wip/adalessandro/dotenv
soulseek-rs: main: Add support for dotenv environment
2026-04-26 11:58:29 +02:00
Michel de Graaf dfdd60f1c8 Merge pull request #1 from adalessandro/dev
soulseek-rs: main_tui: Restore terminal status before exiting
2026-04-26 11:57:54 +02:00
michel bff7ca4bd4 chore: change copyright date 2026-04-26 11:55:40 +02:00
michel de58c8cbe2 Add TUI download pause and queued delete 2026-04-26 11:50:37 +02:00
michel 807dea5f17 Fix TUI search cursor editing 2026-04-26 11:43:44 +02:00
Ariel D'Alessandro 3696e95760 soulseek-rs: main: Add support for dotenv environment
Signed-off-by: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
2026-01-29 19:36:15 -03:00
Ariel D'Alessandro 9627fa475d soulseek-rs: main_tui: Restore terminal status before exiting
Signed-off-by: Ariel D'Alessandro <ariel.dalessandro@collabora.com>
2026-01-29 17:16:19 -03:00
Michel de Graaf dfe6f532fa chore: formatting 2026-01-04 20:37:03 +01:00
mayor 16c8a4bde9 chore: update to Rust 2024 edition 2026-01-04 20:08:16 +01:00
Michel de Graaf f5b65053e2 fix: hanging TUI on queueing downloads 2025-11-19 09:13:03 +01:00
Michel de Graaf 9f174c4c5e fix: linting 2025-11-19 08:40:41 +01:00
Michel de Graaf bb86e50b4f fix: queuing downloads 2025-11-19 08:27:50 +01:00
Michel de Graaf b7a5037edb chore: keep updating search results 2025-11-18 16:02:19 +01:00
Michel de Graaf 2ba48663e4 feat: logging to log file 2025-11-18 15:49:53 +01:00
Michel de Graaf 4c6d7c6850 chore: refactor UI to use Downloads struct 2025-11-18 14:33:33 +01:00
Michel de Graaf 467de9cb91 chore: bump version 2025-11-13 14:13:28 +01:00
Michel de Graaf 9a4ad6c38a feat: main TUI 2025-11-13 14:12:37 +01:00
Michel de Graaf cafa40a09d chore: refactor to support multi search 2025-11-13 10:28:16 +01:00
Michel de Graaf 4a34583f9c chore: improve download selector 2025-11-13 10:08:16 +01:00
Michel de Graaf fff407c30d chore: bump version 2025-11-12 15:46:50 +01:00
Michel de Graaf 69a044b1fa chore: UI tweaks 2025-11-12 14:52:31 +01:00
Michel de Graaf 406def31d9 chore: rename label from files to download 2025-11-12 11:56:32 +01:00
Michel de Graaf e87853c66a chore: bump version 2025-11-12 02:03:17 +01:00
Michel de Graaf 5816d9b632 chore: refactor CLI app with new modules for seperation of concerns, reusable components and consistent styling 2025-11-12 02:02:35 +01:00
Michel de Graaf 5a28452c02 feat: clear filter in multi-select download screen function 2025-11-12 01:52:27 +01:00
Michel de Graaf 689e667ade chore: bump version 2025-11-12 01:41:43 +01:00
Michel de Graaf 3f506389f0 feat: improved search screen 2025-11-12 01:37:08 +01:00
Michel de Graaf f84d4ea41e chore: rename the cli binary to soulseek-rs 2025-11-12 01:36:51 +01:00
Michel de Graaf 30c4fd2aad feat: better search TUI, multi select downloads and parallel downloads 2025-11-12 00:26:03 +01:00
Michel de Graaf 98a8f53094 feat: improved cli 2025-11-11 23:38:59 +01:00
Michel de Graaf 3d6cc9f7cd feat: client settings and defaults 2025-11-11 22:22:21 +01:00
Michel de Graaf 7bf2748e1f feat: seperate into lib and cli packages 2025-11-11 21:55:53 +01:00