update simsearch, fill out help screen
This commit is contained in:
parent
2b1e0f7cb8
commit
77d44b8868
6 changed files with 80 additions and 68 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -414,7 +414,7 @@ dependencies = [
|
|||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim 0.11.1",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -545,7 +545,7 @@ dependencies = [
|
|||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.11.1",
|
||||
"strsim",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
|
@ -2281,11 +2281,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "simsearch"
|
||||
version = "0.2.5"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c869b25830e4824ef7279015cfc298a0674aca6a54eeff2efce8d12bf3701fe"
|
||||
checksum = "629d21c4ebf25655995cda9eb93e85539fa68b0438acb85e9e5d10f6fe2404bc"
|
||||
dependencies = [
|
||||
"strsim 0.10.0",
|
||||
"strsim",
|
||||
"triple_accel",
|
||||
]
|
||||
|
||||
|
@ -2323,12 +2323,6 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
@ -2699,9 +2693,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "triple_accel"
|
||||
version = "0.3.4"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622b09ce2fe2df4618636fb92176d205662f59803f39e70d1c333393082de96c"
|
||||
checksum = "22048bc95dfb2ffd05b1ff9a756290a009224b60b2f0e7525faeee7603851e63"
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
|
|
|
@ -6,7 +6,7 @@ version = "1.6.1803"
|
|||
edition = "2024"
|
||||
authors = ["Joe Ardent <code@ardent.nebcorp.com>"]
|
||||
keywords = ["p2p", "localsend", "tui", "linux"]
|
||||
description = "A terminal implementation of the LocalSend protocol"
|
||||
description = "A TUI for LocalSend"
|
||||
readme = "README.md"
|
||||
license-file = "LICENSE.md"
|
||||
repository = "https://git.kittencollective.com/nebkor/joecalsend"
|
||||
|
@ -34,7 +34,7 @@ rustix = { version = "1", default-features = false, features = ["system"] }
|
|||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sha256 = "1.6"
|
||||
simsearch = "0.2"
|
||||
simsearch = "0.3"
|
||||
thiserror = "2"
|
||||
tokio = { version = "1", default-features = false, features = ["time", "macros", "rt-multi-thread"] }
|
||||
tokio-rustls = { version = "0.26", default-features = false, features = ["tls12", "logging"] }
|
||||
|
|
18
README.md
18
README.md
|
@ -10,7 +10,8 @@ that uses [Ratatui](https://github.com/ratatui/ratatui) to provide an interactiv
|
|||
application, and is compatible with the official app.
|
||||
|
||||
Install with `cargo install jocalsend` (requires [Rust](https://rustup.rs/)); tested on Linux, it
|
||||
will probably work on Macs but if you're on a Mac, you probably have AirDrop.
|
||||
will probably work on Macs but if you're on a Mac, you probably have AirDrop. It's also available in
|
||||
nixpkgs, and so if you're a NixOS user, `nix-shell -p jocalsend` will do what you expect.
|
||||
|
||||
## Capabilities and screenshots
|
||||
|
||||
|
@ -22,18 +23,21 @@ available:
|
|||
- `S` -> go to the sending screen, defaulting to sending files
|
||||
- `R` -> go to the receiving screen to approve or deny incoming transfers
|
||||
- `L` -> go to the logging screen where you can adjust the log level
|
||||
- `C` -> clear the list of local peers and re-discover them
|
||||
- `H` or `?` -> go to help screen
|
||||
- `ESC` -> go back to the previous screen
|
||||
- `Q` -> exit the application
|
||||
|
||||
Additionally, when in the sending screen, the following are available
|
||||
When in the sending screen, the following are available
|
||||
|
||||
- `TAB` -> switch between content selection and peer selection
|
||||
- `P` -> switch to peer selection
|
||||
- `T` -> switch to entering text to send
|
||||
- `F` -> switch to selecting files to send (not available when entering text, use `ESC` to exit text entry)
|
||||
- `T` -> enter text directly to send, `ESC` to cancel
|
||||
- `/` -> fuzzy filename search, use `ESC` to stop inputting text
|
||||
|
||||
In addition to the interactive commands, it will also accept commandline arguments to pre-select a
|
||||
file or pre-populate text to send:
|
||||
When in the receiving screen, use `A` to approve the incoming transfer request, or `D` to deny it.
|
||||
|
||||
Finally, it will also accept commandline arguments to pre-select a file or pre-populate text to
|
||||
send:
|
||||
|
||||
```
|
||||
$ jocalsend -h
|
||||
|
|
|
@ -15,20 +15,18 @@ pub(crate) struct FileFinder {
|
|||
pub input: Input,
|
||||
}
|
||||
|
||||
fn searcher() -> SimSearch<usize> {
|
||||
SimSearch::new_with(
|
||||
SearchOptions::new()
|
||||
.stop_words(vec![std::path::MAIN_SEPARATOR_STR.to_string()])
|
||||
.stop_whitespace(false)
|
||||
.threshold(0.0),
|
||||
)
|
||||
}
|
||||
|
||||
impl FileFinder {
|
||||
pub fn new() -> Result<Self> {
|
||||
let fuzzy = SimSearch::new_with(
|
||||
SearchOptions::new()
|
||||
.stop_words(vec![std::path::MAIN_SEPARATOR_STR.to_string()])
|
||||
.stop_whitespace(false)
|
||||
.threshold(0.0),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
explorer: FileExplorer::new()?,
|
||||
fuzzy: searcher(),
|
||||
fuzzy,
|
||||
working_dir: None,
|
||||
input: Default::default(),
|
||||
})
|
||||
|
@ -54,14 +52,10 @@ impl FileFinder {
|
|||
}
|
||||
|
||||
pub fn reset_fuzzy(&mut self) {
|
||||
self.clear_fuzzy();
|
||||
self.fuzzy.clear();
|
||||
self.input.reset();
|
||||
}
|
||||
|
||||
fn clear_fuzzy(&mut self) {
|
||||
self.fuzzy = searcher();
|
||||
}
|
||||
|
||||
pub fn index(&mut self) {
|
||||
if let Some(owd) = self.working_dir.as_ref()
|
||||
&& owd == self.cwd()
|
||||
|
@ -69,7 +63,7 @@ impl FileFinder {
|
|||
return;
|
||||
}
|
||||
self.working_dir = Some(self.cwd().to_path_buf());
|
||||
self.clear_fuzzy();
|
||||
self.reset_fuzzy();
|
||||
|
||||
for (i, f) in self.explorer.files().iter().enumerate() {
|
||||
self.fuzzy.insert(i, f.name());
|
||||
|
|
|
@ -253,8 +253,6 @@ fn outer_frame(screen: &CurrentScreen, menu: &Line, area: Rect, buf: &mut Buffer
|
|||
}
|
||||
|
||||
fn help_screen(area: Rect, buf: &mut Buffer) {
|
||||
let intro = "JocalSend is a mode-based application that responds to key-presses. Most modes support the following key bindings:".to_line().centered();
|
||||
let intro = Paragraph::new(intro).wrap(Wrap { trim: true });
|
||||
let spacer = "".to_line().centered();
|
||||
let main_bindings = vec![
|
||||
Row::new(vec!["".to_line(), spacer.clone(), "".to_line()]),
|
||||
|
@ -266,7 +264,7 @@ fn help_screen(area: Rect, buf: &mut Buffer) {
|
|||
]),
|
||||
// Receiving
|
||||
Row::new(vec![
|
||||
"Manage incoming data requests (receive data)"
|
||||
"Manage incoming transfer requests"
|
||||
.bold()
|
||||
.into_right_aligned_line(),
|
||||
spacer.clone(),
|
||||
|
@ -280,23 +278,19 @@ fn help_screen(area: Rect, buf: &mut Buffer) {
|
|||
spacer.clone(),
|
||||
"L".bold().into_left_aligned_line(),
|
||||
]),
|
||||
// misc: pop
|
||||
Row::new(vec![
|
||||
"Go to previous screen".bold().into_right_aligned_line(),
|
||||
spacer.clone(),
|
||||
"ESC".bold().into_left_aligned_line(),
|
||||
]),
|
||||
// misc: main menu
|
||||
Row::new(vec![
|
||||
"Go to the main screen".bold().into_right_aligned_line(),
|
||||
spacer.clone(),
|
||||
"M".bold().into_left_aligned_line(),
|
||||
]),
|
||||
// misc: quit
|
||||
// misc: clear peers
|
||||
Row::new(vec![
|
||||
"Quit the application".bold().into_right_aligned_line(),
|
||||
"Clear peers and rediscover"
|
||||
.bold()
|
||||
.into_right_aligned_line(),
|
||||
spacer.clone(),
|
||||
"Q".bold().into_left_aligned_line(),
|
||||
"C".bold().into_left_aligned_line(),
|
||||
]),
|
||||
// misc: help
|
||||
Row::new(vec![
|
||||
|
@ -304,10 +298,27 @@ fn help_screen(area: Rect, buf: &mut Buffer) {
|
|||
spacer.clone(),
|
||||
"H or ?".bold().into_left_aligned_line(),
|
||||
]),
|
||||
// misc: pop
|
||||
Row::new(vec![
|
||||
"Go to previous screen".bold().into_right_aligned_line(),
|
||||
spacer.clone(),
|
||||
"ESC".bold().into_left_aligned_line(),
|
||||
]),
|
||||
// misc: quit
|
||||
Row::new(vec![
|
||||
"Quit the application".bold().into_right_aligned_line(),
|
||||
spacer.clone(),
|
||||
"Q".bold().into_left_aligned_line(),
|
||||
]),
|
||||
];
|
||||
|
||||
let layout = Layout::vertical(vec![Constraint::Max(3), Constraint::Min(1)]);
|
||||
let [intro_area, bindings_area] = layout.areas(area);
|
||||
let layout = Layout::vertical(vec![
|
||||
Constraint::Max(3),
|
||||
Constraint::Max(12),
|
||||
Constraint::Max(3),
|
||||
])
|
||||
.flex(Flex::SpaceAround);
|
||||
let [intro_area, bindings_area, outro_area] = layout.areas(area);
|
||||
|
||||
let widths = vec![
|
||||
Constraint::Percentage(50),
|
||||
|
@ -322,8 +333,15 @@ fn help_screen(area: Rect, buf: &mut Buffer) {
|
|||
|
||||
Clear.render(area, buf);
|
||||
|
||||
let intro = "JocalSend is a mode-based application that responds to key-presses. Most modes support the following key bindings:".to_line().centered();
|
||||
let intro = Paragraph::new(intro).wrap(Wrap { trim: true });
|
||||
|
||||
let outro = "Additional key bindings are available when in the sending or receiving screens, and are displayed at the bottom of the screen there.".to_line().centered();
|
||||
let outro = Paragraph::new(outro).wrap(Wrap { trim: true });
|
||||
|
||||
intro.render(intro_area, buf);
|
||||
main_bindings.render(bindings_area, buf);
|
||||
outro.render(outro_area, buf);
|
||||
}
|
||||
|
||||
fn logger(area: Rect, buf: &mut Buffer) {
|
||||
|
|
|
@ -131,7 +131,6 @@ impl JocalService {
|
|||
let token = match prepare_response.files.get(&metadata.id) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
log::warn!("");
|
||||
send_tx(
|
||||
JocalEvent::SendFailed {
|
||||
error: "missing token in prepare response from remote".into(),
|
||||
|
@ -144,6 +143,15 @@ impl JocalService {
|
|||
|
||||
let content_id = &metadata.id;
|
||||
let session_id = prepare_response.session_id;
|
||||
log::info!(
|
||||
"sending {content_id} to {}",
|
||||
peers
|
||||
.lock()
|
||||
.await
|
||||
.get(&peer)
|
||||
.map(|(_, peer)| peer.alias.as_str())
|
||||
.unwrap_or("unknown peer")
|
||||
);
|
||||
let resp = do_send_bytes(sessions, client, &session_id, content_id, token, bytes).await;
|
||||
|
||||
match resp {
|
||||
|
@ -273,11 +281,7 @@ pub async fn handle_receive_upload(
|
|||
let file_metadata = match session.files.get(file_id) {
|
||||
Some(metadata) => metadata,
|
||||
None => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"File not found".to_string(),
|
||||
)
|
||||
.into_response();
|
||||
return (StatusCode::BAD_REQUEST, "File not found".to_string()).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -285,11 +289,8 @@ pub async fn handle_receive_upload(
|
|||
|
||||
// Create directory if it doesn't exist
|
||||
if let Err(e) = tokio::fs::create_dir_all(download_dir).await {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to create directory: {e}"),
|
||||
)
|
||||
.into_response();
|
||||
log::error!("could not create download directory '{download_dir:?}', got {e}");
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, "could not save content").into_response();
|
||||
}
|
||||
|
||||
// Create file path
|
||||
|
@ -297,13 +298,14 @@ pub async fn handle_receive_upload(
|
|||
|
||||
// Write file
|
||||
if let Err(e) = tokio::fs::write(&file_path, body).await {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to write file: {e}"),
|
||||
)
|
||||
.into_response();
|
||||
log::warn!("could not save content to {file_path:?}, got {e}");
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, "could not save content").into_response();
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"saved content from {} to {file_path:?}",
|
||||
&session.sender.alias
|
||||
);
|
||||
if let Ok(id) = Julid::from_str(session_id) {
|
||||
service.send_event(JocalEvent::ReceivedInbound(id));
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue