From da8ab4530320388cfef978a034d60645e332a6bd Mon Sep 17 00:00:00 2001 From: Michel de Graaf Date: Mon, 7 Sep 2026 07:09:50 +0000 Subject: [PATCH] feat(tui): results across the top, with panes you can hide, zoom and Tab through The results pane now has the whole width, where a long file name needs it, and Searches, Downloads and Info share the row underneath. Tab and Shift-Tab walk the panes, z zooms the focused one to the window, w hides it so the others widen, and its number brings it back. Every list takes the same navigation keys, the shortcuts bar wraps instead of cutting keys off, and ? lists every key by pane, scrolling on a short terminal. Follows up on #55. --- README.md | 9 + fixtures/demo.svg | 2 +- soulseek-rs/src/models/app_state.rs | 196 ++++++++ soulseek-rs/src/models/mod.rs | 2 +- soulseek-rs/src/ui/main_tui/input.rs | 210 ++++++--- soulseek-rs/src/ui/main_tui/mod.rs | 311 +++++++++++- soulseek-rs/src/ui/main_tui/render.rs | 468 +++++++++++++++---- soulseek-rs/src/ui/main_tui/search.rs | 4 +- soulseek-rs/src/ui/panes/downloads_pane.rs | 11 +- soulseek-rs/src/ui/panes/searches_pane.rs | 10 +- soulseek-rs/src/ui/styles.rs | 101 +++- website/src/components/tui/data.ts | 7 +- website/src/components/tui/hero-terminal.tsx | 67 ++- website/src/components/tui/panes.tsx | 2 +- website/src/pages/docs.tsx | 41 +- 15 files changed, 1209 insertions(+), 232 deletions(-) diff --git a/README.md b/README.md index 0621669..df879aa 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,15 @@ soulseek-rs Everything else is a one-shot command that runs headless. +### The TUI + +Results run across the top, with the whole width for long file names; +Searches, Downloads and Info share the row underneath. `Tab`/`Shift-Tab` or +`1`–`3` move the focus between panes, `z` zooms the focused pane to the whole +window, and `w` hides it so the others widen (its number brings it back). +Every list takes `↑`/`↓`, `Home`/`End`, `PgUp`/`PgDn` and the vim keys, and +`?` shows every key for where you are. + ### Daemon mode A one-shot command logs in, does its job, and exits. Run a daemon instead and diff --git a/fixtures/demo.svg b/fixtures/demo.svg index 00c714c..0f07359 100644 --- a/fixtures/demo.svg +++ b/fixtures/demo.svg @@ -1 +1 @@ -╭soulseek-rslogin───────────────────────────────╮│Username:jackass44││Password:•••••••••││││Connecting…││Newusernamesareregisteredautomatically.││Tab:switch·Enter:login·Esc:quit│╰──────────────────────────────────────────────────╯╭Status────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│soulseek-rs🦀v7.0.0Downloads:0active,0completed,0fail│╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╭[1]Searches───────────────────────╮╭[2]Results────────────────────────────────────────────────────────────────────────────╮│StatusQueryResults││soulseek-rsv7.0.0││›Done tiesto 0││││Donethexx0││Noresultsyet.││Donedjbount0││Pickasearchin[1]Searches,orstartanewone.│││││││╰─────────────────────────────────────────────────────────────────────────────────────────╯││╭[3]Downloads/Uploads──────────────────────────────╮╭[Info]──────────────────────────╮│││StatUserProgressSpeed││RavingGeorgefeat.Oscar&The││││›⋯ Dancemaniako Queued -││Wolf-TwoClocks(MaceoPlex││││⋯DancemaniakoQueued-││Remix).mp3││││⋯DancemaniakoQueued-││@@kpmeh/-2013-/Electro,Deep,││││││TechnoHouse&TranceCollection││││││byLORDEGRAFvol.12││││││││││││UserDancemaniako││││││Size9.4MB││││││StatusQueued│╰─────────────────────────────────────╯╰─────────────────────────────────────────────────────╯╰──────────────────────────────────╯╭Shortcuts·Sharing:2folders────────────────────────────────────────────────────────────────────────────────────────────────╮│[s→search][m→message][i→inbox][c→chat][b→browseuser][1-3→focuspane][↑↓→navigate][Enter→results⟨✉1⟩││[s→search][m→message][i→inbox][c→chat][b→browseuser][1-3→focuspane][↑↓→navigate][Enter→results⟨✉1⟩│╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│search:│╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯│[Type→searchterm][←→→movecursor][Backspace/Del→edit][Enter→search][Esc→cancel]⟨✉1⟩││[Type→searchterm][←→→movecursor][Backspace/Del→edit][Enter→search][Esc→cancel]⟨✉1⟩│╭[1]Searches───────────────────────╮╭[2]Results:SaveYourAtoll───────────────────────────────────────────────────────────╮│StatusQueryResults││soulseek-rsv7.0.0││Donetiesto0││││Donethexx0││Noresultsyet.││Donedjbount0││Pickasearchin[1]Searches,orstartanewone.││›Active Save Yo Searching.│││││││││╰─────────────────────────────────────────────────────────────────────────────────────────╯││╭[3]Downloads/Uploads──────────────────────────────╮╭[Info]──────────────────────────╮│││StatUserProgressSpeed││RavingGeorgefeat.Oscar&The││││›⋯ Dancemaniako Queued -││Wolf-TwoClocks(MaceoPlex││││⋯DancemaniakoQueued-││Remix).mp3││││⋯DancemaniakoQueued-││@@kpmeh/-2013-/Electro,Deep,││││││TechnoHouse&TranceCollection││││││byLORDEGRAFvol.12││││││││││││UserDancemaniako││││││Size9.4MB││││││StatusQueued│╰─────────────────────────────────────╯╰─────────────────────────────────────────────────────╯╰──────────────────────────────────╯│[Space→select][Enter→download][b→browseowner][c→chat][/→filter][a/A→selectall/none][1-3→focuspan⟨✉1⟩││[Space→select][Enter→download][b→browseowner][c→chat][/→filter][a/A→selectall/none][1-3→focuspan⟨✉1⟩││StatusQueryResults││✓FilenameSizeUserBitrateSpeedSlots││Donetiesto0││›[ ] @@kpmeh\- 2021 -\The P 16.5 MB Dancemaniako 320 kbps 0.46 MB/s 1││Donethexx0││[]@@vqmcr\20241128\Save56.0MBQadavre-1.69MB/s1││Donedjbount0││[]@@vqmcr\20241128\Save70.0MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save80.4MBQadavre-1.69MB/s1││›Active Save Yo 219││[]@@vqmcr\20241128\Save60.7MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save64.3MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save64.6MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save57.9MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save67.2MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save68.8MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save59.1MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save68.0MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\Save59.7MBQadavre-1.69MB/s1│╭[1]Searches───────────────────────╮╭[2]Results:MaceoPlex────────────────────────────────────────────────────────────────╮│ActiveSaveYo219││││›Active Maceo P Searching.││││││││StatusInprogress││Donetiesto0││›[ ] Music Sorted\#Beatport 17.8 MB beatrice.songbi 320 kbps 1.87 MB/s 1││Donethexx0││[]Music\#DJPools\2026\218.1MBbeatrice.songbi320kbps1.87MB/s1││Donedjbount0││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1│╭[1]Searches───────────────────────╮╭[2]Results:MaceoPlex────────────────────────────────────────────────────────────────╮│StatusQueryResults││✓FilenameSizeUserBitrateSpeedSlots││Donetiesto0││›[ ] Music Sorted\#Beatport 17.8 MB beatrice.songbi 320 kbps 1.87 MB/s 1││Donethexx0││[]Music\#DJPools\2026\218.1MBbeatrice.songbi320kbps1.87MB/s1││Donedjbount0││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││││[]Music\0dayMP3\2026\20207.7MBbeatrice.songbi320kbps1.87MB/s1││││[]@@jlksj\new_music\Ali14.0MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu16.9MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu15.1MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu16.1MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu17.5MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Gabr23.6MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Gabr12.9MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Gabr18.0MBQadavre320kbps1.69MB/s1││││││StatusInprogress││›Active Save Yo 227││[]@@vqmcr\20241128\Save60.7MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\VA-29.6MBQadavre-1.69MB/s1││││[]@@vqmcr\20241128\VA-27.0MBQadavre-1.69MB/s1││ActiveMaceoP589││[]@@vqmcr\20241128\Save80.4MBQadavre-1.69MB/s1││Donetiesto0││[]@@lustt\complete\ashur14.5MBvivio320kbps1.16MB/s1││Donethexx0││[]@@lustt\complete\cyber15.7MBvivio320kbps1.16MB/s1││Donedjbount0││[]@@lustt\complete\cyber14.7MBvivio320kbps1.16MB/s1││›Active Save Yo 227││[]@@lustt\complete\cyber15.7MBvivio320kbps1.16MB/s1││ActiveMaceoP589││[]@@lustt\complete\cyber14.4MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\cyber16.3MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala13.4MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala13.3MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala18.3MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala15.8MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala15.7MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala14.7MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala13.0MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\panin16.7MBvivio320kbps1.16MB/s1││Donetiesto0││[]@@qxibn\music2024\Sav80.4MBsillysillysilly-0.64MB/s0││Donethexx0││[]@@qxibn\music2024\Sav64.3MBsillysillysilly-0.64MB/s0││Donedjbount0││[]@@qxibn\music2024\Sav64.6MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav68.8MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav59.1MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav68.0MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav59.7MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav1.9MBsillysillysilly-0.64MB/s0││││[]@@wztnu\MUZYKA\FLAC\TE26.9MBDozel-0.02MB/s1││││[]@@puclh\Marzo2024\05-60.8MBneska1411kbps1.77MB/s1││││[]Music\0dayFLAC\2026\20.4MBbeatrice.songbi-1.87MB/s1││›Done Save Yo 229││[]@@qxibn\music2024\Sav57.9MBsillysillysilly-0.64MB/s0││ActiveMaceoP612││[]@@qxibn\music2024\Sav67.2MBsillysillysilly-0.64MB/s0││││›[ ] @@wztnu\MUZYKA\FLAC\TE 26.9 MB Dozel - 0.02 MB/s 1││││[✓]@@qxibn\music2024\Sav59.7MBsillysillysilly-0.64MB/s0││││›[✓] @@wztnu\MUZYKA\FLAC\TE 26.9 MB Dozel - 0.02 MB/s 1││DoneMaceoP612││[]@@qxibn\music2024\Sav67.2MBsillysillysilly-0.64MB/s0││││⋯DozelQueued-││TechnoHouse&TranceCollection││││⋯QadavreQueued-││byLORDEGRAFvol.12││││⋯vivioQueued-││││││⋯sillysillysillyQueued-││UserDancemaniako││││↯Dozel0.0MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection│╭[1]Searches───────────────────────╮╭[2]Results:SaveYourAtoll───────────────────────────────────────────────────────────╮│Donetiesto0││[]@@qxibn\music2024\Sav80.4MBsillysillysilly-0.64MB/s0││Donethexx0││[]@@qxibn\music2024\Sav64.3MBsillysillysilly-0.64MB/s0││Donedjbount0││[]@@qxibn\music2024\Sav64.6MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav68.8MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav59.1MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav68.0MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav59.7MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav1.9MBsillysillysilly-0.64MB/s0││││[]@@wztnu\MUZYKA\FLAC\TE26.9MBDozel-0.02MB/s1││││›[ ] @@wztnu\MUZYKA\FLAC\TE 26.9 MB Dozel - 0.02 MB/s 1││││[]@@puclh\Marzo2024\05-60.8MBneska1411kbps1.77MB/s1││││[]Music\0dayFLAC\2026\20.4MBbeatrice.songbi-1.87MB/s1││││⋯sillysillysillyQueued-││UserDancemaniako││DoneSaveYo229││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││›Done Maceo P 612││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││││[]Music\0dayMP3\2026\20207.7MBbeatrice.songbi320kbps1.87MB/s1││││[]@@jlksj\new_music\Ali14.0MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu16.9MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu15.1MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu16.1MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Ellu17.5MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Gabr23.6MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Gabr12.9MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\Gabr18.0MBQadavre320kbps1.69MB/s1││Donetiesto0││[]MusicSorted\#Beatport17.8MBbeatrice.songbi320kbps1.87MB/s1││││↯Dozel0.2MB/26.9MB0.4MB/s││TechnoHouse&TranceCollection││││✓vivioCompleted-││││Donethexx0││[✓]Music\#DJPools\2026\218.1MBbeatrice.songbi320kbps1.87MB/s1││Donedjbount0││[✓]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││││›[ ] Music\0day MP3\2026\20 207.7 MB beatrice.songbi 320 kbps 1.87 MB/s 1││││›✓ Dancemaniako Completed -││Wolf-TwoClocks(MaceoPlex││││││StatusCompleted││││↯Dozel0.2MB/26.9MB0.2MB/s││TechnoHouse&TranceCollection││││↯Dancemaniako0.0MB/11.1MB0.0MB/s││Remix).mp3││││[✓]@@jlksj\new_music\Ellu17.5MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\july16.0MBQadavre320kbps1.69MB/s1││││↯Dozel0.3MB/26.9MB0.5MB/s││TechnoHouse&TranceCollection││Donetiesto0││[]@@kpmeh\-2013-\2013.10.4MBDancemaniako192kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2013-\2013.7.3MBDancemaniako192kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2013-\AllG40.0MBDancemaniako-0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2013-\Azari50.9MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2013-\Beatp18.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Beatp17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Butch57.5MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2013-\Deep15.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Defec19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Defec17.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Defec18.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Defec54.6MBDancemaniako-0.46MB/s1││││›[ ] @@kpmeh\- 2013 -\E-Dan 11.1 MB Dancemaniako 320 kbps 0.46 MB/s 1││DoneSaveYo245││[]@@kpmeh\-2013-\Armad17.5MBDancemaniako320kbps0.46MB/s1││││[✓]@@kpmeh\-2013-\E-Dan11.1MBDancemaniako320kbps0.46MB/s1││││↯Dozel0.4MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││↯Dozel0.4MB/26.9MB0.1MB/s││TechnoHouse&TranceCollection││││[]@@kpmeh\-2014-\Armad15.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Armad10.5MBDancemaniako320kbps0.46MB/s1││││↯Dozel0.5MB/26.9MB0.4MB/s││TechnoHouse&TranceCollection││││[]@@kpmeh\-2015-\VA-16.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Top114.7MBDancemaniako320kbps0.46MB/s1││││↯Dozel0.5MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││[]@@kpmeh\-2017-\Beatp16.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\Beatp16.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\Beatp14.9MBDancemaniako320kbps0.46MB/s1││││↯Dozel0.6MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││[]@@kpmeh\-2022-\Defec19.0MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││││[✓]@@jlksj\new_music\Gabr23.6MBQadavre320kbps1.69MB/s1││││↯Dozel0.7MB/26.9MB0.4MB/s││TechnoHouse&TranceCollection││││✓QadavreCompleted-││byLORDEGRAFvol.12││Donetiesto0││[]music3\MaceoPlex-DJ35.2MBcinstitute2-4.4MB/s1││Donethexx0││[]music3\MaceoPlex-DJ35.9MBcinstitute2-4.4MB/s1││Donedjbount0││[]music3\MaceoPlex-DJ27.7MBcinstitute2-4.4MB/s1││DoneSaveYo245││[]music3\MaceoPlex-DJ37.7MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ30.0MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ24.5MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ43.8MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ26.3MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ26.8MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ40.8MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ0.0MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ0.2MBcinstitute2-4.4MB/s1││›Done Maceo P 612││[✓]music3\MaceoPlex-DJ26.2MBcinstitute2-4.4MB/s1││││↯Dozel0.8MB/26.9MB0.1MB/s││TechnoHouse&TranceCollection││││↯Dozel0.8MB/26.9MB0.4MB/s││TechnoHouse&TranceCollection││›Done Maceo P 612││[]music3\MaceoPlex-DJ26.2MBcinstitute2-4.4MB/s1││││›[ ] music3\Maceo Plex - DJ 26.3 MB cinstitute2 - 4.4 MB/s 1││││⋯cinstitute2Queued-││Size9.4MB││││⋯cinstitute2Queued-││StatusCompleted│╭[1]Searches───────────────────────╮╭[2]Results:MaceoPlex────────────────────────────────────────────────────────────────╮│StatusQueryResults││✓FilenameSizeUserBitrateSpeedSlots││Donetiesto0││[]music3\MaceoPlex-DJ35.2MBcinstitute2-4.4MB/s1││Donethexx0││[]music3\MaceoPlex-DJ35.9MBcinstitute2-4.4MB/s1││Donedjbount0││[]music3\MaceoPlex-DJ27.7MBcinstitute2-4.4MB/s1││DoneSaveYo245││[]music3\MaceoPlex-DJ37.7MBcinstitute2-4.4MB/s1││›Done Maceo P 612││[]music3\MaceoPlex-DJ26.2MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ30.0MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ24.5MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ43.8MBcinstitute2-4.4MB/s1││││›[ ] music3\Maceo Plex - DJ 26.3 MB cinstitute2 - 4.4 MB/s 1││││[]music3\MaceoPlex-DJ26.8MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ40.8MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ0.0MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ0.2MBcinstitute2-4.4MB/s1│││╰─────────────────────────────────────────────────────────────────────────────────────────╯││╭[3]Downloads/Uploads──────────────────────────────╮╭[Info]──────────────────────────╮│││StatUserProgressSpeed││RavingGeorgefeat.Oscar&The││││›✓ Dancemaniako Completed -││Wolf-TwoClocks(MaceoPlex││││⋯DancemaniakoQueued-││@@kpmeh/-2013-/Electro,Deep,││││↯Dozel0.9MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││✓QadavreCompleted-││byLORDEGRAFvol.12││││✓vivioCompleted-││││││⋯sillysillysillyQueued-││UserDancemaniako││││⋯cinstitute2Queued-││StatusCompleted│╰─────────────────────────────────────╯╰─────────────────────────────────────────────────────╯╰──────────────────────────────────╯│[p→pause/resume][r→retryfailed][d→deletequeued][c→clearfinished][b→browseuser][1-3→focuspane][q⟨✉1⟩││[p→pause/resume][r→retryfailed][d→deletequeued][c→clearfinished][b→browseuser][1-3→focuspane][q⟨✉1⟩││││⋯cinstitute2Queued-││Saveto││││StatUserProgressSpeed││020.MaceoPlex-Insomnia││││⋯sillysillysillyQueued-││2022.mp3││││⋯cinstitute2Queued-││ClubbingElectroHouseVol.12││││✓beatrice.songbiCompleted-││││││›⋯ Dancemaniako Queued -││/Users/micheldegraaf/Downloads/S││[p→pause/resume][r→retryfailed][d→deletequeued][c→clearfinished][b→browseuser][1-3→focuspane][q⟨✉2⟩││││⋯cinstitute2Queued-││││││✓beatrice.songbiCompleted-││UserDancemaniako││││⋯DancemaniakoQueued-││Saveto││││⋯DancemaniakoQueued-││/Users/micheldegraaf/Downloads/S││[p→pause/resume][r→retryfailed][d→deletequeued][c→clearfinished][b→browseuser][1-3→focuspane][q⟨✉2⟩││││StatUserProgressSpeed││OddParents,MaceoPlex-Learn││││✓cinstitute2Completed-││ToFly(Maceo's808Dub).mp3││││✓beatrice.songbiCompleted-││#BeatportdecadeTechHouse││││✓beatrice.songbiCompleted-││UserQadavre││││›✓ Qadavre Completed -││Size17.5MB││││StatUserProgressSpeed││OddParents,MaceoPlex-Learn││││✓cinstitute2Completed-││ToFly(Maceo's808Dub).mp3││││✓beatrice.songbiCompleted-││#BeatportdecadeTechHouse││││⋯cinstitute2Queued-││││││✓beatrice.songbiCompleted-││UserQadavre││││›✓ Qadavre Completed -││Size17.5MB││││⋯DancemaniakoQueued-││Saveto││││⋯DancemaniakoQueued-││/Users/micheldegraaf/Downloads/S││[Space→select][Enter→download][b→browseowner][c→chat][/→filter][a/A→selectall/none][1-3→focuspan⟨✉2⟩││[Space→select][Enter→download][b→browseowner][c→chat][/→filter][a/A→selectall/none][1-3→focuspan⟨✉2⟩││││✓cinstitute2Completed-││@@jlksj/new_music/EllumAudio││Status╭Rooms(Enter:join,/:filter,Tab:openrooms,Esc:close)───────────────────────────────────────────────╮Slots││Done│roomusers│1││Done│› ! ! LGBTQ+ ! ! 461│1││Done│RareMusic320│1││Done│Linux262│1││›Done│SoulSync229│1│││lossless199│1│││indie180│1│││+BlackMetal+153│1│││drum'n'bass150│1│││/mu/148│1│││what.cd138│1│││Furry135│1│││:3126│1│││TECHNO,MixesandTunes122│1│││r/musichoarder119│1│││japanesemusic113│─────────╯││MOVIES105│─────────╮││Ambient89│-Learn│││Anime79│.mp3│││!!!cumeaters!!!77│Audio│││EBooks77│se│││HOUSEMUSICLOVERS(AG)69││││+Autism+62│vre│││Hardcore/punk62│MB│││!meowchat:361│leted│││programming61│││╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯nloads/S││[↑↓→move][Enter→join][/→filter][Tab→openrooms][Esc→close]⟨✉2⟩││[↑↓→move][Enter→join][/→filter][Tab→openrooms][Esc→close]⟨✉2⟩││Done│nicotine1270│1││Done│!!LGBTQ+!!466│1││Done│› Rare Music 327│1││›Done│Linux264│1│││SoulSync230│1│││TheLobby215│1│││lossless200│1│││indie181│1│││+BlackMetal+155│1│││/mu/151│1│││drum'n'bass150│1│││what.cd139│1│││Furry137│1│││TECHNO,MixesandTunes122│─────────╯││r/musichoarder120│─────────╮││japanesemusic117│-Learn│││MOVIES106│.mp3│││Ambient89│Audio│││Anime79│se│││EBooks77││││!!!cumeaters!!!76│vre│││HOUSEMUSICLOVERS(AG)68│MB│││!meowchat:362│leted│││+Autism+62│││Status╭Chatrooms(Tab:switch,l:roomlist,x:leave,Esc:back)───────────────────────────────────────────────╮Slots││Done│Rare Music│1││Done│04:59→jackass44│Users(1)│1││Done││▸ jackass44│1││Done│││1││›Done│││1│││││1│││││1│││││─────────╯││││─────────╮││││-Learn│││││.mp3│││││Audio│││││se││││││││││vre│││││MB│││││leted│││Enter:typeamessage│││[Enter→say][↑↓→pickuser][b→browseuser][m→messageuser][Tab→switchroom][l→rooms][x→leave]⟨✉2⟩││[Enter→say][↑↓→pickuser][b→browseuser][m→messageuser][Tab→switchroom][l→rooms][x→leave]⟨✉2⟩││[Type→message][Enter→send][Esc→cancel]⟨✉2⟩││Done│04:59→jackass44│Users(328)│1││Done│04:59—joined—│▸ mayonerio│1││Done││bradybunch668│1││›Done││splashdown│1││││1800thrownaway│1││││baza333│1││││Galaxion│1││││enricopoliman│1││││smilerrr│1││││DJ_Hannibalistic│1││││toddomg│1││││niet│1││││kebnimqojom│1││││beautysleep│1││││sSsSs│─────────╯│││ratgirrl│─────────╮│││v3rtual│-Learn││││professionalspeed│.mp3││││kasmanga1│Audio││││uvizj4yt8f75│se││││SBORRANET123│││││xeroxeom│vre││││osofer│MB││││craigp56│leted││[Type→message][Enter→send][Esc→cancel]⟨✉2⟩│││›helloworld▏│││Done│04:59←stupidmusichoarder│bradybunch668│1││Done│04:59→jackass44│Users(326)│1││›Done│04:59←Bipolar213│splashdown│1│││04:59<jackass44>helloworld!│1800thrownaway│1││Done│04:59→jackass44│Users(325)│1│││04:59←dudebro│baza333│1│││04:59→martitoci│Galaxion│1││Done│04:59—joined—│mayonerio│1││›Done│04:59←Bipolar213│▸ splashdown│1││Status╭Browsesplashdown───────────────────────────────────────────────────────────────────────────────────╮Slots││Done│⠋Requestingsharedfilesfromsplashdown…│1││Done││1││›Done││1││││1││││1││││────────────╯│││────────────╮│││ex-Learn││││ub).mp3││││umAudio││││House││││││││adavre││││7.5MB││││ompleted││╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯Downloads/S││[↑↓→move][→←→expand/collapse][Enter→open/download][d→downloadfolder][Tab→switchuser][r→retry][w→c⟨✉2⟩││Done│⠴Requestingsharedfilesfromsplashdown…│1││[↑↓→move][→←→expand/collapse][Enter→open/download][d→downloadfolder][Tab→switchuser][r→retry][w→c⟨✉2⟩││││✓cinstitute2Completed-││││││✓QadavreCompleted-││StatusCompleted││Doned╭Settings───────────────────────────────────────────────────────────────────────────────╮4MB/s1││DoneS│>Downloadfolder:/Users/micheldegraaf/Downloads/Soulseek│4MB/s1││›Done M││4MB/s1│││Sharedfolders(2):│4MB/s1│││/Users/micheldegraaf/Downloads/Soulseek│4MB/s1│││/Users/micheldegraaf/Downloads│4MB/s1││││4 MB/s 1││││4MB/s1││││──────────────────╯│││──────────────────╮│││ceoPlex-Learn││││808Dub).mp3││││ic/EllumAudio││││TechHouse││││││││Qadavre││╰─────────────────────────────────────────────────────────────────────────────────────────╯17.5MB││[↑↓→move][Enter/e→editdownloaddir][a→addshare][d→removeshare][r→re-index][Esc→close]⟨✉2⟩││[↑↓→move][Enter/e→editdownloaddir][a→addshare][d→removeshare][r→re-index][Esc→close]⟨✉2⟩││search:SaveYourAtoll││›Active Save Yo 85││[]@@vqmcr\20241128\Save60.7MBQadavre-1.69MB/s1││││[]@@vqmcr\2││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail0.0/9.4MB·0MB/s·[░░░░░░░░░░░░░░░░░░░░░]0%││││›↯ Dancemaniako 0.0 MB/9.4 MB ( 0.0 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail0.1/9.4MB·0.11MB/s·[░░░░░░░░░░░░░░░░░░░░░]0%││││›↯ Dancemaniako 0.1 MB/9.4 MB ( 0.1 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail0.4/9.4MB·0.56MB/s·[░░░░░░░░░░░░░░░░░░░░░]3%││││›↯ Dancemaniako 0.4 MB/9.4 MB ( 0.6 MB/s││Wolf-TwoClocks(MaceoPlex││search:MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail0.6/9.4MB·0.61MB/s·[█░░░░░░░░░░░░░░░░░░░░]6%││││›↯ Dancemaniako 0.6 MB/9.4 MB ( 0.6 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail0.9/9.4MB·0.62MB/s·[█░░░░░░░░░░░░░░░░░░░░]9%││││›↯ Dancemaniako 0.9 MB/9.4 MB ( 0.6 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail1.1/9.4MB·0.48MB/s·[██░░░░░░░░░░░░░░░░░░░]11%││ActiveSaveYo219││[]Music\0dayMP3\2026\20││││›↯ Dancemaniako 1.0 MB/9.4 MB ( 0.8 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail1.3/9.4MB·0.47MB/s·[██░░░░░░░░░░░░░░░░░░░]14%││ActiveSaveYo219││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││›Active Maceo P 56││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││││›↯ Dancemaniako 1.3 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail1.7/9.4MB·1.6MB/s·[███░░░░░░░░░░░░░░░░░░]17%││›Active Save Yo 225││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││ActiveMaceoP56││[]Music\0dayMP3\2026\200.0MBbeatrice.songbi-1.87MB/s1││││›↯ Dancemaniako 1.7 MB/9.4 MB ( 1.6 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail1.9/9.4MB·0.46MB/s·[████░░░░░░░░░░░░░░░░░]19%││ActiveMaceoP56││[]@@vqmcr\20241128\Save80.4MBQadavre-1.69MB/s1││││›↯ Dancemaniako 1.9 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail2.1/9.4MB·0.48MB/s·[████░░░░░░░░░░░░░░░░░]21%││││›↯ Dancemaniako 2.1 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail2.3/9.4MB·1.26MB/s·[█████░░░░░░░░░░░░░░░░]24%││Donetiesto0││[]@@kpmeh\-2021-\TheP16.5MBDancemaniako320kbps0.46MB/s1││Donedjbount0││›[✓] @@vqmcr\20241128\Save 70.0 MB Qadavre - 1.69 MB/s 1││││›↯ Dancemaniako 2.3 MB/9.4 MB ( 1.3 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail2.6/9.4MB·0.49MB/s·[█████░░░░░░░░░░░░░░░░]27%││Donetiesto0││[]@@vqmcr\20241128\Save56.0MBQadavre-1.69MB/s1││Donethexx0││[✓]@@vqmcr\20241128\Save70.0MBQadavre-1.69MB/s1││Donedjbount0││[]@@vqmcr\20241128\Save60.7MBQadavre-1.69MB/s1││›Active Save Yo 227││[]@@vqmcr\20241128\Save80.4MBQadavre-1.69MB/s1││ActiveMaceoP589││[]@@vqmcr\20241128\Save64.3MBQadavre-1.69MB/s1││││›[ ] @@vqmcr\20241128\VA-Pr 37.0 MB Qadavre - 1.69 MB/s 1││││›↯ Dancemaniako 2.6 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail2.9/9.4MB·1.24MB/s·[██████░░░░░░░░░░░░░░░]30%││││[]@@lustt\complete\koala13.6MBvivio320kbps1.16MB/s1││││›[ ] @@lustt\complete\koala 14.4 MB vivio 320 kbps 1.16 MB/s 1││││›↯ Dancemaniako 2.9 MB/9.4 MB ( 1.2 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail3.1/9.4MB·1.13MB/s·[██████░░░░░░░░░░░░░░░]32%││││›[✓] @@lustt\complete\koala 13.6 MB vivio 320 kbps 1.16 MB/s 1││││[]@@lustt\complete\koala14.4MBvivio320kbps1.16MB/s1││││›↯ Dancemaniako 3.1 MB/9.4 MB ( 1.1 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail3.3/9.4MB·0.44MB/s·[███████░░░░░░░░░░░░░░]35%││Donetiesto0││[]@@lustt\complete\koala14.4MBvivio320kbps1.16MB/s1││Donethexx0││[]@@lustt\complete\koala16.3MBvivio320kbps1.16MB/s1││Donedjbount0││[]@@lustt\complete\koala14.7MBvivio320kbps1.16MB/s1││›Done Save Yo 227││[]@@lustt\complete\koala14.8MBvivio320kbps1.16MB/s1││ActiveMaceoP589││[]@@lustt\complete\koala16.5MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala15.6MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\koala15.3MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\liuzi15.2MBvivio-1.16MB/s1││││[]@@lustt\complete\liuzi16.3MBvivio-1.16MB/s1││││[]@@lustt\complete\panin16.8MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\panin19.9MBvivio320kbps1.16MB/s1││││›[ ] @@lustt\complete\panin 17.9 MB vivio 320 kbps 1.16 MB/s 1││││›↯ Dancemaniako 3.3 MB/9.4 MB ( 0.4 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail3.6/9.4MB·0.82MB/s·[███████░░░░░░░░░░░░░░]37%││Donetiesto0││[]@@lustt\complete\panin16.2MBvivio320kbps1.16MB/s1││Donethexx0││[]@@lustt\complete\panin15.4MBvivio320kbps1.16MB/s1││Donedjbount0││[]@@lustt\complete\panin19.4MBvivio320kbps1.16MB/s1││›Done Save Yo 227││[]@@lustt\complete\panin19.4MBvivio320kbps1.16MB/s1││ActiveMaceoP589││[]@@lustt\complete\panin14.3MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\panin19.0MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\panin15.8MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\panin16.9MBvivio320kbps1.16MB/s1││││[]@@lustt\complete\panin15.2MBvivio320kbps1.16MB/s1││││[]@@qxibn\music2024\Osl64.9MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav56.0MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav70.0MBsillysillysilly-0.64MB/s0││││[]@@qxibn\music2024\Sav60.7MBsillysillysilly-0.64MB/s0││││›[ ] @@qxibn\music 2024\Sav 80.4 MB sillysillysilly - 0.64 MB/s 0││││›↯ Dancemaniako 3.6 MB/9.4 MB ( 0.8 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail3.7/9.4MB·0.5MB/s·[████████░░░░░░░░░░░░░]39%││›Done Save Yo 227││[]@@qxibn\music2024\Sav57.9MBsillysillysilly-0.64MB/s0││ActiveMaceoP589││[]@@qxibn\music2024\Sav67.2MBsillysillysilly-0.64MB/s0││││›[ ] @@puclh\Marzo 2024\05- 60.8 MB neska 1411 kbps 1.77 MB/s 1││││›↯ Dancemaniako 3.7 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail4.1/9.4MB·0.56MB/s·[█████████░░░░░░░░░░░░]43%││││›↯ Dancemaniako 4.1 MB/9.4 MB ( 0.6 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail4.3/9.4MB·2.07MB/s·[█████████░░░░░░░░░░░░]45%││││›[✓] @@qxibn\music 2024\Sav 68.0 MB sillysillysilly - 0.64 MB/s 0││││›↯ Dancemaniako 4.3 MB/9.4 MB ( 2.1 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail4.6/9.4MB·0.52MB/s·[██████████░░░░░░░░░░░]49%││││›[ ] @@qxibn\music 2024\Sav 59.7 MB sillysillysilly - 0.64 MB/s 0││││›↯ Dancemaniako 4.6 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail4.9/9.4MB·3.49MB/s·[██████████░░░░░░░░░░░]52%││││›[ ] @@qxibn\music 2024\Sav 1.9 MB sillysillysilly - 0.64 MB/s 0││││›↯ Dancemaniako 4.9 MB/9.4 MB ( 3.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail5.2/9.4MB·2.3MB/s·[███████████░░░░░░░░░░]54%││││›↯ Dancemaniako 5.2 MB/9.4 MB ( 2.3 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail5.4/9.4MB·1.79MB/s·[████████████░░░░░░░░░]57%││││›↯ Dancemaniako 5.4 MB/9.4 MB ( 1.8 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail5.6/9.4MB·0.45MB/s·[████████████░░░░░░░░░]59%││││›↯ Dancemaniako 5.6 MB/9.4 MB ( 0.4 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail5.9/9.4MB·1.69MB/s·[█████████████░░░░░░░░]62%││││›↯ Dancemaniako 5.9 MB/9.4 MB ( 1.7 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail6.1/9.4MB·0.48MB/s·[█████████████░░░░░░░░]64%││││›↯ Dancemaniako 6.1 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:1active,0completed,0fail6.4/9.4MB·0.54MB/s·[██████████████░░░░░░░]67%││││›↯ Dancemaniako 6.4 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail8.3/120.1MB·42.32MB/s·[█░░░░░░░░░░░░░░░░░░░░]6%││││›↯ Dancemaniako 6.6 MB/9.4 MB ( 0.6 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre1.0MB/70.0MB5.1MB/s││byLORDEGRAFvol.12││││↯vivio0.6MB/13.6MB36.3MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail11.5/120.1MB·10.84MB/s·[██░░░░░░░░░░░░░░░░░░░]9%││││›↯ Dancemaniako 6.8 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre2.6MB/70.0MB6.7MB/s││byLORDEGRAFvol.12││││↯vivio2.1MB/13.6MB3.3MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail14.7/120.1MB·9.05MB/s·[██░░░░░░░░░░░░░░░░░░░]12%││││›↯ Dancemaniako 7.1 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre4.2MB/70.0MB5.1MB/s││byLORDEGRAFvol.12││││↯vivio3.4MB/13.6MB3.2MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail18.7/120.1MB·10.55MB/s·[███░░░░░░░░░░░░░░░░░░]15%││││›↯ Dancemaniako 7.4 MB/9.4 MB ( 2.8 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre6.0MB/70.0MB4.5MB/s││byLORDEGRAFvol.12││││↯vivio5.2MB/13.6MB3.0MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail21.9/120.1MB·11.36MB/s·[███░░░░░░░░░░░░░░░░░░]18%││││›↯ Dancemaniako 7.7 MB/9.4 MB ( 2.5 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre7.6MB/70.0MB5.0MB/s││byLORDEGRAFvol.12││││↯vivio6.7MB/13.6MB3.6MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail25.1/120.1MB·10.33MB/s·[████░░░░░░░░░░░░░░░░░]20%││││›↯ Dancemaniako 7.9 MB/9.4 MB ( 2.1 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre9.1MB/70.0MB4.9MB/s││byLORDEGRAFvol.12││││↯vivio8.0MB/13.6MB3.0MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail28.6/120.1MB·11.11MB/s·[█████░░░░░░░░░░░░░░░░]23%││›Done Save Yo 229││[]@@qxibn\music2024\Sav57.9MBsillysillysilly-0.64MB/s0││DoneMaceoP612││[]@@qxibn\music2024\Sav67.2MBsillysillysilly-0.64MB/s0││││›↯ Dancemaniako 8.2 MB/9.4 MB ( 1.7 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Dozel0.0MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││↯Qadavre10.8MB/70.0MB6.0MB/s││byLORDEGRAFvol.12││││↯vivio9.6MB/13.6MB3.1MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail32.5/120.1MB·9.85MB/s·[█████░░░░░░░░░░░░░░░░]27%││DoneSaveYo229││[]@@qxibn\music2024\Sav57.9MBsillysillysilly-0.64MB/s0││›Done Maceo P 612││[]@@qxibn\music2024\Sav67.2MBsillysillysilly-0.64MB/s0││││›↯ Dancemaniako 8.4 MB/9.4 MB ( 2.0 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Dozel0.1MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││↯Qadavre12.6MB/70.0MB4.7MB/s││byLORDEGRAFvol.12││││↯vivio11.3MB/13.6MB2.8MB/s││││soulseek-rs🦀v7.0.0Downloads:4active,0completed,0fail35.8/120.1MB·13.79MB/s·[██████░░░░░░░░░░░░░░░]29%││││›↯ Dancemaniako 8.7 MB/9.4 MB ( 2.4 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Dozel0.1MB/26.9MB0.5MB/s││TechnoHouse&TranceCollection││││↯Qadavre14.4MB/70.0MB5.8MB/s││byLORDEGRAFvol.12││││↯vivio12.5MB/13.6MB5.1MB/s││││soulseek-rs🦀v7.0.0Downloads:3active,1completed,0fail25.2/106.4MB·7.35MB/s·[████░░░░░░░░░░░░░░░░░]23%││Donethexx0││›[ ] Music\#DJ Pools\2026\2 18.1 MB beatrice.songbi 320 kbps 1.87 MB/s 1││││›↯ Dancemaniako 8.9 MB/9.4 MB ( 2.1 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre16.1MB/70.0MB4.8MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,1completed,0fail27.1/106.4MB·1.94MB/s·[█████░░░░░░░░░░░░░░░░]25%││Donedjbount0││›[ ] Music\0day MP3\2026\20 0.0 MB beatrice.songbi - 1.87 MB/s 1││││›↯ Dancemaniako 9.2 MB/9.4 MB ( 0.5 MB/s││Wolf-TwoClocks(MaceoPlex││││↯Qadavre17.7MB/70.0MB1.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:2active,2completed,0fail19.8/97.0MB·3.44MB/s·[████░░░░░░░░░░░░░░░░░]20%││││↯Dozel0.2MB/26.9MB0.1MB/s││TechnoHouse&TranceCollection││││↯Qadavre19.6MB/70.0MB3.4MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:2active,2completed,0fail21.4/97.0MB·4.71MB/s·[████░░░░░░░░░░░░░░░░░]22%││││›[ ] @@jlksj\new_music\Gabr 23.6 MB Qadavre 320 kbps 1.69 MB/s 1││││↯Qadavre21.2MB/70.0MB4.5MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail22.9/108.1MB·7.48MB/s·[████░░░░░░░░░░░░░░░░░]21%││││›[✓] @@jlksj\new_music\Ellu 17.5 MB Qadavre 320 kbps 1.69 MB/s 1││││↯Qadavre22.7MB/70.0MB7.3MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail24.6/108.1MB·6.01MB/s·[████░░░░░░░░░░░░░░░░░]22%││││›[✓] @@jlksj\new_music\Gabr 23.6 MB Qadavre 320 kbps 1.69 MB/s 1││││↯Qadavre24.4MB/70.0MB5.8MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail26.6/108.1MB·7.02MB/s·[█████░░░░░░░░░░░░░░░░]24%││Donetiesto0││[]@@jlksj\new_music\Mace16.6MBQadavre320kbps1.69MB/s1││Donethexx0││[]@@jlksj\new_music\Mace17.1MBQadavre320kbps1.69MB/s1││Donedjbount0││[]@@jlksj\new_music\Mace17.3MBQadavre320kbps1.69MB/s1││DoneSaveYo229││[]@@jlksj\new_music\VA-12.9MBQadavre320kbps1.69MB/s1││›Done Maceo P 612││[]@@jlksj\new_music\WhoM15.3MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\WhoM17.9MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\augu0.1MBQadavre-1.69MB/s1││││[]@@jlksj\new_music\augu18.3MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\june17.7MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\marc16.9MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\marc15.1MBQadavre320kbps1.69MB/s1││││[]@@jlksj\new_music\marc15.2MBQadavre320kbps1.69MB/s1││││›[ ] @@jlksj\new_music\marc 17.2 MB Qadavre 320 kbps 1.69 MB/s 1││││↯Dancemaniako0.1MB/11.1MB0.1MB/s││Remix).mp3││││↯Dozel0.3MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││↯Qadavre26.2MB/70.0MB6.6MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail28.3/108.1MB·7.29MB/s·[█████░░░░░░░░░░░░░░░░]26%││Donetiesto0││[]@@qgkbx\tmp\GabrielAn12.9MBQadavre320kbps1.69MB/s1││Donethexx0││[]@@qgkbx\tmp\MaceoPlex20.6MBQadavre320kbps1.69MB/s1││Donedjbount0││[]@@qgkbx\tmp\MaceoPlex17.3MBQadavre320kbps1.69MB/s1││DoneSaveYo229││[]@@qgkbx\tmp\MaribouSt13.5MBQadavre320kbps1.69MB/s1││›Done Maceo P 612││[]@@qgkbx\tmp\Rebolledo16.5MBQadavre-1.69MB/s1││││[]@@qgkbx\tmp\Royksopp-S18.3MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl13.6MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl21.1MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl11.4MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl8.5MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl17.4MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl9.3MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl15.6MBQadavre320kbps1.69MB/s1││││[]@@vqmcr\SeagatePortabl16.4MBQadavre320kbps1.69MB/s1││││›[ ] @@vqmcr\SeagatePortabl 17.5 MB Qadavre 320 kbps 1.69 MB/s 1││││↯Dancemaniako0.4MB/11.1MB0.4MB/s││Remix).mp3││││↯Qadavre27.7MB/70.0MB6.3MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail30.3/108.1MB·5.16MB/s·[█████░░░░░░░░░░░░░░░░]27%││DoneSaveYo229││[]@@kpmeh\-2013-\Armad17.5MBDancemaniako320kbps0.46MB/s1││││↯Dancemaniako0.6MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre29.3MB/70.0MB4.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail32.1/108.1MB·5.73MB/s·[██████░░░░░░░░░░░░░░░]29%││││↯Dancemaniako0.9MB/11.1MB0.4MB/s││Remix).mp3││││↯Qadavre30.9MB/70.0MB4.8MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail34.2/108.1MB·11.34MB/s·[██████░░░░░░░░░░░░░░░]31%││││›[ ] @@kpmeh\- 2013 -\Defec 54.6 MB Dancemaniako - 0.46 MB/s 1││││↯Dancemaniako1.2MB/11.1MB5.9MB/s││Remix).mp3││││↯Qadavre32.6MB/70.0MB5.2MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail36.0/108.1MB·6.22MB/s·[██████░░░░░░░░░░░░░░░]33%││││›[✓] @@kpmeh\- 2013 -\Defec 54.6 MB Dancemaniako - 0.46 MB/s 1││││↯Dancemaniako1.3MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre34.3MB/70.0MB5.5MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail38.2/108.1MB·5.47MB/s·[███████░░░░░░░░░░░░░░]35%││Donetiesto0││[]@@kpmeh\-2013-\AllG40.0MBDancemaniako-0.46MB/s1││Donethexx0││[]@@kpmeh\-2013-\Armad17.5MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2013-\Azari50.9MBDancemaniako-0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2013-\Beatp18.1MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2013-\Beatp17.4MBDancemaniako320kbps0.46MB/s1││││[✓]@@kpmeh\-2013-\Defec54.6MBDancemaniako-0.46MB/s1││││[✓]@@kpmeh\-2013-\E-Dec54.6MBDancemaniako-0.46MB/s1││││›[ ] @@kpmeh\- 2013 -\Elect 9.4 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako1.6MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre36.2MB/70.0MB4.7MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail40.0/108.1MB·4.45MB/s·[███████░░░░░░░░░░░░░░]36%││Donetiesto0││[]@@kpmeh\-2013-\VA-13.3MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2013-\VA-12.2MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2013-\VA-16.2MBDancemaniako257kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2013-\VA-13.4MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2013-\VA-16.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-15.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-9.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-17.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-20.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-17.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-19.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-8.7MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2013-\VA-14.1MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2013-\VA-12.9MBDancemaniako-0.46MB/s1││││›[ ] @@kpmeh\- 2013 -\VA - 7.2 MB Dancemaniako - 0.46 MB/s 1││││↯Dancemaniako1.9MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre37.7MB/70.0MB3.9MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail41.8/108.1MB·6.75MB/s·[████████░░░░░░░░░░░░░]38%││Donetiesto0││[]@@kpmeh\-2013-\VA-13.7MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2013-\VA-20.0MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2013-\VA-37.4MBDancemaniako-0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2013-\VA-12.9MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2013-\VA-17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-De14.1MBDancemaniako256kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-Se16.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-Sp18.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\VA-Ul14.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Vario54.6MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2013-\Vario22.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Vario15.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Vocal19.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2013-\Vocal18.2MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2013 -\va - 18.9 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako2.2MB/11.1MB3.8MB/s││Remix).mp3││││↯Qadavre39.2MB/70.0MB2.7MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail43.6/108.1MB·8.12MB/s·[████████░░░░░░░░░░░░░]40%││Donetiesto0││[]@@kpmeh\-2014-\ARDI317.4MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2014-\Armad19.2MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2014-\Armad17.5MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2014-\Armad17.3MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2014-\Armad19.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Bargr13.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Beatp19.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Beatp17.9MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2014 -\Beatp 17.6 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako2.4MB/11.1MB3.0MB/s││Remix).mp3││││↯Qadavre40.7MB/70.0MB4.8MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail45.3/108.1MB·4.98MB/s·[████████░░░░░░░░░░░░░]41%││Donetiesto0││[]@@kpmeh\-2014-\Ellum16.0MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2014-\Ellum17.4MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2014-\Ellum17.4MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2014-\Ellum16.8MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2014-\Ellum15.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\GusGu17.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Kompa16.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Maceo13.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Maceo17.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Melod20.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Melod18.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Minis17.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Minis20.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\Pete13.8MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2014 -\Resid 17.3 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako2.6MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre42.3MB/70.0MB4.2MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail47.2/108.1MB·5.41MB/s·[█████████░░░░░░░░░░░░]43%││Donetiesto0││[]@@kpmeh\-2014-\VA-13.4MBDancemaniako249kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2014-\VA-10.0MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2014-\VA-17.2MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2014-\VA-47.0MBDancemaniako-0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2014-\VA-17.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-20.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-17.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-14.9MBDancemaniako275kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-17.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-22.1MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2014-\VA-Az19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2014-\VA-De51.5MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2014-\Vario15.2MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2014 -\Vario 15.2 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako2.9MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre43.8MB/70.0MB5.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail49.1/108.1MB·5.33MB/s·[█████████░░░░░░░░░░░░]45%││Donetiesto0││[]@@kpmeh\-2015-\2015\20.5MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2015-\2015\17.2MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2015-\2015\20.5MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2015-\2015\14.7MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2015-\About49.4MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2015-\Armad17.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Armad8.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\BT_Be17.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\BT_De15.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\BT_Te17.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\BT_Te20.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Beatp17.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Beatp17.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Beatp17.5MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2015 -\Beatp 17.8 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako3.1MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre45.5MB/70.0MB4.5MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail50.9/108.1MB·6.48MB/s·[█████████░░░░░░░░░░░░]47%││Donetiesto0││[]@@kpmeh\-2015-\Club18.2MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2015-\Dance17.1MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2015-\Deep11.7MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2015-\Deep18.2MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2015-\Deep17.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Deep16.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Deep9.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Deep19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Deep16.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Deep16.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Defec16.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Defec17.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\Globa54.3MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2015-\Globa47.0MBDancemaniako-0.46MB/s1││││›[ ] @@kpmeh\- 2015 -\Grace 20.8 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako3.4MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre47.0MB/70.0MB5.7MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail52.9/108.1MB·7.52MB/s·[██████████░░░░░░░░░░░]48%││Donetiesto0││[]@@kpmeh\-2015-\Toolr16.9MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2015-\VA-17.2MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2015-\VA-49.5MBDancemaniako-0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2015-\VA-46.4MBDancemaniako-0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2015-\VA-17.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\VA-47.1MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2015-\VA-18.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\VA-54.8MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2015-\VA-9.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\VA-20.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\VA-20.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\VA-19.7MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2015 -\VA - 18.5 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako3.7MB/11.1MB2.6MB/s││Remix).mp3││││↯Qadavre48.7MB/70.0MB4.6MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail54.7/108.1MB·7.78MB/s·[██████████░░░░░░░░░░░]50%││Donetiesto0││[]@@kpmeh\-2015-\beatp17.7MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2015-\beatp17.9MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2015-\beatp18.0MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2015-\beatp20.5MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2015-\beatp17.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2015-\theb16.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\01-2017.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\201526.5MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2016-\201651.4MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2016-\201651.6MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2016-\201641.2MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2016-\2016.15.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\2016.9.0MBDancemaniako192kbps0.46MB/s1││││[]@@kpmeh\-2016-\2016.10.0MBDancemaniako192kbps0.46MB/s1││││›[ ] @@kpmeh\- 2016 -\BT To 15.2 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako3.9MB/11.1MB3.0MB/s││Remix).mp3││││↯Dozel0.5MB/26.9MB0.1MB/s││TechnoHouse&TranceCollection││││↯Qadavre50.2MB/70.0MB4.7MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail56.4/108.1MB·5.2MB/s·[██████████░░░░░░░░░░░]52%││Donetiesto0││[]@@kpmeh\-2016-\Dutch15.1MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2016-\Minis52.0MBDancemaniako-0.46MB/s1││Donedjbount0││[]@@kpmeh\-2016-\Resid18.8MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2016-\Spinn15.6MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2016-\Tech18.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Techn20.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Techn18.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Toolr15.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Top116.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Top117.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\Top118.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2016-\VA-19.4MBDancemaniako-0.46MB/s1││││›[ ] @@kpmeh\- 2016 -\VA - 19.4 MB Dancemaniako - 0.46 MB/s 1││││↯Dancemaniako4.1MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre51.7MB/70.0MB4.4MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail58.3/108.1MB·5.69MB/s·[███████████░░░░░░░░░░]53%││Donetiesto0││[]@@kpmeh\-2016-\remix18.6MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2017-\100E18.4MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2017-\100E18.4MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2017-\2017.11.0MBDancemaniako192kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2017-\2017.10.8MBDancemaniako192kbps0.46MB/s1││││[]@@kpmeh\-2017-\Beatp0.7MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2017-\Beatp17.9MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2017 -\Beatp 16.5 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako4.4MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre53.4MB/70.0MB5.0MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail60.2/108.1MB·6.04MB/s·[███████████░░░░░░░░░░]55%││Donetiesto0││[]@@kpmeh\-2017-\IP6(212.5MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2017-\Resid16.2MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2017-\Resid18.8MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2017-\This51.8MBDancemaniako-0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2017-\This17.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\This51.8MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2017-\VA-16.6MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-16.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-9.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-17.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-16.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2017-\VA-16.2MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2017 -\VA - 11.0 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako4.6MB/11.1MB0.5MB/s││Remix).mp3││││↯Qadavre55.0MB/70.0MB5.3MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail62.0/108.1MB·3.57MB/s·[████████████░░░░░░░░░]57%││Donetiesto0││[]@@kpmeh\-2018-\21PRE13.0MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2018-\Beatp16.1MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2018-\Beatp20.5MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2018-\Beatp14.5MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2018-\Beatp16.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Beatp16.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Beatp13.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Beatp13.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Beatp14.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Beatp17.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Beatp16.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Indie16.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\July5.9MBDancemaniako128kbps0.46MB/s1││││[]@@kpmeh\-2018-\Maceo36.0MBDancemaniako-0.46MB/s1││││›[ ] @@kpmeh\- 2018 -\Maceo 38.6 MB Dancemaniako - 0.46 MB/s 1││││↯Dancemaniako4.9MB/11.1MB0.5MB/s││Remix).mp3││││↯Dozel0.6MB/26.9MB0.1MB/s││TechnoHouse&TranceCollection││││↯Qadavre56.5MB/70.0MB3.0MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail63.9/108.1MB·9.66MB/s·[████████████░░░░░░░░░]59%││Donetiesto0││[]@@kpmeh\-2018-\TheS37.5MBDancemaniako-0.46MB/s1││Donethexx0││[]@@kpmeh\-2018-\Toolr13.5MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2018-\Top516.1MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2018-\VA-13.4MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2018-\VA-40.6MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2018-\Volum19.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\Volum21.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2018-\tech16.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2019-\Beatp17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2019-\Beatp16.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2019-\Beatp14.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2019-\Fresh14.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2019-\Mayda14.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2019-\Mayda17.5MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2019 -\TOP N 14.0 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako5.1MB/11.1MB0.4MB/s││Remix).mp3││││↯Qadavre58.0MB/70.0MB1.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail65.6/108.1MB·3.54MB/s·[████████████░░░░░░░░░]60%││Donetiesto0││[]@@kpmeh\-2021-\VA-17.2MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2021-\VA-18.9MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2021-\VA-Se14.7MBDancemaniako265kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2021-\defec19.0MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2021-\defec17.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2022-\Best19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2022-\Best17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2022-\Defec17.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2022-\Defec18.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2022-\E-Dan11.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2022-\NewE14.5MBDancemaniako-0.46MB/s1││││[]@@kpmeh\-2022-\VA-19.2MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\- 2022 -\VA - 18.9 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako5.3MB/11.1MB2.2MB/s││Remix).mp3││││↯Qadavre59.6MB/70.0MB1.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail67.4/108.1MB·1.79MB/s·[█████████████░░░░░░░░]62%││Donetiesto0││[]@@kpmeh\-2023-\TECHN18.4MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\-2023-\TECHN13.5MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\-2023-\TECHN17.5MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\-2023-\VA-20.5MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\-2023-\VA-17.4MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2023-\VA-19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2023-\VA-17.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2024-\Defec19.0MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2024-\Defec18.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\-2024-\Hard17.9MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears13.5MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears16.7MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears9.1MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears17.2MBDancemaniako320kbps0.46MB/s1││││›[ ] @@kpmeh\Various Years 19.0 MB Dancemaniako 320 kbps 0.46 MB/s 1││││↯Dancemaniako5.6MB/11.1MB0.4MB/s││Remix).mp3││││↯Qadavre61.2MB/70.0MB1.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail69.3/108.1MB·2.11MB/s·[█████████████░░░░░░░░]64%││Donetiesto0││[]@@kpmeh\VariousYears17.4MBDancemaniako320kbps0.46MB/s1││Donethexx0││[]@@kpmeh\VariousYears18.2MBDancemaniako320kbps0.46MB/s1││Donedjbount0││[]@@kpmeh\VariousYears15.6MBDancemaniako320kbps0.46MB/s1││DoneSaveYo245││[]@@kpmeh\VariousYears17.5MBDancemaniako320kbps0.46MB/s1││›Done Maceo P 612││[]@@kpmeh\VariousYears13.8MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears18.2MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears18.3MBDancemaniako320kbps0.46MB/s1││││[]@@kpmeh\VariousYears19.4MBDancemaniako320kbps0.46MB/s1││││[]music3\Chromatics-Sh33.1MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ30.6MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ22.2MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ10.9MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ24.9MBcinstitute2-4.4MB/s1││││[]music3\MaceoPlex-DJ33.6MBcinstitute2-4.4MB/s1││││›[ ] music3\Maceo Plex - DJ 32.9 MB cinstitute2 - 4.4 MB/s 1││││↯Dancemaniako5.8MB/11.1MB0.4MB/s││Remix).mp3││││↯Dozel0.6MB/26.9MB0.6MB/s││TechnoHouse&TranceCollection││││↯Qadavre62.8MB/70.0MB1.1MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail71.1/108.1MB·5.61MB/s·[█████████████░░░░░░░░]65%││││›[ ] @@jlksj\new_music\Ali 14.0 MB Qadavre 320 kbps 1.69 MB/s 1││││↯Dancemaniako6.0MB/11.1MB0.4MB/s││Remix).mp3││││↯Qadavre64.4MB/70.0MB4.8MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail72.8/108.1MB·4.47MB/s·[██████████████░░░░░░░]67%││││↯Dancemaniako6.3MB/11.1MB0.4MB/s││Remix).mp3││││↯Qadavre65.8MB/70.0MB3.6MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail75.0/108.1MB·5.85MB/s·[██████████████░░░░░░░]69%││›Done Maceo P 612││›[ ] Music\0day MP3\2026\20 0.0 MB beatrice.songbi - 1.87 MB/s 1││││↯Dancemaniako6.6MB/11.1MB0.6MB/s││Remix).mp3││││↯Qadavre67.7MB/70.0MB4.8MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:3active,2completed,0fail76.9/108.1MB·5.41MB/s·[██████████████░░░░░░░]71%││›Done Maceo P 612││›[✓] Music\0day MP3\2026\20 0.0 MB beatrice.songbi - 1.87 MB/s 1││││↯Dancemaniako6.9MB/11.1MB0.6MB/s││Remix).mp3││││↯Dozel0.7MB/26.9MB0.5MB/s││TechnoHouse&TranceCollection││││↯Qadavre69.3MB/70.0MB4.3MB/s││byLORDEGRAFvol.12││soulseek-rs🦀v7.0.0Downloads:2active,3completed,0fail7.8/38.0MB·0.91MB/s·[████░░░░░░░░░░░░░░░░░]20%││DoneSaveYo245││›[ ] Music\0day MP3\2026\20 0.0 MB beatrice.songbi - 1.87 MB/s 1││││↯Dancemaniako7.1MB/11.1MB0.5MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:2active,3completed,0fail8.1/38.0MB·0.84MB/s·[████░░░░░░░░░░░░░░░░░]21%││›Done Maceo P 612││›[ ] music3\Maceo Plex - DJ 26.2 MB cinstitute2 - 4.4 MB/s 1││││↯Dancemaniako7.4MB/11.1MB0.5MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:2active,3completed,0fail8.4/38.0MB·2.22MB/s·[████░░░░░░░░░░░░░░░░░]22%││││›[ ] music3\Maceo Plex - DJ 24.5 MB cinstitute2 - 4.4 MB/s 1││││↯Dancemaniako7.7MB/11.1MB2.1MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:2active,3completed,0fail8.7/38.0MB·3.93MB/s·[████░░░░░░░░░░░░░░░░░]22%││││›[ ] music3\Maceo Plex - DJ 43.8 MB cinstitute2 - 4.4 MB/s 1││││↯Dancemaniako7.9MB/11.1MB3.6MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:2active,3completed,0fail8.9/38.0MB·0.83MB/s·[████░░░░░░░░░░░░░░░░░]23%││││[✓]music3\MaceoPlex-DJ43.8MBcinstitute2-4.4MB/s1││││›[✓] music3\Maceo Plex - DJ 26.3 MB cinstitute2 - 4.4 MB/s 1││││↯Dancemaniako8.1MB/11.1MB0.5MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:2active,3completed,0fail9.4/38.0MB·4.05MB/s·[█████░░░░░░░░░░░░░░░░]24%││││↯Dancemaniako8.5MB/11.1MB3.7MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:4active,3completed,0fail10.5/73.6MB·7.42MB/s·[██░░░░░░░░░░░░░░░░░░░]14%││││↯Dancemaniako8.8MB/11.1MB2.7MB/s││Remix).mp3││soulseek-rs🦀v7.0.0Downloads:5active,3completed,0fail15.5/117.4MB·33.69MB/s·[██░░░░░░░░░░░░░░░░░░░]13%││││↯Dancemaniako9.1MB/11.1MB1.8MB/s││Remix).mp3││││↯Dozel0.9MB/26.9MB0.3MB/s││TechnoHouse&TranceCollection││││↯cinstitute20.3MB/43.8MB3.3MB/s││Size9.4MB││soulseek-rs🦀v7.0.0Downloads:5active,3completed,0fail26.6/117.4MB·32.45MB/s·[████░░░░░░░░░░░░░░░░░]22%││││↯Dancemaniako9.3MB/11.1MB2.7MB/s││Remix).mp3││││↯cinstitute20.5MB/43.8MB7.2MB/s││Size9.4MB││soulseek-rs🦀v7.0.0Downloads:4active,4completed,0fail17.2/99.3MB·8.19MB/s·[███░░░░░░░░░░░░░░░░░░]17%││││↯Dancemaniako9.6MB/11.1MB2.8MB/s││Remix).mp3││││↯cinstitute20.9MB/43.8MB0.7MB/s││Size9.4MB││soulseek-rs🦀v7.0.0Downloads:5active,4completed,0fail21.5/99.3MB·23.03MB/s·[████░░░░░░░░░░░░░░░░░]21%││││StatUserProgressSpeed││20SaveYourAtoll-││││✓DancemaniakoCompleted-││Mediterranea.flac││││↯Dancemaniako9.8MB/11.1MB1.7MB/s││@@wztnu/MUZYKA/FLAC/TECHNO/The││││⋯DancemaniakoQueued-││GodsPlanet-X(FLACWEB)[The││││›↯ Dozel 0.9 MB/26.9 MB 0.3 MB/s││GodsPlanet]2020-12-10││││✓QadavreCompleted-││││││✓vivioCompleted-││UserDozel││││⋯sillysillysillyQueued-││Size26.9MB││││↯cinstitute23.2MB/43.8MB15.2MB/s││StatusInprogress││soulseek-rs🦀v7.0.0Downloads:4active,5completed,0fail27.1/99.3MB·41.66MB/s·[█████░░░░░░░░░░░░░░░░]27%││││StatUserProgressSpeed││1-15.Jaydee-Jaydee-PP.flac││││✓DancemaniakoCompleted-││music3/MaceoPlex-DJ-Kicks││││↯Dancemaniako10.1MB/11.1MB2.1MB/s││(2013)[CD-FLAC]││││⋯DancemaniakoQueued-││││││↯Dozel0.9MB/26.9MB0.3MB/s││Usercinstitute2││││✓QadavreCompleted-││Size43.8MB││││✓vivioCompleted-││StatusInprogress││││⋯sillysillysillyQueued-││Saveto││││›↯ cinstitute2 6.9 MB/43.8 MB 33.8 MB/s││/Users/micheldegraaf/Downloads/S││││⋯cinstitute2Queued-││oulseek││soulseek-rs🦀v7.0.0Downloads:4active,5completed,0fail32.9/99.3MB·171.54MB/s·[██████░░░░░░░░░░░░░░░]33%││││↯cinstitute210.9MB/43.8MB166.9MB/s││@@kpmeh/-2013-/E-Dance.││││⋯cinstitute2Queued-││UserDancemaniako││││✓beatrice.songbiCompleted-││Size11.1MB││││↯Qadavre10.9MB/17.5MB3.9MB/s││StatusQueued││││⋯QadavreQueued-││Saveto││soulseek-rs🦀v7.0.0Downloads:4active,5completed,0fail45.0/99.3MB·39.69MB/s·[█████████░░░░░░░░░░░░]45%││││StatUserProgressSpeed││113.SaveYourAtoll-El││││✓DancemaniakoCompleted-││Ritual.mp3││││↯Dancemaniako10.5MB/11.1MB0.5MB/s││@@kpmeh/-2021-/ThePlastic││││›⋯ Dancemaniako Queued -││Moon.TechnoSet││││↯Dozel1.0MB/26.9MB0.1MB/s││││││✓QadavreCompleted-││UserDancemaniako││││✓vivioCompleted-││Size16.5MB││││⋯sillysillysillyQueued-││StatusQueued││││↯cinstitute221.3MB/43.8MB34.9MB/s││Saveto││││⋯cinstitute2Queued-││/Users/micheldegraaf/Downloads/S││soulseek-rs🦀v7.0.0Downloads:4active,5completed,0fail60.2/99.3MB·118.74MB/s·[████████████░░░░░░░░░]60%││││StatUserProgressSpeed││9.AzioneAntropica.flac││││✓DancemaniakoCompleted-││@@qxibn/music2024/SaveYour││││↯Dancemaniako10.7MB/11.1MB0.4MB/s││Atoll/SaveYourAtoll-2023-││││⋯DancemaniakoQueued-││TheBigBreath[24bit]││││↯Dozel1.0MB/26.9MB0.5MB/s││││││✓QadavreCompleted-││User││││✓vivioCompleted-││sillysillysilly││││›⋯ sillysillysilly Queued -││Size59.7MB││││↯cinstitute235.0MB/43.8MB114.0MB/s││StatusQueued││soulseek-rs🦀v7.0.0Downloads:3active,6completed,0fail27.0/55.5MB·2.07MB/s·[██████████░░░░░░░░░░░]48%││││StatUserProgressSpeed││1-16.S.A.M.-Nangijala(Maceo││││⋯DancemaniakoQueued-││PlexReggaeEdit).flac││││↯Dozel1.0MB/26.9MB0.5MB/s││music3/MaceoPlex-DJ-Kicks││││✓QadavreCompleted-││(2013)[CD-FLAC]││││⋯sillysillysillyQueued-││Usercinstitute2││││✓cinstitute2Completed-││Size26.3MB││││⋯cinstitute2Queued-││StatusQueued││││✓beatrice.songbiCompleted-││Saveto││││›⋯ cinstitute2 Queued -││/Users/micheldegraaf/Downloads/S││soulseek-rs🦀v7.0.0Downloads:2active,7completed,0fail17.8/44.4MB·5.28MB/s·[████████░░░░░░░░░░░░░]40%││││✓cinstitute2Completed-││@@kpmeh/-2013-/E-Dance.││││⋯cinstitute2Queued-││User││││⋯cinstitute2Queued-││UserQadavre││││✓beatrice.songbiCompleted-││Size23.6MB││││↯Qadavre16.5MB/17.5MB4.1MB/s││StatusQueued││││›⋯ Qadavre Queued -││Saveto││soulseek-rs🦀v7.0.0Downloads:2active,8completed,0fail1.2/53.2MB·1.03MB/s·[░░░░░░░░░░░░░░░░░░░░░]2%││││StatUserProgressSpeed││08.MaceoPlex-StayHigh││││✓cinstitute2Completed-││Baby.flac││││↯cinstitute20.2MB/26.2MB1.0MB/s││@@kpmeh/-2013-/Defected││││✓beatrice.songbiCompleted-││PresentsMostRated2013││││✓QadavreCompleted-││Size54.6MB││││⋯QadavreQueued-││StatusQueued││soulseek-rs🦀v7.0.0Downloads:4active,8completed,0fail3.1/93.2MB·12.57MB/s·[░░░░░░░░░░░░░░░░░░░░░]3%││││✓cinstitute2Completed-││2022.mp3││││↯cinstitute20.9MB/26.2MB7.4MB/s││@@kpmeh/-2013-/E-Dance.││││✓beatrice.songbiCompleted-││ClubbingElectroHouseVol.12││││✓QadavreCompleted-││Size11.1MB││││↯Qadavre1.2MB/23.6MB4.9MB/s││StatusQueued││││›⋯ Dancemaniako Queued -││Saveto││soulseek-rs🦀v7.0.0Downloads:4active,8completed,0fail5.5/93.2MB·4.75MB/s·[█░░░░░░░░░░░░░░░░░░░░]5%││││↯cinstitute21.4MB/26.2MB0.3MB/s││@@jlksj/new_music/EllumAudio││││↯Qadavre3.1MB/23.6MB4.1MB/s││StatusCompleted││soulseek-rs🦀v7.0.0Downloads:4active,8completed,0fail11.6/93.2MB·138.39MB/s·[██░░░░░░░░░░░░░░░░░░░]12%││││↯cinstitute25.4MB/26.2MB132.9MB/s││@@jlksj/new_music/EllumAudio││││↯Qadavre4.6MB/23.6MB6.4MB/s││StatusCompleted││soulseek-rs🦀v7.0.0Downloads:4active,8completed,0fail20.4/93.2MB·228.76MB/s·[████░░░░░░░░░░░░░░░░░]21%││││↯cinstitute212.6MB/26.2MB223.4MB/s││@@jlksj/new_music/EllumAudio││││↯Qadavre6.4MB/23.6MB4.5MB/s││StatusCompleted││soulseek-rs🦀v7.0.0Downloads:4active,8completed,0fail23.6/93.2MB·130.5MB/s·[█████░░░░░░░░░░░░░░░░]25%││││↯cinstitute213.8MB/26.2MB125.7MB/s││@@jlksj/new_music/EllumAudio││││↯Qadavre8.0MB/23.6MB4.0MB/s││StatusCompleted││soulseek-rs🦀v7.0.0Downloads:3active,9completed,0fail11.3/67.0MB·4.72MB/s·[███░░░░░░░░░░░░░░░░░░]16%││││↯Qadavre9.2MB/23.6MB3.9MB/s││StatusCompleted││soulseek-rs🦀v7.0.0Downloads:3active,9completed,0fail13.0/67.0MB·9.41MB/s·[████░░░░░░░░░░░░░░░░░]19%││││↯Qadavre10.7MB/23.6MB4.9MB/s││StatusCompleted││soulseek-rs🦀v7.0.0Downloads:4active,9completed,0fail15.3/93.3MB·12.98MB/s·[███░░░░░░░░░░░░░░░░░░]16%││soulseek-rs🦀v7.0.0Downloads:4active,9completed,0fail17.9/93.3MB·14.03MB/s·[████░░░░░░░░░░░░░░░░░]19%││soulseek-rs🦀v7.0.0Downloads:4active,9completed,0fail21.0/93.3MB·15.71MB/s·[████░░░░░░░░░░░░░░░░░]22%││soulseek-rs🦀v7.0.0Downloads:4active,9completed,0fail26.6/93.3MB·45.35MB/s·[█████░░░░░░░░░░░░░░░░]28%││soulseek-rs🦀v7.0.0Downloads:4active,9completed,0fail39.0/93.3MB·82.45MB/s·[████████░░░░░░░░░░░░░]41%││soulseek-rs🦀v7.0.0Downloads:3active,10completed,0fai24.8/67.0MB·5.38MB/s·[███████░░░░░░░░░░░░░░]37%││soulseek-rs🦀v7.0.0Downloads:3active,10completed,0fai27.0/67.0MB·5.87MB/s·[████████░░░░░░░░░░░░░]40%│││›▏│││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai4.6/43.4MB·0.71MB/s·[██░░░░░░░░░░░░░░░░░░░]10%│││›h▏│││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai4.9/43.4MB·3.01MB/s·[██░░░░░░░░░░░░░░░░░░░]11%│││›hello▏│││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai5.1/43.4MB·2.4MB/s·[██░░░░░░░░░░░░░░░░░░░]11%│││›hellowo▏│││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai5.4/43.4MB·1.62MB/s·[██░░░░░░░░░░░░░░░░░░░]12%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai5.7/43.4MB·2.2MB/s·[██░░░░░░░░░░░░░░░░░░░]13%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai5.9/43.4MB·2MB/s·[██░░░░░░░░░░░░░░░░░░░]13%││Done│04:59→jackass44│Users(327)│1│││›helloworld!▏│││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai6.1/43.4MB·0.76MB/s·[██░░░░░░░░░░░░░░░░░░░]14%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai6.4/43.4MB·2.28MB/s·[███░░░░░░░░░░░░░░░░░░]14%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai6.7/43.4MB·0.91MB/s·[███░░░░░░░░░░░░░░░░░░]15%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai7.0/43.4MB·8.28MB/s·[███░░░░░░░░░░░░░░░░░░]16%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai7.2/43.4MB·3.15MB/s·[███░░░░░░░░░░░░░░░░░░]16%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai7.5/43.4MB·6.2MB/s·[███░░░░░░░░░░░░░░░░░░]17%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai7.7/43.4MB·0.93MB/s·[███░░░░░░░░░░░░░░░░░░]17%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai8.0/43.4MB·4.37MB/s·[███░░░░░░░░░░░░░░░░░░]18%││Done│04:59←stupidmusichoarder│▸ bradybunch668│1││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai8.2/43.4MB·0.45MB/s·[███░░░░░░░░░░░░░░░░░░]18%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai8.5/43.4MB·0.84MB/s·[████░░░░░░░░░░░░░░░░░]19%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai8.8/43.4MB·0.95MB/s·[████░░░░░░░░░░░░░░░░░]20%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai9.1/43.4MB·3.67MB/s·[████░░░░░░░░░░░░░░░░░]20%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai9.4/43.4MB·2.19MB/s·[████░░░░░░░░░░░░░░░░░]21%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai9.6/43.4MB·0.89MB/s·[████░░░░░░░░░░░░░░░░░]22%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai9.9/43.4MB·0.84MB/s·[████░░░░░░░░░░░░░░░░░]22%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai10.1/43.4MB·0.81MB/s·[████░░░░░░░░░░░░░░░░░]23%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai10.4/43.4MB·0.54MB/s·[█████░░░░░░░░░░░░░░░░]23%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai10.7/43.4MB·0.78MB/s·[█████░░░░░░░░░░░░░░░░]24%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai11.0/43.4MB·3.37MB/s·[█████░░░░░░░░░░░░░░░░]25%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai11.2/43.4MB·3.19MB/s·[█████░░░░░░░░░░░░░░░░]25%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai11.5/43.4MB·2.22MB/s·[█████░░░░░░░░░░░░░░░░]26%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai11.7/43.4MB·1.76MB/s·[█████░░░░░░░░░░░░░░░░]26%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai11.9/43.4MB·1.89MB/s·[█████░░░░░░░░░░░░░░░░]27%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai12.2/43.4MB·0.83MB/s·[█████░░░░░░░░░░░░░░░░]28%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai12.5/43.4MB·0.54MB/s·[██████░░░░░░░░░░░░░░░]28%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai12.8/43.4MB·1.13MB/s·[██████░░░░░░░░░░░░░░░]29%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai13.0/43.4MB·1.06MB/s·[██████░░░░░░░░░░░░░░░]30%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai13.3/43.4MB·0.97MB/s·[██████░░░░░░░░░░░░░░░]30%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai13.6/43.4MB·1.02MB/s·[██████░░░░░░░░░░░░░░░]31%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai13.8/43.4MB·7.92MB/s·[██████░░░░░░░░░░░░░░░]31%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai14.1/43.4MB·2.01MB/s·[██████░░░░░░░░░░░░░░░]32%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai14.3/43.4MB·2.31MB/s·[██████░░░░░░░░░░░░░░░]32%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai14.6/43.4MB·0.78MB/s·[███████░░░░░░░░░░░░░░]33%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai14.9/43.4MB·2.39MB/s·[███████░░░░░░░░░░░░░░]34%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai15.2/43.4MB·0.78MB/s·[███████░░░░░░░░░░░░░░]35%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai15.5/43.4MB·3.12MB/s·[███████░░░░░░░░░░░░░░]35%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai15.7/43.4MB·0.81MB/s·[███████░░░░░░░░░░░░░░]36%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai16.0/43.4MB·0.81MB/s·[███████░░░░░░░░░░░░░░]36%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai16.3/43.4MB·0.79MB/s·[███████░░░░░░░░░░░░░░]37%││soulseek-rs🦀v7.0.0Downloads:2active,11completed,0fai16.3/43.4MB·0.54MB/s·[███████░░░░░░░░░░░░░░]37%││[Space→select][Enter→download][b→browseowner][c→chat][/→filter[r→re-index][Esc→close]⟨✉2⟩│ \ No newline at end of file +╭Status────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│soulseek-rs🦀v14.1.2Downloads:0active,0completed,0failed,0que│╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╭[2]Results───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│soulseek-rsv14.1.2││││Noresultsyet.││Pickasearchin[1]Searches,orstartanewone.│╭[1]Searches────────────────────╮╭[3]Downloads/Uploads──────────────────────────────────────────────╮╭[Info]─────────────────────────────────╮│StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││Selectaresultortransferfordetails│││││││╰──────────────────────────────────╯╰─────────────────────────────────────────────────────────────────────╯╰─────────────────────────────────────────╯╭Shortcuts·Sharing:~/Music──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│[s→search][Enter→results][d→remove][m→message][i→inbox][c→chat][b→browseuser][Tab/1-3→pane][z→zoom][w→hide]││[?→keys][q→quit]│╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│search:│╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯│[Type→searchterm][←→→movecursor][Backspace/Del→edit][Enter→search][Esc→cancel]││search:public││search:publicdomain││search:publicdomainfield││search:publicdomainfieldrecordings│╭[2]Results:publicdomainfieldrecordings───────────────────────────────────────────────────────────────────────────────────────────────────────╮│soulseek-rsv14.1.2││││Noresultsyet.││Pickasearchin[1]Searches,orstartanewone.│╭[1]Searches────────────────────╮╭[3]Downloads/Uploads──────────────────────────────────────────────╮╭[Info]─────────────────────────────────╮│StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││Selectaresultortransferfordetails││›Activepublic domain fi…│││││││││││╰──────────────────────────────────╯╰─────────────────────────────────────────────────────────────────────╯╰─────────────────────────────────────────╯│[Space→select][Enter→download][b→browseowner][c→chat][/→filter][a/A→selectall/none][h/l→scrollname][g/G→top/end]││[Tab/1-3→pane][z→zoom][w→hide][?→keys][q→quit]││✓FilenameSizeUserBitrateSpeedSlots││›[ ]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\01 - Coastal D30.5 MBloopdigger675 kbps3.35 MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\02-Reservoir22.6MBloopdigger569kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\03-Marshat48.0MBloopdigger920kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\04-TramDepo32.2MBloopdigger725kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourB52.5MBloopdigger902kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\06-RainonZ30.4MBloopdigger804kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\cover.jpg0.4MBloopdigger-3.35MB/s1││StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││01-CoastalDawn(SunriseTake,Long││›Activepublic domain fi7││││Version).flac││││││@@gufoo/FieldRecordings/CoastalDawn││││││Sessions(2019)[FLAC24-96]││││││Userloopdigger││││││Size30.5MB││││││Bitrate675kbps││││││Length6m52s││││││Freeslots1available││││││Uploadspeed3.4MB/s││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0117.4MBtapehiss836kbps2.1MB/s2││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0238.0MBtapehiss741kbps2.1MB/s2││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0334.6MBtapehiss753kbps2.1MB/s2││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0418.7MBtapehiss723kbps2.1MB/s2││›Activepublic domain fi11││││Version).flac││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\01-CoastalD30.5MBloopdigger675kbps3.35MB/s1││›[ ]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\02 - Reservoir22.6 MBloopdigger569 kbps3.35 MB/s1││StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││02-ReservoirWind.flac││›Activepublic domain fi11││││@@gufoo/FieldRecordings/CoastalDawn││││││Size22.6MB││││││Bitrate569kbps││││││Length5m18s││›[ ]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at48.0 MBloopdigger920 kbps3.35 MB/s1││StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││03-MarshatNight(Long).flac││││││Size48.0MB││││││Bitrate920kbps││││││Length8m21s││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-9.4MBnectoUA320kbps1.05MB/s0││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-5.1MBnectoUA320kbps1.05MB/s0││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-19.5MBnectoUA320kbps1.05MB/s0││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-0.1MBnectoUA-1.05MB/s0││›Activepublic domain fi15││││@@gufoo/FieldRecordings/CoastalDawn││›[✓]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at48.0 MBloopdigger920 kbps3.35 MB/s1││[✓]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\03-Marshat48.0MBloopdigger920kbps3.35MB/s1││›[ ]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\04 - Tram Depo32.2 MBloopdigger725 kbps3.35 MB/s1││StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││04-TramDepot,06h.flac││││││Size32.2MB││││││Bitrate725kbps││││││Length6m06s││›[ ]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\05 - Harbour B52.5 MBloopdigger902 kbps3.35 MB/s1││StatusQueryResults││Notransfers.Selectfilesin[2]ResultsandpressEnter.││05-HarbourBells.flac││││││Size52.5MB││││││Bitrate902kbps││││││Length9m33s││›[✓]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\05 - Harbour B52.5 MBloopdigger902 kbps3.35 MB/s1││›Donepublic domain fi15││││@@gufoo/FieldRecordings/CoastalDawn││soulseek-rs🦀v14.1.2Downloads:0active,0completed,0failed,2que││StatusQueryResults││StatusFilenameUserProgressSpeed││05-HarbourBells.flac││›Donepublic domain fi15││›⋯@@gufoo\FloopdiggerQueued-││@@gufoo/FieldRecordings/CoastalDawn││││⋯@@gufoo\FloopdiggerQueued-││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que2.8/100.5MB·18.61MB/s·[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]2%│╭[2]Results:publicdomainfieldrecordings───────────────────────────────────────────────────────────────────────────────────────────────────────╮│✓FilenameSizeUserBitrateSpeedSlots││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\01-CoastalD30.5MBloopdigger675kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\02-Reservoir22.6MBloopdigger569kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\03-Marshat48.0MBloopdigger920kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\04-TramDepo32.2MBloopdigger725kbps3.35MB/s1││›[ ]@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\05 - Harbour B52.5 MBloopdigger902 kbps3.35 MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\06-RainonZ30.4MBloopdigger804kbps3.35MB/s1││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\cover.jpg0.4MBloopdigger-3.35MB/s1││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0117.4MBtapehiss836kbps2.1MB/s2││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0238.0MBtapehiss741kbps2.1MB/s2││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0334.6MBtapehiss753kbps2.1MB/s2││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0418.7MBtapehiss723kbps2.1MB/s2││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-9.4MBnectoUA320kbps1.05MB/s0││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-5.1MBnectoUA320kbps1.05MB/s0││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-19.5MBnectoUA320kbps1.05MB/s0││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-0.1MBnectoUA-1.05MB/s0│╭[1]Searches────────────────────╮╭[3]Downloads/Uploads──────────────────────────────────────────────╮╭[Info]─────────────────────────────────╮│StatusQueryResults││StatusFilenameUserProgressSpeed││03-MarshatNight(Long).flac││›Donepublic domain fi15││›↯@@gufoo\Floopdigger1.3 MB/48.0 MB (2%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger1.5MB/52.5MB(2%)9.7MB/s││Sessions(2019)[FLAC24-96]││││││││││││Userloopdigger││││││Size48.0MB││││││StatusInprogress││││││Saveto││││││/home/demo/Music/Soulseek││││││Bitrate920kbps││││││Length8m21s│╰──────────────────────────────────╯╰─────────────────────────────────────────────────────────────────────╯╰─────────────────────────────────────────╯│[p→pause/resume][x→cancel][r→retryfailed][d→deletequeued/done][c→clearfinished][b→browseuser][Tab/1-3→pane][z→zoom]││[w→hide][?→keys][q→quit]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que5.6/100.5MB·18.61MB/s·[█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]5%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger2.7 MB/48.0 MB (5%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger2.9MB/52.5MB(5%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que11.2/100.5MB·18.61MB/s·[███░░░░░░░░░░░░░░░░░░░░░░░░░░░░]11%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger5.3 MB/48.0 MB (11%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger5.8MB/52.5MB(11%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que19.5/100.5MB·18.61MB/s·[██████░░░░░░░░░░░░░░░░░░░░░░░░░]19%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger9.3 MB/48.0 MB (19%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger10.2MB/52.5MB(19%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que25.1/100.5MB·18.61MB/s·[███████░░░░░░░░░░░░░░░░░░░░░░░░]25%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger12.0 MB/48.0 MB (25%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger13.1MB/52.5MB(25%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que30.7/100.5MB·18.61MB/s·[█████████░░░░░░░░░░░░░░░░░░░░░░]30%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger14.7 MB/48.0 MB (30%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger16.0MB/52.5MB(30%)9.7MB/s││Sessions(2019)[FLAC24-96]││›Donepublic domain fi15││›↯@@gufoo\Floopdigger17.3 MB/48.0 MB (36%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger20.4MB/52.5MB(38%)9.7MB/s││Sessions(2019)[FLAC24-96]││›Donepublic domain fi15││›↯@@gufoo\Floopdigger18.7 MB/48.0 MB (38%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↑↯HarbourBcrateminer5.3MB/41.0MB(12%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que41.9/100.5MB·18.61MB/s·[████████████░░░░░░░░░░░░░░░░░░░]41%│╭[3]Downloads/Uploads─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│StatusFilenameUserProgressSpeed││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger20.0 MB/48.0 MB (41%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger21.9MB/52.5MB(41%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer5.7MB/41.0MB(13%)3.7MB/s││[p→pause/resume][x→cancel][r→retryfailed][d→deletequeued/done][c→clearfinished][b→browseuser][Tab/1-3→pane][z→unzoom]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que50.2/100.5MB·18.61MB/s·[███████████████░░░░░░░░░░░░░░░░]50%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger24.0 MB/48.0 MB (50%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger26.2MB/52.5MB(50%)9.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que55.8/100.5MB·18.61MB/s·[█████████████████░░░░░░░░░░░░░░]55%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger26.7 MB/48.0 MB (55%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger29.2MB/52.5MB(55%)9.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que61.4/100.5MB·18.61MB/s·[██████████████████░░░░░░░░░░░░░]61%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger29.3 MB/48.0 MB (61%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger32.1MB/52.5MB(61%)9.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que67.0/100.5MB·18.61MB/s·[████████████████████░░░░░░░░░░░]66%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger32.0 MB/48.0 MB (66%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger35.0MB/52.5MB(66%)9.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que72.6/100.5MB·18.61MB/s·[██████████████████████░░░░░░░░░]72%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger34.7 MB/48.0 MB (72%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger37.9MB/52.5MB(72%)9.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que81.0/100.5MB·18.61MB/s·[████████████████████████░░░░░░░]80%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger38.7 MB/48.0 MB (80%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger42.3MB/52.5MB(80%)9.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que83.8/100.5MB·18.61MB/s·[█████████████████████████░░░░░░]83%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger40.0 MB/48.0 MB (83%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger43.8MB/52.5MB(83%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer14.0MB/41.0MB(34%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que89.3/100.5MB·18.61MB/s·[███████████████████████████░░░░]88%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger42.7 MB/48.0 MB (88%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger46.7MB/52.5MB(88%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que94.9/100.5MB·18.61MB/s·[█████████████████████████████░░]94%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger45.3 MB/48.0 MB (94%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger49.6MB/52.5MB(94%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que97.7/100.5MB·18.61MB/s·[██████████████████████████████░]97%││StatusQueryResults││StatusFilenameUserProgressSpeed││03-MarshatNight(Long).flac││›Donepublic domain fi15││›↯@@gufoo\Floopdigger46.7 MB/48.0 MB (97%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger51.0MB/52.5MB(97%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer17.0MB/41.0MB(41%)3.7MB/s││││││││Userloopdigger││││││Size48.0MB││││││StatusInprogress││││││Saveto││││││/home/demo/Music/Soulseek││││││Bitrate920kbps││││││Length8m21s││soulseek-rs🦀v14.1.2Downloads:0active,2completed,0failed,0que││›Donepublic domain fi15││›✓@@gufoo\FloopdiggerCompleted-││@@gufoo/FieldRecordings/CoastalDawn││││✓@@gufoo\FloopdiggerCompleted-││Sessions(2019)[FLAC24-96]││││││StatusCompleted││││↑↯HarbourBcrateminer19.9MB/41.0MB(48%)3.7MB/s│││╭[3]Downloads/Uploads─────────────────────────────────────────────────────────────────────╮╭[Info]──────────────────────────────────────────────╮│StatusFilenameUserProgressSpeed││05-HarbourBells.flac││›✓@@gufoo\Field Recordings\CoastalloopdiggerCompleted-││@@gufoo/FieldRecordings/CoastalDawnSessions││✓@@gufoo\FieldRecordings\CoastalloopdiggerCompleted-││(2019)[FLAC24-96]││↑↯HarbourBells(LiveattheQuay)crateminer20.3MB/41.0MB(49%)3.7MB/s││││││Userloopdigger││││Size52.5MB││││Bitrate902kbps││││Length9m33s││││││││Freeslots1available││││Uploadspeed3.4MB/s│╰────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────╯│↑↯HarbourBells(LiveattheQuay)crateminer27.6MB/41.0MB(67%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer29.2MB/41.0MB(71%)3.7MB/s││││[]…eldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\01-CoastalDawn(Sunr30.5MBloopdigger675kbps3.35MB/s1││[]…eldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\02-ReservoirWind.fla22.6MBloopdigger569kbps3.35MB/s1││[]…eldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\03-MarshatNight(Lo48.0MBloopdigger920kbps3.35MB/s1││[]…eldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\04-TramDepot,06h.fl32.2MBloopdigger725kbps3.35MB/s1││›[ ]…eld Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\05 - Harbour Bells.flac52.5 MBloopdigger902 kbps3.35 MB/s1││[]…eldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\06-RainonZincRoof.30.4MBloopdigger804kbps3.35MB/s1││[]…less\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\01-EmptyC17.4MBtapehiss836kbps2.1MB/s2││[]…less\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\02-Forest38.0MBtapehiss741kbps2.1MB/s2││[]…less\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\03-Quarry34.6MBtapehiss753kbps2.1MB/s2││[]…less\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\04-NightF18.7MBtapehiss723kbps2.1MB/s2││[]…mplete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-BY)\01-9.4MBnectoUA320kbps1.05MB/s0││[]…mplete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-BY)\02-5.1MBnectoUA320kbps1.05MB/s0││[]…mplete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-BY)\03-19.5MBnectoUA320kbps1.05MB/s0││[]…mplete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-BY)\readm0.1MBnectoUA-1.05MB/s0││↑↯HarbourBells(LiveattheQuay)crateminer35.3MB/41.0MB(86%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer38.2MB/41.0MB(93%)3.7MB/s││││││↑↯HarbourBcrateminer38.5MB/41.0MB(94%)3.7MB/s││││││↑✓HarbourBcrateminerCompleted-││││›[ ]@@gufoo\Field Recor╭Keys─────────────────────────────────────────────────────────────────────────────────────────╮bps3.35 MB/s1││[]@@gufoo\FieldRecor│PanesSearches│bps3.35MB/s1││[]@@gufoo\FieldRecor│Tab/Shift-Tabnext/previouspaneEntershowitsresults│3.35MB/s1││[]Music\lossless\Publ│123focusapane,hiddenornotdremovethesearch│bps2.1MB/s2││[]Music\lossless\Publ│zzoomthefocusedpaneCcleareverysearch│bps2.1MB/s2││[]Music\lossless\Publ│whidethefocusedpanecchatrooms│bps2.1MB/s2││[]Music\lossless\Publ│Escleavezoom│bps2.1MB/s2││[]@@hnttf\complete\Ex│clickfocusapaneResults│bps1.05MB/s0││[]@@hnttf\complete\Ex│Spaceselect/deselect│bps1.05MB/s0││[]@@hnttf\complete\Ex│Anywherea/Aselectall/none│bps1.05MB/s0││[]@@hnttf\complete\Ex│ssearchthenetworkEnterdownloadtheselection│1.05MB/s0│││bbrowseauser'sfiles/filter,Enterkeepsit,Escclears││││mcomposeaprivatemessagehl/←→scrollthefilename││││iinbox0/$start/endofthefilename││││osettingsbbrowsetheowner││╰─────────────────────────│?thislistcchatrooms│──────────────────────────╯╭[1]Searches───────────│qquit│──────────────────────────╮│StatusQuery│Downloads/Uploads│Night(Long).flac││›Donepublic domain fi│Anylistppause/resume│Recordings/CoastalDawn│││↑↓/kjmoveonerowxcancel│)[FLAC24-96]│││HomeEnd/gGfirst/lastrowrretryafailedone││││PgUpPgDn/^b^fonepageddeleteaqueuedorfinishedone│loopdigger│││^u^dhalfapageccleareveryfinishedone│48.0MB││╰───────────────────────────────────────────────────────────────────────────────────────────────╯Completed││[?/Esc→close]│╰────────────────────────────────────────────────────────────────╭[1]Searches────────────────────╮╭[3]Downloads/Uploads───│││││╭Shortcuts·Sharing:~/Music───────────────────────────────────────────────────────────────────────────│[s→search][Enter→results][d→remove][m→message][i→inbox][c→chat][b→browseuser][Tab/1-3→pane│StatusQueryResults╮╭[3]Downloads/Uploads──────────────────────────────────────────────╮╭[Info]─────────────────────────────────╮╭│││││╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│[Type→searchterm][←→→movecursor][Backspace/Del→edit][Enter→search]││search:p││search:pu││search:pub││search:publ││search:publi││search:publicd││search:publicdo││search:publicdom││search:publicdoma││search:publicdomai││search:publicdomainf││search:publicdomainfi││search:publicdomainfie││search:publicdomainfiel││search:publicdomainfieldr││search:publicdomainfieldre││search:publicdomainfieldrec││search:publicdomainfieldreco││search:publicdomainfieldrecor││search:publicdomainfieldrecord││search:publicdomainfieldrecordi││search:publicdomainfieldrecordin││search:publicdomainfieldrecording│╰────────────s────────────────────╮╭[3]Downloads/Uploads──────────────────────────────────────────────╮╭[Info]─────────────────────────────────╮│StatusQueryResults│││││││││││╰──────────────────────────────────╯╰──────────────────────││[Space→select][Enter→download]───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│[Tab/1-3→pane][z][←→→movecursor][Backspace/Del→edit][Enter→search][Esc→cancel]││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2││││││Length││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\041││›Activepublic domain fi11││││@@gufoo/FieldRecordings/CoastalDa││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-0││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0418.7MBtapehiss723kbps2.1MB/s2││›Donepublic domain fi15││›↯@@gufoo\FloopdiggerQueued-││@@gufoo/FieldRecordings/CoastalDawn││││││Length3.4MB/s││[p→pause/resume][x→cancel][r→retryfailed][d→deletequeued/done][c→clearfinished][one][h/l→scrollname][g/G→top/end]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que8.4/100.5MB·18.61MB/s·[██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]8%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger4.0 MB/48.0 MB (8%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger4.4MB/52.5MB(8%)9.7MB/s││Sessions(2019)[FLAC24-96]││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que14.0/100.5MB·18.61MB/s·[████░░░░░░░░░░░░░░░░░░░░░░░░░░░]13%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger6.7 MB/48.0 MB (13%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger7.3MB/52.5MB(13%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer0.2MB/41.0MB(0%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que16.8/100.5MB·18.61MB/s·[█████░░░░░░░░░░░░░░░░░░░░░░░░░░]16%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger8.0 MB/48.0 MB (16%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger8.8MB/52.5MB(16%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer0.6MB/41.0MB(1%)3.7MB/s││││││↑↯HarbourBcrateminer1.0MB/41.0MB(2%)3.7MB/s││││││↑↯HarbourBcrateminer1.4MB/41.0MB(3%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que22.3/100.5MB·18.61MB/s·[██████░░░░░░░░░░░░░░░░░░░░░░░░░]22%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger10.7 MB/48.0 MB (22%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger11.7MB/52.5MB(22%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer1.7MB/41.0MB(4%)3.7MB/s││││││↑↯HarbourBcrateminer2.1MB/41.0MB(5%)3.7MB/s││││││↑↯HarbourBcrateminer2.5MB/41.0MB(6%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que27.9/100.5MB·18.61MB/s·[████████░░░░░░░░░░░░░░░░░░░░░░░]27%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger13.3 MB/48.0 MB (27%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger14.6MB/52.5MB(27%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer2.9MB/41.0MB(7%)3.7MB/s││││││↑↯HarbourBcrateminer3.3MB/41.0MB(8%)3.7MB/s││││││↑↯HarbourBcrateminer3.7MB/41.0MB(9%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que33.5/100.5MB·18.61MB/s·[██████████░░░░░░░░░░░░░░░░░░░░░]33%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger16.0 MB/48.0 MB (33%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger17.5MB/52.5MB(33%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer4.1MB/41.0MB(9%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que36.3/100.5MB·18.61MB/s·[███████████░░░░░░░░░░░░░░░░░░░░]36%││││↯@@gufoo\Floopdigger19.0MB/52.5MB(36%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer4.5MB/41.0MB(10%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que37.7/100.5MB·18.61MB/s·[███████████░░░░░░░░░░░░░░░░░░░░]37%││││↑↯HarbourBcrateminer4.9MB/41.0MB(11%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que39.1/100.5MB·18.61MB/s·[████████████░░░░░░░░░░░░░░░░░░░]38%││›↯@@gufoo\ldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\01-CoastalD30.5MBloopdigger675kbps3.35MB/s1││↑↯HarbourBells(LiveattheQuay).flaccrateminer5.7MB/41.0MB(13%)3.7MB/s││FieldRecordings,Vol.2(CreativeCommonsZero)\0117.4MBtapehiss836kbps2.1MB/s2││dings2004(netlabel,CC-9.4MBnectoUA320kbps1.05MB/s0││─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯│││↑↯HarbourBcrateminer5.3MB/41.0MB(12%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay).flaccrateminer6.0MB/41.0MB(14%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que44.7/100.5MB·18.61MB/s·[█████████████░░░░░░░░░░░░░░░░░░]44%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger21.3 MB/48.0 MB (44%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger23.3MB/52.5MB(44%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer6.4MB/41.0MB(15%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que47.5/100.5MB·18.61MB/s·[██████████████░░░░░░░░░░░░░░░░░]47%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger22.7 MB/48.0 MB (47%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger24.8MB/52.5MB(47%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer6.8MB/41.0MB(16%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer7.1MB/41.0MB(17%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer7.5MB/41.0MB(18%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que53.0/100.5MB·18.61MB/s·[████████████████░░░░░░░░░░░░░░░]52%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger25.3 MB/48.0 MB (52%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger27.7MB/52.5MB(52%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer7.9MB/41.0MB(19%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer8.3MB/41.0MB(20%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer8.7MB/41.0MB(21%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que58.6/100.5MB·18.61MB/s·[██████████████████░░░░░░░░░░░░░]58%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger28.0 MB/48.0 MB (58%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger30.6MB/52.5MB(58%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer9.1MB/41.0MB(22%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer9.4MB/41.0MB(23%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer9.8MB/41.0MB(23%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que64.2/100.5MB·18.61MB/s·[███████████████████░░░░░░░░░░░░]63%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger30.7 MB/48.0 MB (63%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger33.5MB/52.5MB(63%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer10.2MB/41.0MB(24%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer10.6MB/41.0MB(25%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer11.0MB/41.0MB(26%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que69.8/100.5MB·18.61MB/s·[█████████████████████░░░░░░░░░░]69%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger33.3 MB/48.0 MB (69%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger36.5MB/52.5MB(69%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer11.4MB/41.0MB(27%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer11.7MB/41.0MB(28%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer12.1MB/41.0MB(29%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que75.4/100.5MB·18.61MB/s·[███████████████████████░░░░░░░░]75%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger36.0 MB/48.0 MB (75%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger39.4MB/52.5MB(75%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer12.5MB/41.0MB(30%)3.7MB/s││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que78.2/100.5MB·18.61MB/s·[████████████████████████░░░░░░░]77%││›↯@@gufoo\Field Recordings\Coastal Dawn Sessions (2019) [FLAC 24-96]\03 - Marsh at Night (loopdigger37.3 MB/48.0 MB (77%)8.9 MB/s││↯@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\05-HarbourBells.flloopdigger40.8MB/52.5MB(77%)9.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer12.9MB/41.0MB(31%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer13.3MB/41.0MB(32%)3.7MB/s││↑↯HarbourBells(LiveattheQuay).flaccrateminer13.7MB/41.0MB(33%)3.7MB/s││[]@@gufoo\FieldRecordings\CoastalDawnSessions(2019)[FLAC24-96]\06-RainonZ││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommonsZero)\0238.0MBtapehiss741kbps││[]@@hnttf\compl│╰──────────────────────────────││StatusQueryResults││││││││↑↯HarbourBcrateminer14.2MB/41.0MB(34%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que86.5/100.5MB·18.61MB/s·[██████████████████████████░░░░░]86%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger41.3 MB/48.0 MB (86%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger45.2MB/52.5MB(86%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer14.6MB/41.0MB(35%)3.7MB/s││││││↑↯HarbourBcrateminer15.0MB/41.0MB(36%)3.7MB/s││││││↑↯HarbourBcrateminer15.4MB/41.0MB(37%)3.7MB/s││││soulseek-rs🦀v14.1.2Downloads:2active,0completed,0failed,0que92.1/100.5MB·18.61MB/s·[████████████████████████████░░░]91%││›Donepublic domain fi15││›↯@@gufoo\Floopdigger44.0 MB/48.0 MB (91%)8.9 MB/s││@@gufoo/FieldRecordings/CoastalDawn││││↯@@gufoo\Floopdigger48.1MB/52.5MB(91%)9.7MB/s││Sessions(2019)[FLAC24-96]││││↑↯HarbourBcrateminer15.8MB/41.0MB(38%)3.7MB/s││││││↑↯HarbourBcrateminer16.2MB/41.0MB(39%)3.7MB/s││││││↑↯HarbourBcrateminer16.6MB/41.0MB(40%)3.7MB/s││││││↑↯HarbourBcrateminer16.6MB/41.0MB(40%)3.7MB/s│││╰──────────────────────────────────╯╰─────────────────────────────────────────────────────────────────────╯╰─────────────────────────────────────────╯│[s→search][Enter→results][d→remove][m→message][i→inbox]done][c→clearfinished][b→browseuser][Tab/1-3→pane][z→zoom]││││↑↯HarbourBcrateminer17.2MB/41.0MB(41%)3.7MB/s││││││↑↯HarbourBcrateminer17.6MB/41.0MB(42%)3.7MB/s││││││↑↯HarbourBcrateminer18.0MB/41.0MB(43%)3.7MB/s││││││↑↯HarbourBcrateminer18.4MB/41.0MB(44%)3.7MB/s││││││↑↯HarbourBcrateminer18.8MB/41.0MB(45%)3.7MB/s││││││↑↯HarbourBcrateminer19.2MB/41.0MB(46%)3.7MB/s││││││↑↯HarbourBcrateminer19.5MB/41.0MB(47%)3.7MB/s│││╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯│StatusFilenameUserProgressSpeed││05d││03-MarshatNight(Long).flac││✓@@gufoo\FieldRecordings\CoastalloopdiggerCompleted-││(2019)[FLAC24-9ions(2019)[FLAC24-96]││││StatusCompleted││││Uploadspeed8m21s││[Space→select][Enter→download][b→browseowner][c[i→inbox][c→chat][b→browseuser][Tab/1-3→pane][z→zoom][w→hide]││[Tab/1-3→pane][z→zoom][w→hide][?→││↑↯HarbourBells(LiveattheQuay)crateminer20.6MB/41.0MB(50%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer21.0MB/41.0MB(51%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer21.4MB/41.0MB(52%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer21.8MB/41.0MB(53%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer22.2MB/41.0MB(54%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer22.5MB/41.0MB(54%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer22.9MB/41.0MB(55%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer23.3MB/41.0MB(56%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer23.7MB/41.0MB(57%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer24.1MB/41.0MB(58%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer24.5MB/41.0MB(59%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer24.9MB/41.0MB(60%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer25.3MB/41.0MB(61%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer25.7MB/41.0MB(62%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer26.1MB/41.0MB(63%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer26.5MB/41.0MB(64%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer26.8MB/41.0MB(65%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer27.2MB/41.0MB(66%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer28.0MB/41.0MB(68%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer28.4MB/41.0MB(69%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer28.8MB/41.0MB(70%)3.7MB/s││││[]…less\PublicDomainFieldRecordings,Vol.2(CreativeCreativeCommonsZero)\0334.6MBtapehiss753kbps2.1MB/s2││↑↯HarbourBells(LiveattheQuay)crateminer29.6MB/41.0MB(72%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer29.8MB/41.0MB(72%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer30.2MB/41.0MB(73%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer30.6MB/41.0MB(74%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer31.0MB/41.0MB(75%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer31.4MB/41.0MB(76%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer31.8MB/41.0MB(77%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer32.2MB/41.0MB(78%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer32.6MB/41.0MB(79%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer33.0MB/41.0MB(80%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer33.4MB/41.0MB(81%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer33.8MB/41.0MB(82%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer34.1MB/41.0MB(83%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer34.5MB/41.0MB(84%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer34.9MB/41.0MB(85%)3.7MB/s││││[]Music\lossless\PublicDomainFieldRecordings,Vol.2(CreativeCommons-Quarry34.6MBtapehiss753kbps2.1MB/s2││↑↯HarbourBells(LiveattheQuay)crateminer35.7MB/41.0MB(87%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer35.8MB/41.0MB(87%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer36.2MB/41.0MB(88%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer36.6MB/41.0MB(89%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer37.0MB/41.0MB(90%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer37.4MB/41.0MB(91%)3.7MB/s││││↑↯HarbourBells(LiveattheQuay)crateminer37.8MB/41.0MB(92%)3.7MB/s│││╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯│││││Userger││[sace→select][Enter→download][b→browseowner][c→chat][/→filter][a/A→selectall/none][h/l→scrollname][g/G→top/end]││[s→search][Enter→results][d→remove][m→message][i→inbox][c→chat][b→browseuser][Tab/1-3→pane][zame][g/G→top/end]││││↑↯HarbourBcrateminer38.8MB/41.0MB(94%)3.7MB/s││││││↑↯HarbourBcrateminer39.2MB/41.0MB(95%)3.7MB/s││││││↑↯HarbourBcrateminer39.6MB/41.0MB(96%)3.7MB/s││││││↑↯HarbourBcrateminer40.0MB/41.0MB(97%)3.7MB/s││││││↑↯HarbourBcrateminer40.4MB/41.0MB(98%)3.7MB/s││││││↑↯HarbourBcrateminer40.7MB/41.0MB(99%)3.7MB/s││││[]@@hnttf\complete\Ex│clickfocusapaneResults320kbps1.05MB/s0│││m│╰─────────────────────────│?thislist──────────────────────────────╮╭[Info]─────────────────────────────────╮│›││✓@@gufoo\FloopdiggerCompleted-││Sessions(2019)[FLAC24-96]│││HomeEnd/gGfirst/lastrowrretryafailedone│loopdigger││││││Savetoo/Music/Soulseek│╭Shortcuts·Sharing:~/Music────────────────────────────────────────────────────────────────────────────────────e][z→zoom][w→hide]││[]Music\lossless\PublicDomainzoomthefocusedpaneCcleareverysearch│bps2.1MB/s2││[]@@hnttf\complete\ExplorersoftheInterior-FieldRecordings2004(netlabel,CC-5.1MBnectoUA│bps1.05MB/s0│╰────────────────────────────────────────────────────────bbrowsetheowner│││listppause/resume│Recordings/CoastalDawn│││n/^b^fonepageddeleteaqueuedorfinishedone│loopdigger│╰────────││││││[s→search][Enter→results][d→remove][m────────────────────────────────────────────────────────────────────────────────────────────────────╮│[?→keys][qe]│ \ No newline at end of file diff --git a/soulseek-rs/src/models/app_state.rs b/soulseek-rs/src/models/app_state.rs index 8cf44f2..c0b6061 100644 --- a/soulseek-rs/src/models/app_state.rs +++ b/soulseek-rs/src/models/app_state.rs @@ -39,6 +39,75 @@ pub enum FocusedPane { Downloads, } +impl FocusedPane { + /// Every focusable pane, in the order `Tab` walks them: the number each + /// carries in its legend. + pub const ALL: [Self; 3] = [Self::Searches, Self::Results, Self::Downloads]; + + const fn index(self) -> usize { + match self { + Self::Searches => 0, + Self::Results => 1, + Self::Downloads => 2, + } + } +} + +/// Which panes are on screen. A hidden pane gives its space to the others; a +/// zoomed one takes the whole content area for itself, so `Tab` through the +/// panes while zoomed reads as switching windows. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct PaneLayout { + hidden: [bool; 3], + pub zoomed: bool, +} + +impl PaneLayout { + #[must_use] + pub const fn is_visible(&self, pane: FocusedPane) -> bool { + !self.hidden[pane.index()] + } + + /// The panes still on screen, in `Tab` order. + pub fn visible(&self) -> impl Iterator + '_ { + FocusedPane::ALL + .into_iter() + .filter(move |pane| self.is_visible(*pane)) + } + + /// Take `pane` off the screen. Refuses when it is the last one showing — + /// an empty window has nothing to press a key in — and says so. + pub fn hide(&mut self, pane: FocusedPane) -> bool { + if self.visible().count() <= 1 { + return false; + } + self.hidden[pane.index()] = true; + true + } + + pub const fn show(&mut self, pane: FocusedPane) { + self.hidden[pane.index()] = false; + } + + /// The visible pane after (or before) `from`, wrapping around. `from` + /// itself may already be hidden, which is how focus leaves a pane that + /// was just hidden. + #[must_use] + pub fn neighbour(&self, from: FocusedPane, forward: bool) -> FocusedPane { + let count = FocusedPane::ALL.len(); + let step = if forward { 1 } else { count - 1 }; + let mut index = from.index(); + for _ in 0..count { + index = (index + step) % count; + let candidate = FocusedPane::ALL[index]; + if self.is_visible(candidate) { + return candidate; + } + } + from + } +} + /// What the shared command bar is currently capturing input for. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CommandBarMode { @@ -92,6 +161,12 @@ pub struct AppState { // UI State pub focused_pane: FocusedPane, + pub layout: PaneLayout, + /// The keys overlay (`?`) is open. + pub show_help: bool, + /// Rows the keys overlay is scrolled down, for a terminal too short to + /// show it whole. The renderer clamps it to what there is to scroll. + pub help_scroll: usize, pub should_exit: bool, pub command_bar_active: bool, pub command_bar_input: String, @@ -164,6 +239,9 @@ impl AppState { active_downloads_count: 0, focused_pane: FocusedPane::Searches, + layout: PaneLayout::default(), + show_help: false, + help_scroll: 0, should_exit: false, command_bar_active: false, command_bar_input: String::new(), @@ -195,6 +273,31 @@ impl AppState { } } + /// Put the focus on `pane`, bringing it back on screen if it was hidden: + /// pressing a pane's number is also how it comes back. + pub const fn focus_pane(&mut self, pane: FocusedPane) { + self.layout.show(pane); + self.focused_pane = pane; + } + + /// Move the focus to the next (or previous) visible pane. + pub fn cycle_focus(&mut self, forward: bool) { + self.focused_pane = self.layout.neighbour(self.focused_pane, forward); + } + + /// Hide the focused pane and move the focus to its neighbour. The last + /// visible pane stays; there would be nowhere for the focus to go. + pub fn hide_focused_pane(&mut self) { + let pane = self.focused_pane; + if self.layout.hide(pane) { + self.focused_pane = self.layout.neighbour(pane, true); + } + } + + pub const fn toggle_zoom(&mut self) { + self.layout.zoomed = !self.layout.zoomed; + } + /// Open the chat popup on a conversation with `peer`, ready to type. pub fn open_chat(&mut self, peer: String) { self.chat_peer = Some(peer); @@ -290,6 +393,99 @@ mod tests { assert_eq!(state.active_chat_peer(), Some("bob")); } + fn visible(layout: &PaneLayout) -> Vec { + layout.visible().collect() + } + + #[test] + fn every_pane_starts_visible_and_unzoomed() { + let state = AppState::new(); + assert_eq!(visible(&state.layout), FocusedPane::ALL); + assert!(!state.layout.zoomed); + assert!(!state.show_help); + } + + #[test] + fn tab_cycles_the_focus_through_the_visible_panes_and_wraps() { + let mut state = AppState::new(); + assert_eq!(state.focused_pane, FocusedPane::Searches); + state.cycle_focus(true); + assert_eq!(state.focused_pane, FocusedPane::Results); + state.cycle_focus(true); + assert_eq!(state.focused_pane, FocusedPane::Downloads); + state.cycle_focus(true); + assert_eq!(state.focused_pane, FocusedPane::Searches, "wraps"); + state.cycle_focus(false); + assert_eq!(state.focused_pane, FocusedPane::Downloads, "and back"); + } + + #[test] + fn hiding_the_focused_pane_moves_the_focus_past_it() { + let mut state = AppState::new(); + state.focus_pane(FocusedPane::Results); + state.hide_focused_pane(); + assert_eq!( + visible(&state.layout), + [FocusedPane::Searches, FocusedPane::Downloads] + ); + assert_eq!(state.focused_pane, FocusedPane::Downloads); + + // Tab now skips the hidden pane in both directions. + state.cycle_focus(true); + assert_eq!(state.focused_pane, FocusedPane::Searches); + state.cycle_focus(false); + assert_eq!(state.focused_pane, FocusedPane::Downloads); + } + + #[test] + fn hiding_wraps_the_focus_to_the_first_pane() { + let mut state = AppState::new(); + state.focus_pane(FocusedPane::Downloads); + state.hide_focused_pane(); + assert_eq!(state.focused_pane, FocusedPane::Searches); + } + + #[test] + fn the_last_visible_pane_cannot_be_hidden() { + let mut state = AppState::new(); + state.hide_focused_pane(); + state.hide_focused_pane(); + assert_eq!(visible(&state.layout), [FocusedPane::Downloads]); + state.hide_focused_pane(); + assert_eq!( + visible(&state.layout), + [FocusedPane::Downloads], + "one pane always stays on screen" + ); + assert_eq!(state.focused_pane, FocusedPane::Downloads); + } + + #[test] + fn focusing_a_hidden_pane_by_number_brings_it_back() { + let mut state = AppState::new(); + state.focus_pane(FocusedPane::Results); + state.hide_focused_pane(); + assert!(!state.layout.is_visible(FocusedPane::Results)); + + state.focus_pane(FocusedPane::Results); + + assert!(state.layout.is_visible(FocusedPane::Results)); + assert_eq!(state.focused_pane, FocusedPane::Results); + } + + #[test] + fn zoom_toggles_and_leaves_hidden_panes_alone() { + let mut state = AppState::new(); + state.focus_pane(FocusedPane::Searches); + state.hide_focused_pane(); + state.toggle_zoom(); + assert!(state.layout.zoomed); + assert!(!state.layout.is_visible(FocusedPane::Searches)); + state.toggle_zoom(); + assert!(!state.layout.zoomed); + assert!(!state.layout.is_visible(FocusedPane::Searches)); + } + #[test] fn opening_a_chat_pins_a_peer_with_no_messages_yet() { let mut state = AppState::new(); diff --git a/soulseek-rs/src/models/mod.rs b/soulseek-rs/src/models/mod.rs index f62e346..554639d 100644 --- a/soulseek-rs/src/models/mod.rs +++ b/soulseek-rs/src/models/mod.rs @@ -6,7 +6,7 @@ mod settings; pub use app_state::{ AppState, ChatMessage, CommandBarMode, DownloadEntry, FocusedPane, - MessageDirection, SearchEntry, SearchStatus, + MessageDirection, PaneLayout, SearchEntry, SearchStatus, }; pub use browse::{ BrowseState, BrowseStatus, BrowseTabs, files_under, find_node, diff --git a/soulseek-rs/src/ui/main_tui/input.rs b/soulseek-rs/src/ui/main_tui/input.rs index 444f8b2..4d7871d 100644 --- a/soulseek-rs/src/ui/main_tui/input.rs +++ b/soulseek-rs/src/ui/main_tui/input.rs @@ -9,6 +9,8 @@ use ratatui::layout::Position; use ratatui::widgets::TableState; const NAME_SCROLL_STEP: isize = 8; +/// Rows a page key moves the keys overlay, which has no table to measure. +const HELP_PAGE: usize = 10; impl MainTui { pub(super) fn handle_key_event(&mut self, key: KeyEvent) { @@ -17,6 +19,27 @@ impl MainTui { return self.handle_command_bar_input(key); } + // The keys list closes on the key that opened it, or the usual two, + // and scrolls like any list for a terminal too short to show it all. + if self.state.show_help { + let scroll = &mut self.state.help_scroll; + match key.code { + KeyCode::Char('?' | 'q') | KeyCode::Esc => { + self.state.show_help = false; + } + KeyCode::Down | KeyCode::Char('j') => *scroll += 1, + KeyCode::Up | KeyCode::Char('k') => { + *scroll = scroll.saturating_sub(1); + } + KeyCode::PageDown => *scroll += HELP_PAGE, + KeyCode::PageUp => *scroll = scroll.saturating_sub(HELP_PAGE), + KeyCode::Home | KeyCode::Char('g') => *scroll = 0, + KeyCode::End | KeyCode::Char('G') => *scroll = usize::MAX, + _ => {} + } + return; + } + // Chat popup takes over navigation while open. if self.state.show_messages { return self.handle_chat_input(key); @@ -33,10 +56,10 @@ impl MainTui { return self.handle_settings_input(key); } + // Control combinations only ever page a list, so they never reach + // the letter keys below: ctrl-d is not d. if key.modifiers.contains(KeyModifiers::CONTROL) { - if self.state.focused_pane == FocusedPane::Results { - self.handle_results_input(key); - } + self.navigate_focused_list(key); return; } @@ -53,16 +76,41 @@ impl MainTui { self.state.should_exit = true; return; } + KeyCode::Char('?') => { + self.state.show_help = true; + self.state.help_scroll = 0; + return; + } KeyCode::Char('1') => { - self.state.focused_pane = FocusedPane::Searches; + self.state.focus_pane(FocusedPane::Searches); return; } KeyCode::Char('2') => { - self.state.focused_pane = FocusedPane::Results; + self.state.focus_pane(FocusedPane::Results); return; } KeyCode::Char('3') => { - self.state.focused_pane = FocusedPane::Downloads; + self.state.focus_pane(FocusedPane::Downloads); + return; + } + KeyCode::Tab => { + self.state.cycle_focus(true); + return; + } + KeyCode::BackTab => { + self.state.cycle_focus(false); + return; + } + KeyCode::Char('z') => { + self.state.toggle_zoom(); + return; + } + KeyCode::Char('w') => { + self.state.hide_focused_pane(); + return; + } + KeyCode::Esc if self.state.layout.zoomed => { + self.state.layout.zoomed = false; return; } KeyCode::Char('s') => { @@ -306,15 +354,45 @@ impl MainTui { } } + /// The list the focus is on and how far one page moves it. + fn navigate_focused_list(&mut self, key: KeyEvent) -> bool { + let focused = self.state.focused_pane; + let page = self.page_size(focused); + let (table, len) = match focused { + FocusedPane::Searches => ( + &mut self.state.searches_table_state, + self.state.searches.len(), + ), + FocusedPane::Results => { + let len = self.visible_results().len(); + (&mut self.state.results_table_state, len) + } + FocusedPane::Downloads => ( + &mut self.state.downloads_table_state, + self.state.downloads.len() + self.state.uploads.len(), + ), + }; + navigate_list(key, table, len, page) + } + + /// Rows a pane shows at once: its inner height less the table header. + fn page_size(&self, pane: FocusedPane) -> isize { + let area = match pane { + FocusedPane::Searches => self.state.searches_pane_area, + FocusedPane::Results => self.state.results_pane_area, + FocusedPane::Downloads => self.state.downloads_pane_area, + }; + let rows = area.map_or(1, |area| { + pane_block(false).inner(area).height.saturating_sub(1) + }); + isize::try_from(rows.max(1)).unwrap_or(isize::MAX) + } + fn handle_searches_input(&mut self, key: KeyEvent) { - let rows = self.state.searches.len(); + if self.navigate_focused_list(key) { + return; + } match key.code { - KeyCode::Up | KeyCode::Char('k') => { - cycle(&mut self.state.searches_table_state, rows, false); - } - KeyCode::Down | KeyCode::Char('j') => { - cycle(&mut self.state.searches_table_state, rows, true); - } KeyCode::Enter => { if let Some(selected) = self.state.searches_table_state.selected() @@ -329,7 +407,7 @@ impl MainTui { self.state.results_selected_indices.clear(); self.state.results_name_offset = 0; self.state.results_table_state.select(Some(0)); - self.state.focused_pane = FocusedPane::Results; + self.state.focus_pane(FocusedPane::Results); } } } @@ -348,31 +426,10 @@ impl MainTui { } fn handle_results_input(&mut self, key: KeyEvent) { - let ctrl = key.modifiers.contains(KeyModifiers::CONTROL); - if ctrl && !matches!(key.code, KeyCode::Char('f' | 'b' | 'd' | 'u')) { + if self.navigate_focused_list(key) { return; } - let items_count = self.visible_results().len(); - let page = self.results_page_size(); - let half = (page / 2).max(1); - match key.code { - KeyCode::Up | KeyCode::Char('k') => { - cycle(&mut self.state.results_table_state, items_count, false); - } - KeyCode::Down | KeyCode::Char('j') => { - cycle(&mut self.state.results_table_state, items_count, true); - } - KeyCode::Home | KeyCode::Char('g') => self.select_row(0), - KeyCode::End | KeyCode::Char('G') => { - self.select_row(items_count.saturating_sub(1)); - } - KeyCode::PageDown => self.scroll_rows(page), - KeyCode::PageUp => self.scroll_rows(-page), - KeyCode::Char('f') if ctrl => self.scroll_rows(page), - KeyCode::Char('b') if ctrl => self.scroll_rows(-page), - KeyCode::Char('d') if ctrl => self.scroll_rows(half), - KeyCode::Char('u') if ctrl => self.scroll_rows(-half), KeyCode::Right | KeyCode::Char('l') => { self.scroll_names(NAME_SCROLL_STEP); } @@ -418,20 +475,6 @@ impl MainTui { } } - fn select_row(&mut self, target: usize) { - let len = self.visible_results().len(); - if len > 0 { - self.state - .results_table_state - .select(Some(target.min(len - 1))); - } - } - - fn scroll_rows(&mut self, delta: isize) { - let current = self.state.results_table_state.selected().unwrap_or(0); - self.select_row(current.saturating_add_signed(delta)); - } - fn scroll_names(&mut self, delta: isize) { let end = self.highlighted_name_end(); let current = self.state.results_name_offset.min(end); @@ -447,23 +490,12 @@ impl MainTui { .map_or(0, |file| name_end_offset(&file.filename, area)) } - fn results_page_size(&self) -> isize { - let rows = self.state.results_pane_area.map_or(1, |area| { - pane_block(false).inner(area).height.saturating_sub(1) - }); - rows.max(1) as isize - } - fn handle_downloads_input(&mut self, key: KeyEvent) { // The pane lists downloads first, then uploads; navigation spans both. - let rows = self.state.downloads.len() + self.state.uploads.len(); + if self.navigate_focused_list(key) { + return; + } match key.code { - KeyCode::Up | KeyCode::Char('k') => { - cycle(&mut self.state.downloads_table_state, rows, false); - } - KeyCode::Down | KeyCode::Char('j') => { - cycle(&mut self.state.downloads_table_state, rows, true); - } KeyCode::Char('x') => { self.cancel_selected_transfer(); } @@ -494,8 +526,12 @@ impl MainTui { (self.state.results_pane_area, FocusedPane::Results), (self.state.downloads_pane_area, FocusedPane::Downloads), ]; + // The areas are from the last draw. A pane hidden since then, in the + // same batch of events, still has one, and is not there to click. for (area, pane) in panes { - if area.is_some_and(|area| area.contains(clicked)) { + if area.is_some_and(|area| area.contains(clicked)) + && self.state.layout.is_visible(pane) + { self.state.focused_pane = pane; return; } @@ -503,6 +539,37 @@ impl MainTui { } } +/// The keys every list answers alike: a row at a time wrapping at either +/// end, a page or half a page at a time stopping there, and the two ends. +/// Says whether `key` was one of them. +fn navigate_list( + key: KeyEvent, + table: &mut TableState, + len: usize, + page: isize, +) -> bool { + let ctrl = key.modifiers.contains(KeyModifiers::CONTROL); + let half = (page / 2).max(1); + match key.code { + KeyCode::Up | KeyCode::Char('k') if !ctrl => cycle(table, len, false), + KeyCode::Down | KeyCode::Char('j') if !ctrl => cycle(table, len, true), + KeyCode::Home | KeyCode::Char('g') if !ctrl => { + select_row(table, len, 0); + } + KeyCode::End | KeyCode::Char('G') if !ctrl => { + select_row(table, len, len.saturating_sub(1)); + } + KeyCode::PageDown => scroll_rows(table, len, page), + KeyCode::PageUp => scroll_rows(table, len, -page), + KeyCode::Char('f') if ctrl => scroll_rows(table, len, page), + KeyCode::Char('b') if ctrl => scroll_rows(table, len, -page), + KeyCode::Char('d') if ctrl => scroll_rows(table, len, half), + KeyCode::Char('u') if ctrl => scroll_rows(table, len, -half), + _ => return false, + } + true +} + /// Move a table's selection one row, wrapping at either end. fn cycle(table: &mut TableState, len: usize, forward: bool) { if len == 0 { @@ -516,3 +583,16 @@ fn cycle(table: &mut TableState, len: usize, forward: bool) { }; table.select(Some(next)); } + +/// Put the selection on `target`, or the last row when that is past the end. +fn select_row(table: &mut TableState, len: usize, target: usize) { + if len > 0 { + table.select(Some(target.min(len - 1))); + } +} + +/// Move the selection `delta` rows, stopping at either end. +fn scroll_rows(table: &mut TableState, len: usize, delta: isize) { + let current = table.selected().unwrap_or(0); + select_row(table, len, current.saturating_add_signed(delta)); +} diff --git a/soulseek-rs/src/ui/main_tui/mod.rs b/soulseek-rs/src/ui/main_tui/mod.rs index ea8389c..65de6ad 100644 --- a/soulseek-rs/src/ui/main_tui/mod.rs +++ b/soulseek-rs/src/ui/main_tui/mod.rs @@ -1341,13 +1341,320 @@ mod tests { assert!(tui.state.results_selected_indices.is_empty()); } - fn screen_of(tui: &mut MainTui) -> String { - let backend = ratatui::backend::TestBackend::new(160, 40); + fn screen_sized(tui: &mut MainTui, width: u16, height: u16) -> String { + let backend = ratatui::backend::TestBackend::new(width, height); let mut terminal = ratatui::Terminal::new(backend).expect("backend"); terminal.draw(|frame| tui.render(frame)).expect("draw"); terminal.backend().to_string() } + fn screen_of(tui: &mut MainTui) -> String { + screen_sized(tui, 160, 40) + } + + /// The screen row carrying a pane's legend, `[2] Results` say, without + /// the quotes the test backend wraps each row in. + fn row_with<'a>(screen: &'a str, needle: &str) -> &'a str { + let line = screen + .lines() + .find(|line| line.contains(needle)) + .unwrap_or_else(|| panic!("no row with {needle:?} in\n{screen}")); + let start = line.find('"').map_or(0, |i| i + 1); + let end = line.rfind('"').unwrap_or(line.len()); + &line[start..end] + } + + /// A window with a search, its results, and a transfer, so every pane + /// has something to show. + fn furnished_tui() -> MainTui { + let mut tui = results_tui(3); + tui.state.searches.push(crate::models::SearchEntry { + query: "aphex twin".to_string(), + status: SearchStatus::Completed, + results: results(3), + known_files: 3, + owned: true, + start_time: std::time::Instant::now(), + cancel_flag: Arc::new(std::sync::atomic::AtomicBool::new(false)), + }); + tui.state.selected_search_index = Some(0); + tui.state.downloads.push(crate::models::DownloadEntry { + download: queued("bob", "@@x\\Music\\00.mp3"), + receiver: None, + }); + tui + } + + #[test] + fn the_results_pane_has_the_whole_width_to_itself() { + let mut tui = furnished_tui(); + let screen = screen_of(&mut tui); + + let results = row_with(&screen, "[2] Results"); + assert!(results.starts_with('╭'), "{results}"); + assert!(results.trim_end().ends_with('╮'), "{results}"); + assert!(!results.contains("[1]"), "nothing beside it: {results}"); + + // The other three share the row underneath, in this order. + let row = row_with(&screen, "[1] Searches"); + let searches = row.find("[1]").expect("searches"); + let downloads = row.find("[3]").expect("downloads"); + let info = row.find("[Info]").expect("info"); + assert!(searches < downloads && downloads < info, "{row}"); + } + + #[test] + fn tab_and_shift_tab_walk_the_panes_in_legend_order() { + let mut tui = furnished_tui(); + tui.state.focused_pane = FocusedPane::Searches; + press(&mut tui, KeyCode::Tab); + assert_eq!(tui.state.focused_pane, FocusedPane::Results); + press(&mut tui, KeyCode::Tab); + assert_eq!(tui.state.focused_pane, FocusedPane::Downloads); + press(&mut tui, KeyCode::Tab); + assert_eq!(tui.state.focused_pane, FocusedPane::Searches); + press(&mut tui, KeyCode::BackTab); + assert_eq!(tui.state.focused_pane, FocusedPane::Downloads); + } + + #[test] + fn w_hides_the_focused_pane_and_its_number_brings_it_back() { + let mut tui = furnished_tui(); + tui.state.focused_pane = FocusedPane::Searches; + + press(&mut tui, KeyCode::Char('w')); + + let screen = screen_of(&mut tui); + assert!(!screen.contains("[1] Searches"), "{screen}"); + assert!(screen.contains("[2] Results"), "{screen}"); + assert!(screen.contains("[3] Downloads"), "{screen}"); + assert_eq!(tui.state.focused_pane, FocusedPane::Results); + assert_eq!(tui.state.searches_pane_area, None, "nothing to click"); + // The transfers widen into the space. + let row = row_with(&screen, "[3] Downloads"); + assert!(row.starts_with('╭'), "{row}"); + + press(&mut tui, KeyCode::Char('1')); + + let screen = screen_of(&mut tui); + assert!(screen.contains("[1] Searches"), "{screen}"); + assert_eq!(tui.state.focused_pane, FocusedPane::Searches); + } + + #[test] + fn hiding_the_results_gives_the_row_the_whole_height() { + let mut tui = furnished_tui(); + press(&mut tui, KeyCode::Char('w')); + let screen = screen_of(&mut tui); + assert!(!screen.contains("[2] Results"), "{screen}"); + // The row now starts right under the status bar. + let status = screen + .lines() + .position(|line| line.contains("Status")) + .expect("status bar"); + let row = screen + .lines() + .position(|line| line.contains("[1] Searches")) + .expect("searches"); + assert_eq!(row, status + 3, "{screen}"); + } + + #[test] + fn the_last_pane_standing_cannot_be_hidden() { + // Results goes, then Downloads; Searches is what is left, and a + // third press leaves it there. + let mut tui = furnished_tui(); + press(&mut tui, KeyCode::Char('w')); + press(&mut tui, KeyCode::Char('w')); + press(&mut tui, KeyCode::Char('w')); + let screen = screen_of(&mut tui); + assert!(screen.contains("[1] Searches"), "{screen}"); + assert!(!screen.contains("[3] Downloads"), "{screen}"); + assert_eq!(tui.state.focused_pane, FocusedPane::Searches); + } + + #[test] + fn z_zooms_the_focused_pane_and_esc_or_z_leaves_it() { + let mut tui = furnished_tui(); + press(&mut tui, KeyCode::Char('z')); + + let screen = screen_of(&mut tui); + assert!(screen.contains("[2] Results"), "{screen}"); + assert!(!screen.contains("[1] Searches"), "{screen}"); + assert!(!screen.contains("[3] Downloads"), "{screen}"); + assert!(!screen.contains("[Info]"), "{screen}"); + assert!(screen.contains("[z → unzoom]"), "{screen}"); + + // Tab while zoomed switches which pane fills the window. + press(&mut tui, KeyCode::Tab); + let screen = screen_of(&mut tui); + assert!(screen.contains("[3] Downloads"), "{screen}"); + assert!(!screen.contains("[2] Results"), "{screen}"); + + press(&mut tui, KeyCode::Esc); + assert!(!tui.state.layout.zoomed); + let screen = screen_of(&mut tui); + assert!(screen.contains("[2] Results"), "{screen}"); + assert!(screen.contains("[Info]"), "{screen}"); + + press(&mut tui, KeyCode::Char('z')); + press(&mut tui, KeyCode::Char('z')); + assert!(!tui.state.layout.zoomed, "z toggles"); + } + + #[test] + fn a_search_brings_a_hidden_results_pane_back() { + let mut tui = furnished_tui(); + press(&mut tui, KeyCode::Char('w')); + assert!(!tui.state.layout.is_visible(FocusedPane::Results)); + + press(&mut tui, KeyCode::Char('s')); + press(&mut tui, KeyCode::Char('x')); + press(&mut tui, KeyCode::Enter); + + assert!(tui.state.layout.is_visible(FocusedPane::Results)); + assert_eq!(tui.state.focused_pane, FocusedPane::Results); + } + + #[test] + fn question_mark_opens_the_keys_list_and_closes_it_again() { + let mut tui = furnished_tui(); + press(&mut tui, KeyCode::Char('?')); + + let screen = screen_of(&mut tui); + assert!(screen.contains("Keys"), "{screen}"); + assert!(screen.contains("Tab / Shift-Tab"), "{screen}"); + assert!(screen.contains("clear every finished one"), "{screen}"); + + // Other keys are swallowed while it is open. + press(&mut tui, KeyCode::Char('q')); + assert!(!tui.state.should_exit, "q closes the list, not the app"); + assert!(!tui.state.show_help); + + press(&mut tui, KeyCode::Char('?')); + press(&mut tui, KeyCode::Char('w')); + assert!(tui.state.show_help, "w did not reach the panes"); + assert!(tui.state.layout.is_visible(FocusedPane::Results)); + press(&mut tui, KeyCode::Esc); + assert!(!tui.state.show_help); + } + + #[test] + fn on_a_small_terminal_the_keys_list_stacks_and_scrolls() { + let mut tui = furnished_tui(); + press(&mut tui, KeyCode::Char('?')); + + let screen = screen_sized(&mut tui, 80, 24); + let panes = row_with(&screen, "Panes"); + assert!(!panes.contains("Searches"), "one column: {panes}"); + assert!(screen.contains("focus a pane, hidden or not"), "{screen}"); + assert!(!screen.contains("clear every finished one"), "{screen}"); + + press(&mut tui, KeyCode::End); + let screen = screen_sized(&mut tui, 80, 24); + assert!(screen.contains("clear every finished one"), "{screen}"); + assert!(!screen.contains("Tab / Shift-Tab"), "{screen}"); + + press(&mut tui, KeyCode::Home); + let screen = screen_sized(&mut tui, 80, 24); + assert!(screen.contains("Tab / Shift-Tab"), "{screen}"); + + // A wide window shows both columns at once, nothing to scroll. + press(&mut tui, KeyCode::End); + let screen = screen_of(&mut tui); + let panes = row_with(&screen, "Panes"); + assert!(panes.contains("Searches"), "two columns: {panes}"); + assert!(screen.contains("Tab / Shift-Tab"), "{screen}"); + } + + #[test] + fn a_click_where_a_pane_just_hid_does_not_focus_it() { + let mut tui = furnished_tui(); + let _ = screen_of(&mut tui); + let searches = tui.state.searches_pane_area.expect("laid out"); + // Hidden, but not yet redrawn: the old area is still on record. + tui.state.focused_pane = FocusedPane::Searches; + press(&mut tui, KeyCode::Char('w')); + assert_eq!(tui.state.focused_pane, FocusedPane::Results); + tui.handle_mouse_event(ratatui::crossterm::event::MouseEvent { + kind: ratatui::crossterm::event::MouseEventKind::Down( + ratatui::crossterm::event::MouseButton::Left, + ), + column: searches.x + 1, + row: searches.y + 1, + modifiers: KeyModifiers::NONE, + }); + assert_eq!(tui.state.focused_pane, FocusedPane::Results); + } + + #[test] + fn the_shortcut_bar_wraps_instead_of_cutting_keys_off() { + let mut tui = furnished_tui(); + let screen = screen_of(&mut tui); + assert!(screen.contains("[q → quit]"), "{screen}"); + let bar_rows = + screen.lines().filter(|line| line.contains(" → ")).count(); + assert_eq!(bar_rows, 2, "{screen}"); + + let wide = screen_sized(&mut tui, 300, 40); + let bar_rows = wide.lines().filter(|line| line.contains(" → ")).count(); + assert_eq!(bar_rows, 1, "{wide}"); + } + + #[test] + fn page_keys_work_in_the_transfers_and_searches_lists_too() { + let mut tui = furnished_tui(); + for i in 0..30 { + tui.state.downloads.push(crate::models::DownloadEntry { + download: queued("bob", &format!("{i}.mp3")), + receiver: None, + }); + } + tui.state.focused_pane = FocusedPane::Downloads; + tui.state.downloads_table_state.select(Some(0)); + let _ = screen_of(&mut tui); // lays the panes out, which sets the page size + + press(&mut tui, KeyCode::End); + assert_eq!(tui.state.downloads_table_state.selected(), Some(30)); + press(&mut tui, KeyCode::Home); + assert_eq!(tui.state.downloads_table_state.selected(), Some(0)); + press(&mut tui, KeyCode::PageDown); + let page = tui.state.downloads_table_state.selected().expect("row"); + assert!(page > 1 && page < 30, "a pane of rows, not one: {page}"); + ctrl(&mut tui, 'd'); + assert_eq!(tui.state.downloads.len(), 31, "ctrl-d pages, d deletes"); + assert!(tui.state.downloads_table_state.selected() > Some(page)); + + tui.state.focused_pane = FocusedPane::Searches; + press(&mut tui, KeyCode::Char('G')); + assert_eq!(tui.state.searches_table_state.selected(), Some(0)); + press(&mut tui, KeyCode::Char('g')); + assert_eq!(tui.state.searches_table_state.selected(), Some(0)); + assert_eq!(tui.state.searches.len(), 1, "g and G only move"); + } + + #[test] + fn clicking_where_a_hidden_pane_was_focuses_whatever_is_there_now() { + let mut tui = furnished_tui(); + let _ = screen_of(&mut tui); + let searches = tui.state.searches_pane_area.expect("laid out"); + let inside = ratatui::crossterm::event::MouseEvent { + kind: ratatui::crossterm::event::MouseEventKind::Down( + ratatui::crossterm::event::MouseButton::Left, + ), + column: searches.x + 1, + row: searches.y + 1, + modifiers: KeyModifiers::NONE, + }; + tui.handle_mouse_event(inside); + assert_eq!(tui.state.focused_pane, FocusedPane::Searches); + + press(&mut tui, KeyCode::Char('w')); + let _ = screen_of(&mut tui); + tui.handle_mouse_event(inside); + assert_eq!(tui.state.focused_pane, FocusedPane::Downloads); + } + #[test] fn the_info_pane_describes_the_highlighted_result() { let mut tui = results_tui(3); diff --git a/soulseek-rs/src/ui/main_tui/render.rs b/soulseek-rs/src/ui/main_tui/render.rs index c4abbfb..06f7b9c 100644 --- a/soulseek-rs/src/ui/main_tui/render.rs +++ b/soulseek-rs/src/ui/main_tui/render.rs @@ -6,19 +6,92 @@ use crate::ui::panes::{ render_rooms_pane, render_searches_pane, selected_transfer, }; use crate::ui::{ - accent_style, dimmed_style, format_shortcuts_styled, pane_block, + SIGNAL, accent_style, body_style, dimmed_style, pack_shortcuts, pane_block, plain_title, primary_style, render_download_stats, warning_style, }; use ratatui::{ Frame, layout::{Alignment, Constraint, Layout, Position, Rect}, - style::Modifier, + style::{Modifier, Style}, text::{Line, Span}, widgets::Paragraph, }; +/// The keys overlay, grouped by where a key applies. Two columns, because a +/// single list runs longer than most terminals are tall. +const HELP_LEFT: &[(&str, &[(&str, &str)])] = &[ + ( + "Panes", + &[ + ("Tab / Shift-Tab", "next / previous pane"), + ("1 2 3", "focus a pane, hidden or not"), + ("z", "zoom the focused pane"), + ("w", "hide the focused pane"), + ("Esc", "leave zoom"), + ("click", "focus a pane"), + ], + ), + ( + "Anywhere", + &[ + ("s", "search the network"), + ("b", "browse a user's files"), + ("m", "compose a private message"), + ("i", "inbox"), + ("o", "settings"), + ("?", "this list"), + ("q", "quit"), + ], + ), + ( + "Any list", + &[ + ("↑ ↓ / k j", "move one row"), + ("Home End / g G", "first / last row"), + ("PgUp PgDn / ^b ^f", "one page"), + ("^u ^d", "half a page"), + ], + ), +]; + +const HELP_RIGHT: &[(&str, &[(&str, &str)])] = &[ + ( + "Searches", + &[ + ("Enter", "show its results"), + ("d", "remove the search"), + ("C", "clear every search"), + ("c", "chat rooms"), + ], + ), + ( + "Results", + &[ + ("Space", "select / deselect"), + ("a / A", "select all / none"), + ("Enter", "download the selection"), + ("/", "filter, Enter keeps it, Esc clears"), + ("h l / ← →", "scroll the file name"), + ("0 / $", "start / end of the file name"), + ("b", "browse the owner"), + ("c", "chat rooms"), + ], + ), + ( + "Downloads / Uploads", + &[ + ("p", "pause / resume"), + ("x", "cancel"), + ("r", "retry a failed one"), + ("d", "delete a queued or finished one"), + ("c", "clear every finished one"), + ], + ), +]; + impl MainTui { pub(super) fn render(&mut self, frame: &mut Frame) { + let shortcuts = self.shortcut_rows(frame.area().width); let mut constraints = vec![ Constraint::Length(3), // Status bar Constraint::Fill(1), // Main content @@ -26,7 +99,7 @@ impl MainTui { if self.state.command_bar_active { constraints.push(Constraint::Length(3)); // Command bar } - constraints.push(Constraint::Length(3)); // Shortcuts + constraints.push(Constraint::Length(shortcuts.len() as u16 + 2)); let main_chunks = Layout::vertical(constraints).split(frame.area()); let daemon = self.client.daemon_endpoint(); @@ -43,55 +116,111 @@ impl MainTui { // Render command bar if active (vim-style, above shortcuts) if self.state.command_bar_active { self.render_command_bar(frame, main_chunks[2]); - self.render_shortcuts(frame, main_chunks[3]); + self.render_shortcuts(frame, main_chunks[3], shortcuts); } else { - self.render_shortcuts(frame, main_chunks[2]); + self.render_shortcuts(frame, main_chunks[2], shortcuts); } self.render_overlays(frame); } + /// Results across the top, where a long file name has the whole width; + /// Searches, Downloads and Info in a row underneath. A hidden pane drops + /// out of its row and the rest widen; a zoomed one has the area alone. fn render_content(&mut self, frame: &mut Frame, area: Rect) { - // Split main content area - let content_chunks = Layout::horizontal([ - Constraint::Percentage(30), // Searches pane - Constraint::Percentage(70), // Results + Downloads - ]) - .split(area); + // Only what is drawn can be clicked. + self.state.searches_pane_area = None; + self.state.results_pane_area = None; + self.state.downloads_pane_area = None; - // Store searches pane area - self.state.searches_pane_area = Some(content_chunks[0]); + if self.state.layout.zoomed { + self.render_pane(frame, area, self.state.focused_pane); + return; + } - // Render Searches pane (left) - render_searches_pane( - frame, - content_chunks[0], - &self.state.searches, - &mut self.state.searches_table_state, - self.state.focused_pane == FocusedPane::Searches, - ); + let row_panes: Vec = + [FocusedPane::Searches, FocusedPane::Downloads] + .into_iter() + .filter(|pane| self.state.layout.is_visible(*pane)) + .collect(); - // Split right side into Results (top) and Downloads (bottom) - let right_chunks = Layout::vertical([ - Constraint::Percentage(60), // Results - Constraint::Percentage(40), // Downloads - ]) - .split(content_chunks[1]); + let row_area = if self.state.layout.is_visible(FocusedPane::Results) { + // With only Info left in the row, the results deserve more of + // the height. + let row_weight = if row_panes.is_empty() { 1 } else { 2 }; + let [top, bottom] = Layout::vertical([ + Constraint::Fill(3), + Constraint::Fill(row_weight), + ]) + .areas(area); + self.render_pane(frame, top, FocusedPane::Results); + bottom + } else { + area + }; - // Split bottom-right into Downloads list and Info pane - let downloads_chunks = Layout::horizontal([ - Constraint::Percentage(60), // Downloads list - Constraint::Percentage(40), // Info pane - ]) - .split(right_chunks[1]); + // Transfers carry the most columns, so they get the most width. + let mut constraints: Vec = row_panes + .iter() + .map(|pane| match pane { + FocusedPane::Searches => Constraint::Fill(5), + FocusedPane::Downloads => Constraint::Fill(10), + FocusedPane::Results => unreachable!("not a row pane"), + }) + .collect(); + constraints.push(Constraint::Fill(6)); // Info + let chunks = Layout::horizontal(constraints).split(row_area); + for (chunk, pane) in chunks.iter().zip(&row_panes) { + self.render_pane(frame, *chunk, *pane); + } + self.render_info_pane(frame, chunks[row_panes.len()]); + } - // Store results and downloads pane areas - self.state.results_pane_area = Some(right_chunks[0]); - self.state.downloads_pane_area = Some(downloads_chunks[0]); + fn render_pane( + &mut self, + frame: &mut Frame, + area: Rect, + pane: FocusedPane, + ) { + let focused = self.state.focused_pane == pane; + match pane { + FocusedPane::Searches => { + self.state.searches_pane_area = Some(area); + render_searches_pane( + frame, + area, + &self.state.searches, + &mut self.state.searches_table_state, + focused, + ); + } + FocusedPane::Results => { + self.state.results_pane_area = Some(area); + self.render_results_pane(frame, area, focused); + } + FocusedPane::Downloads => { + self.state.downloads_pane_area = Some(area); + render_downloads_pane( + frame, + area, + &self.state.downloads, + &self.state.uploads, + &mut self.state.downloads_table_state, + focused, + ); + } + } + } - // Render Results pane. When a filter is active the rendered rows are a - // subset, so the pane also needs the mapping back to unfiltered indices - // to render the selection checkboxes correctly. + fn render_results_pane( + &mut self, + frame: &mut Frame, + area: Rect, + focused: bool, + ) { + // When a filter is active the rendered rows are a subset, so the pane + // also needs the mapping back to unfiltered indices to render the + // selection checkboxes correctly. let (results_items, results_original_indices) = if self.state.results_filter_query.is_empty() { (&self.state.results_items, None) @@ -110,7 +239,7 @@ impl MainTui { render_results_pane( frame, - right_chunks[0], + area, ResultsPaneParams { items: results_items, table_state: &mut self.state.results_table_state, @@ -118,21 +247,16 @@ impl MainTui { original_indices: results_original_indices, filter_query: &self.state.results_filter_query, is_filtering: self.state.results_is_filtering, - focused: self.state.focused_pane == FocusedPane::Results, + focused, active_search_query, name_offset: self.state.results_name_offset, }, ); + } - render_downloads_pane( - frame, - downloads_chunks[0], - &self.state.downloads, - &self.state.uploads, - &mut self.state.downloads_table_state, - self.state.focused_pane == FocusedPane::Downloads, - ); - + /// The Info pane follows the focus: the highlighted result, or the + /// highlighted transfer. + fn render_info_pane(&self, frame: &mut Frame, area: Rect) { let selected = if self.state.focused_pane == FocusedPane::Results { self.highlighted_result().map(InfoSubject::Result) } else { @@ -144,7 +268,7 @@ impl MainTui { }; render_download_info_pane( frame, - downloads_chunks[1], + area, selected, matches!( self.state.focused_pane, @@ -153,6 +277,49 @@ impl MainTui { ); } + /// The keys list, two columns side by side where the terminal is wide + /// enough and one on top of the other where it is not, scrolling when + /// even that is taller than the window. + fn render_help_popup(&mut self, frame: &mut Frame) { + let screen = frame.area(); + // Borders and padding on either side of the text. + let frame_width = 4; + let width = screen.width.min(help_width() + frame_width); + let columns = help_columns(width.saturating_sub(frame_width)); + let tallest = columns.iter().map(Vec::len).max().unwrap_or(0); + let height = screen + .height + .min(u16::try_from(tallest + 2).unwrap_or(u16::MAX)); + let area = Rect::new( + screen.x + (screen.width - width) / 2, + screen.y + (screen.height - height) / 2, + width, + height, + ); + frame.render_widget(ratatui::widgets::Clear, area); + + let block = pane_block(true).title(plain_title("Keys", true)); + let inner = block.inner(area); + frame.render_widget(block, area); + + // Both columns scroll together, and no further than the taller one + // needs; the clamp is written back so the keys cannot run past it. + let furthest = tallest.saturating_sub(usize::from(inner.height)); + self.state.help_scroll = self.state.help_scroll.min(furthest); + let scroll = u16::try_from(self.state.help_scroll).unwrap_or(u16::MAX); + + let areas = + Layout::horizontal(vec![Constraint::Fill(1); columns.len()]) + .spacing(HELP_GAP) + .split(inner); + for (area, lines) in areas.iter().zip(columns) { + frame.render_widget( + Paragraph::new(lines).scroll((scroll, 0)), + *area, + ); + } + } + fn render_overlays(&mut self, frame: &mut Frame) { // Messages inbox overlays everything when open. if self.state.show_messages { @@ -188,6 +355,12 @@ impl MainTui { &mut self.state.rooms_list_table_state, ); } + + // The keys list only opens from the main view, so it is on top of + // nothing; drawn last so that stays true if that ever changes. + if self.state.show_help { + self.render_help_popup(frame); + } } fn render_settings_popup(&self, frame: &mut Frame) { @@ -304,10 +477,31 @@ impl MainTui { } } + /// The keys every pane shares for moving between and resizing panes, + /// ending the bar the same way wherever the focus sits. + fn pane_shortcuts(&self) -> Vec<(&'static str, &'static str)> { + vec![ + ("Tab/1-3", "pane"), + ( + "z", + if self.state.layout.zoomed { + "unzoom" + } else { + "zoom" + }, + ), + ("w", "hide"), + ("?", "keys"), + ("q", "quit"), + ] + } + /// Which shortcuts the bar offers, which is purely a question of what is /// open and where the focus sits. Sibling of [`Self::rooms_shortcuts`]. fn shortcuts(&self) -> Vec<(&'static str, &'static str)> { - if self.state.settings.is_some() { + if self.state.show_help { + vec![("?/Esc", "close")] + } else if self.state.settings.is_some() { vec![ ("↑↓", "move"), ("Enter/e", "edit download dir"), @@ -364,29 +558,28 @@ impl MainTui { ("Esc", "cancel"), ], } + } else if self.state.results_is_filtering + && self.state.focused_pane == FocusedPane::Results + { + // Typing goes to the filter, so no letter keys are on offer here. + vec![ + ("Type", "filter"), + ("↑/↓", "navigate"), + ("PgUp/PgDn", "page"), + ("Enter", "keep filter"), + ("Esc", "clear filter"), + ] } else { - match self.state.focused_pane { + let mut keys = match self.state.focused_pane { FocusedPane::Searches => vec![ ("s", "search"), + ("Enter", "results"), + ("d", "remove"), ("m", "message"), ("i", "inbox"), ("c", "chat"), ("b", "browse user"), - ("1-3", "focus pane"), - ("↑↓", "navigate"), - ("Enter", "results"), - ("q", "quit"), ], - FocusedPane::Results if self.state.results_is_filtering => { - vec![ - ("Type", "filter"), - ("↑/↓", "navigate"), - ("Enter", "confirm"), - ("Esc", "clear filter"), - ("1-3", "focus pane"), - ("q", "quit"), - ] - } FocusedPane::Results => vec![ ("Space", "select"), ("Enter", "download"), @@ -394,29 +587,50 @@ impl MainTui { ("c", "chat"), ("/", "filter"), ("a/A", "select all/none"), - ("h/l", "scroll"), + ("h/l", "scroll name"), ("g/G", "top/end"), - ("1-3", "focus pane"), - ("q", "quit"), ], - FocusedPane::Downloads => { - vec![ - ("p", "pause/resume"), - ("x", "cancel"), - ("r", "retry failed"), - ("d", "delete queued/done"), - ("c", "clear finished"), - ("b", "browse user"), - ("1-3", "focus pane"), - ("q", "quit"), - ] - } - } + FocusedPane::Downloads => vec![ + ("p", "pause/resume"), + ("x", "cancel"), + ("r", "retry failed"), + ("d", "delete queued/done"), + ("c", "clear finished"), + ("b", "browse user"), + ], + }; + keys.extend(self.pane_shortcuts()); + keys } } - fn render_shortcuts(&self, frame: &mut Frame, area: Rect) { - let shortcuts_line = format_shortcuts_styled(&self.shortcuts()); + /// The legend packed into rows for a window `width` wide, so a narrow + /// terminal grows the bar instead of cutting keys off its right edge. + fn shortcut_rows(&self, width: u16) -> Vec> { + // Borders and padding, and the unread badge's column when it shows. + let badge = self.unread_badge().width() as u16; + let reserved = 4 + if badge > 0 { badge + 1 } else { 0 }; + pack_shortcuts(&self.shortcuts(), width.saturating_sub(reserved)) + } + + /// A prominent unread badge for the shortcuts bar. It rides in the + /// always-visible bar, so unread mail/chat shows in every pane, not just + /// some. The spinner tick (0..10, ~100ms each) drives a ~1s blink: bright + /// for the first half of the cycle, dim for the second. + fn unread_badge(&self) -> Line<'static> { + unread_indicator( + self.state.unread_messages, + self.state.rooms.total_unread(), + self.spinner_state < 5, + ) + } + + fn render_shortcuts( + &self, + frame: &mut Frame, + area: Rect, + rows: Vec>, + ) { // Surface our own sharing status in the block title. let shared = self.client.shared_directories(); let sharing = match shared.as_slice() { @@ -431,26 +645,20 @@ impl MainTui { let inner = block.inner(area); frame.render_widget(block, area); - // A prominent unread badge, right-aligned so the context shortcuts on - // the left never crowd it out. It rides in the always-visible shortcuts - // bar, so unread mail/chat shows in every pane, not just some. The - // spinner tick (0..10, ~100ms each) drives a ~1s blink: bright for the - // first half of the cycle, dim for the second. - let unread = unread_indicator( - self.state.unread_messages, - self.state.rooms.total_unread(), - self.spinner_state < 5, - ); + // The unread badge is right-aligned so the context shortcuts on the + // left never crowd it out. + let unread = self.unread_badge(); if unread.spans.is_empty() { - frame.render_widget(Paragraph::new(shortcuts_line), inner); + frame.render_widget(Paragraph::new(rows), inner); } else { let badge_width = unread.width() as u16; let cols = Layout::horizontal([ Constraint::Fill(1), Constraint::Length(badge_width), ]) + .spacing(1) .split(inner); - frame.render_widget(Paragraph::new(shortcuts_line), cols[0]); + frame.render_widget(Paragraph::new(rows), cols[0]); frame.render_widget( Paragraph::new(unread).alignment(Alignment::Right), cols[1], @@ -493,6 +701,72 @@ impl MainTui { } } +/// Columns between the two halves of the keys list. +const HELP_GAP: u16 = 2; + +/// The widest key in a column, so the descriptions line up. +fn key_column_width(sections: &[(&str, &[(&str, &str)])]) -> usize { + sections + .iter() + .flat_map(|(_, keys)| keys.iter()) + .map(|(key, _)| Span::raw(*key).width()) + .max() + .unwrap_or(0) +} + +/// Cells a column of the keys list needs to show every line whole. +fn help_column_width(sections: &[(&str, &[(&str, &str)])]) -> u16 { + let widest = help_lines(sections) + .iter() + .map(Line::width) + .max() + .unwrap_or(0); + u16::try_from(widest).unwrap_or(u16::MAX) +} + +/// The text width of the keys list with both halves side by side. +fn help_width() -> u16 { + help_column_width(HELP_LEFT) + HELP_GAP + help_column_width(HELP_RIGHT) +} + +/// The keys list laid out for `width` cells of text: two columns when both +/// fit beside each other, otherwise the right half under the left. +fn help_columns(width: u16) -> Vec>> { + let left = help_lines(HELP_LEFT); + let right = help_lines(HELP_RIGHT); + if width >= help_width() { + return vec![left, right]; + } + let mut stacked = left; + stacked.push(Line::from("")); + stacked.extend(right); + vec![stacked] +} + +fn help_lines(sections: &[(&str, &[(&str, &str)])]) -> Vec> { + let key_width = key_column_width(sections); + let mut lines = Vec::new(); + for (index, (heading, keys)) in sections.iter().enumerate() { + if index > 0 { + lines.push(Line::from("")); + } + lines.push(Line::styled( + (*heading).to_string(), + accent_style().add_modifier(Modifier::BOLD), + )); + for (key, action) in *keys { + lines.push(Line::from(vec![ + Span::styled( + format!("{key: Rect { let vertical = Layout::vertical([ diff --git a/soulseek-rs/src/ui/main_tui/search.rs b/soulseek-rs/src/ui/main_tui/search.rs index bea4d0f..d574d11 100644 --- a/soulseek-rs/src/ui/main_tui/search.rs +++ b/soulseek-rs/src/ui/main_tui/search.rs @@ -231,8 +231,8 @@ impl MainTui { self.state.results_table_state.select(Some(0)); self.state.results_name_offset = 0; - // Switch focus to Results pane - self.state.focused_pane = FocusedPane::Results; + // Switch focus to Results pane, bringing it back if it was hidden + self.state.focus_pane(FocusedPane::Results); let client = self.client.clone(); let timeout = self.search_timeout; diff --git a/soulseek-rs/src/ui/panes/downloads_pane.rs b/soulseek-rs/src/ui/panes/downloads_pane.rs index 493b6e8..2684fda 100644 --- a/soulseek-rs/src/ui/panes/downloads_pane.rs +++ b/soulseek-rs/src/ui/panes/downloads_pane.rs @@ -163,12 +163,15 @@ pub fn render_downloads_pane( ]) })); + // Sized to the widest value each column shows in ordinary use — a + // three-digit megabyte count on both sides of the progress — so the file + // name keeps whatever width the pane has beyond that. let widths = [ - ratatui::layout::Constraint::Length(8), - ratatui::layout::Constraint::Fill(2), - ratatui::layout::Constraint::Length(15), - ratatui::layout::Constraint::Length(25), + ratatui::layout::Constraint::Length(6), + ratatui::layout::Constraint::Fill(1), ratatui::layout::Constraint::Length(12), + ratatui::layout::Constraint::Length(25), + ratatui::layout::Constraint::Length(10), ]; let table = Table::new(rows, widths) diff --git a/soulseek-rs/src/ui/panes/searches_pane.rs b/soulseek-rs/src/ui/panes/searches_pane.rs index a0aaeaf..8942d83 100644 --- a/soulseek-rs/src/ui/panes/searches_pane.rs +++ b/soulseek-rs/src/ui/panes/searches_pane.rs @@ -43,10 +43,12 @@ pub fn render_searches_pane( } else { search.results.len() }; + // Nothing yet from a search still out: the status column already + // says it is looking. let results_text = if results_count == 0 && search.status == SearchStatus::Active { - "Searching...".to_string() + "…".to_string() } else { format!("{results_count}") }; @@ -59,10 +61,12 @@ pub fn render_searches_pane( }) .collect(); + // The pane shares a row with two others, so the fixed columns are as + // narrow as their widest word: the query gets the rest. let widths = [ - ratatui::layout::Constraint::Length(15), + ratatui::layout::Constraint::Length(6), ratatui::layout::Constraint::Fill(1), - ratatui::layout::Constraint::Length(10), + ratatui::layout::Constraint::Length(7), ]; let table = Table::new(rows, widths) diff --git a/soulseek-rs/src/ui/styles.rs b/soulseek-rs/src/ui/styles.rs index 2df9ab5..930cc02 100644 --- a/soulseek-rs/src/ui/styles.rs +++ b/soulseek-rs/src/ui/styles.rs @@ -158,22 +158,59 @@ pub fn download_status_glyph(status: &DownloadStatus) -> (&'static str, Style) { } } -pub fn format_shortcuts_styled(shortcuts: &[(&str, &str)]) -> Line<'static> { - let mut spans = Vec::new(); +/// One `[key → action]` legend entry. +fn shortcut_spans(key: &str, action: &str) -> Vec> { + vec![ + Span::styled("[", dimmed_style()), + Span::styled(key.to_string(), Style::default().fg(SIGNAL)), + Span::styled(format!(" {SHORTCUT_ARROW} "), dimmed_style()), + Span::styled(action.to_string(), body_style()), + Span::styled("]", dimmed_style()), + ] +} - for (i, (key, action)) in shortcuts.iter().enumerate() { - if i > 0 { - spans.push(Span::raw(" ")); +/// The most rows the shortcuts bar grows to. Past that a terminal is too +/// narrow for the legend to be worth the content rows it would take. +pub const SHORTCUT_ROWS_MAX: usize = 3; + +/// Lay the legend out in rows no wider than `width`, never breaking inside an +/// entry: a `[key → action]` split over two rows reads as two keys. Entries +/// that do not fit in [`SHORTCUT_ROWS_MAX`] rows are dropped, so a bar always +/// has at least one row and never more than that. +pub fn pack_shortcuts( + shortcuts: &[(&str, &str)], + width: u16, +) -> Vec> { + let width = usize::from(width); + let mut rows: Vec> = Vec::new(); + let mut row: Vec> = Vec::new(); + let mut row_width = 0; + + for (key, action) in shortcuts { + let entry = shortcut_spans(key, action); + let entry_width: usize = entry.iter().map(Span::width).sum(); + let needed = if row.is_empty() { + entry_width + } else { + row_width + 1 + entry_width + }; + if !row.is_empty() && needed > width { + rows.push(Line::from(std::mem::take(&mut row))); + if rows.len() == SHORTCUT_ROWS_MAX { + return rows; + } + row_width = entry_width; + } else { + if !row.is_empty() { + row.push(Span::raw(" ")); + } + row_width = needed; } - - spans.push(Span::styled("[", dimmed_style())); - spans.push(Span::styled(key.to_string(), Style::default().fg(SIGNAL))); - spans.push(Span::styled(format!(" {SHORTCUT_ARROW} "), dimmed_style())); - spans.push(Span::styled(action.to_string(), body_style())); - spans.push(Span::styled("]", dimmed_style())); + row.extend(entry); } - Line::from(spans) + rows.push(Line::from(row)); + rows } pub fn format_progress_bar( @@ -253,6 +290,46 @@ mod tests { assert_eq!(cursor.bg, Some(SELECTION)); } + fn texts(rows: &[Line<'static>]) -> Vec { + rows.iter() + .map(|line| line.spans.iter().map(|s| s.content.as_ref()).collect()) + .collect() + } + + #[test] + fn shortcuts_stay_on_one_row_when_they_fit() { + let rows = pack_shortcuts(&[("q", "quit"), ("s", "search")], 80); + assert_eq!(texts(&rows), ["[q → quit] [s → search]"]); + } + + #[test] + fn shortcuts_wrap_between_entries_never_inside_one() { + let rows = pack_shortcuts( + &[("Space", "select"), ("Enter", "download"), ("q", "quit")], + 30, + ); + // "[Space → select] [Enter → download]" is 35 cells, over the 30 the + // bar has, so the second entry starts a new row whole. + assert_eq!( + texts(&rows), + ["[Space → select]", "[Enter → download] [q → quit]"] + ); + } + + #[test] + fn an_entry_wider_than_the_bar_still_gets_a_row() { + let rows = pack_shortcuts(&[("Enter", "download"), ("q", "quit")], 5); + assert_eq!(texts(&rows), ["[Enter → download]", "[q → quit]"]); + } + + #[test] + fn the_bar_never_grows_past_its_cap() { + let keys: Vec<(&str, &str)> = (0..10).map(|_| ("k", "act")).collect(); + let rows = pack_shortcuts(&keys, 12); + assert_eq!(rows.len(), SHORTCUT_ROWS_MAX); + assert_eq!(texts(&rows)[0], "[k → act]"); + } + #[test] fn progress_bar_splits_fill_and_track() { let bar = format_progress_bar(0.25, 20, 25); diff --git a/website/src/components/tui/data.ts b/website/src/components/tui/data.ts index 62e5f33..154510b 100644 --- a/website/src/components/tui/data.ts +++ b/website/src/components/tui/data.ts @@ -182,6 +182,11 @@ export const SHORTCUTS: readonly (readonly [string, string])[] = [ ['c', 'chat'], ['/', 'filter'], ['a/A', 'select all/none'], - ['1-3', 'focus pane'], + ['h/l', 'scroll name'], + ['g/G', 'top/end'], + ['Tab/1-3', 'pane'], + ['z', 'zoom'], + ['w', 'hide'], + ['?', 'keys'], ['q', 'quit'], ] diff --git a/website/src/components/tui/hero-terminal.tsx b/website/src/components/tui/hero-terminal.tsx index 5806214..02221bf 100644 --- a/website/src/components/tui/hero-terminal.tsx +++ b/website/src/components/tui/hero-terminal.tsx @@ -37,45 +37,44 @@ export const HeroTerminal = () => { > -
-
- +
+ + {demo.searching && ( +
+ searching the network + +
+ )}
-
-
- +
+ - {demo.searching && ( -
- searching the network - -
- )}
- -
-
- -
-
- -
+
+ +
+
+
diff --git a/website/src/components/tui/panes.tsx b/website/src/components/tui/panes.tsx index 4e5854b..7bb8344 100644 --- a/website/src/components/tui/panes.tsx +++ b/website/src/components/tui/panes.tsx @@ -6,7 +6,7 @@ import { TuiPane } from './chrome' import type { ResultRow, SearchRow, TransferRow, TransferStatus } from './data' const RESULT_COLS = '30px 1fr 74px 92px 78px 74px 42px' -const TRANSFER_COLS = '48px 1fr 96px 150px 74px' +const TRANSFER_COLS = '48px 1fr 96px 168px 74px' interface SearchesPaneProps { searches: readonly SearchRow[] diff --git a/website/src/pages/docs.tsx b/website/src/pages/docs.tsx index 0c23098..0142507 100644 --- a/website/src/pages/docs.tsx +++ b/website/src/pages/docs.tsx @@ -25,10 +25,20 @@ const GLOBAL_KEYS: readonly (readonly [string, string])[] = [ ['i', 'inbox (unread counter)'], ['/', 'filter the current list'], ['a / A', 'select all / none'], - ['1–3', 'focus a pane'], + ['?', 'every key, grouped by pane'], ['q', 'quit'], ] +const PANE_KEYS: readonly (readonly [string, string])[] = [ + ['Tab · ⇧Tab', 'next / previous pane'], + ['1–3', 'focus a pane, and bring it back if hidden'], + ['z', 'zoom the focused pane to the whole window'], + ['w', 'hide the focused pane; the others widen'], + ['↑ ↓ · j k', 'move a row in any list'], + ['Home End · g G', 'first / last row'], + ['PgUp PgDn · ^u ^d', 'a page, or half a page'], +] + const ROOM_KEYS: readonly (readonly [string, string])[] = [ ['Enter', 'join room / send message'], ['Tab · ⇧Tab', 'switch between open rooms'], @@ -40,16 +50,16 @@ const ROOM_KEYS: readonly (readonly [string, string])[] = [ ] const PANES: readonly { num?: number; title: string; body: string }[] = [ + { + num: 2, + title: 'Results', + body: 'Files from the network across the whole width: size, user, bitrate, speed, free slots. Space selects, Enter queues a download. h/l or ←/→ scroll a long name, 0 and $ jump to either end.', + }, { num: 1, title: 'Searches', body: 'Your queries and how many results each returned. Press s to search the network; each search keeps its own result set.', }, - { - num: 2, - title: 'Results', - body: 'Files from the network: size, user, bitrate, speed, free slots. Space selects, Enter queues a download. h/l or ←/→ scroll a long name, 0 and $ jump to either end; Home/End, g/G, PgUp/PgDn and Ctrl-f/b/d/u move through the list.', - }, { num: 3, title: 'Downloads / Uploads', @@ -106,8 +116,10 @@ const FirstRun = () => ( const Layout = () => (
- The whole client is these four boxes. Press 1–3 to focus - one; the focused pane’s legend turns green. + The whole client is these four boxes: results across the top, the rest in a row + underneath. Tab or 1–3 focuses one and its legend + turns green; z gives it the whole window, w hides it so the + others widen.
@@ -127,7 +139,8 @@ const Layout = () => ( const Keys = () => (
- The bottom bar always shows the keys for where you are. The essentials: + The bottom bar always shows the keys for where you are, and ? lists every + one. The essentials:
@@ -138,6 +151,16 @@ const Keys = () => ( ))}
+
+ panes and lists +
+ {PANE_KEYS.map(([combo, description]) => ( + + ))} +
+
+
+
in the chat-rooms popup (press c)