chore: tui improvements
7a0298f1
2 file(s) · +63 −2
| 8 | 8 | path = "src/main.rs" |
|
| 9 | 9 | ||
| 10 | 10 | [[bin]] |
|
| 11 | - | name = "sipp-tui" |
|
| 11 | + | name = "sipp" |
|
| 12 | 12 | path = "src/bin/tui.rs" |
|
| 13 | 13 | ||
| 14 | 14 | [dependencies] |
|
| 29 | 29 | clap = { version = "4", features = ["derive", "env"] } |
|
| 30 | 30 | toml = "0.8" |
|
| 31 | 31 | rpassword = "5" |
|
| 32 | + | open = "5.3.3" |
|
| 12 | 12 | use sipp_rust::config; |
|
| 13 | 13 | use sipp_rust::db::Snippet; |
|
| 14 | 14 | use std::io::Cursor; |
|
| 15 | + | use std::path::PathBuf; |
|
| 15 | 16 | use std::time::{Duration, Instant}; |
|
| 16 | 17 | use syntect::easy::HighlightLines; |
|
| 17 | 18 | use syntect::highlighting::Theme; |
|
| 28 | 29 | /// API key for authenticated operations |
|
| 29 | 30 | #[arg(short = 'k', long, env = "SIPP_API_KEY")] |
|
| 30 | 31 | api_key: Option<String>, |
|
| 32 | + | ||
| 33 | + | /// File path to create a snippet from (uses filename as name, file contents as content) |
|
| 34 | + | #[arg(value_name = "FILE")] |
|
| 35 | + | file: Option<PathBuf>, |
|
| 31 | 36 | ||
| 32 | 37 | #[command(subcommand)] |
|
| 33 | 38 | command: Option<Commands>, |
|
| 148 | 153 | let _ = clipboard.set_text(&link); |
|
| 149 | 154 | self.status_message = |
|
| 150 | 155 | Some(("Link copied!".to_string(), Instant::now())); |
|
| 156 | + | } |
|
| 157 | + | } |
|
| 158 | + | } |
|
| 159 | + | None => { |
|
| 160 | + | self.status_message = |
|
| 161 | + | Some(("No remote URL configured".to_string(), Instant::now())); |
|
| 162 | + | } |
|
| 163 | + | } |
|
| 164 | + | } |
|
| 165 | + | ||
| 166 | + | fn open_in_browser(&mut self) { |
|
| 167 | + | match &self.remote_url { |
|
| 168 | + | Some(url) => { |
|
| 169 | + | if let Some(snippet) = self.selected_snippet() { |
|
| 170 | + | let link = format!("{}/s/{}", url.trim_end_matches('/'), snippet.short_id); |
|
| 171 | + | if let Err(e) = open::that(&link) { |
|
| 172 | + | self.status_message = |
|
| 173 | + | Some((format!("Failed to open browser: {}", e), Instant::now())); |
|
| 174 | + | } else { |
|
| 175 | + | self.status_message = |
|
| 176 | + | Some(("Opened in browser!".to_string(), Instant::now())); |
|
| 151 | 177 | } |
|
| 152 | 178 | } |
|
| 153 | 179 | } |
|
| 345 | 371 | ||
| 346 | 372 | let (backend, is_remote, remote_url) = resolve_backend(&cli); |
|
| 347 | 373 | ||
| 374 | + | if let Some(file_path) = &cli.file { |
|
| 375 | + | let name = file_path |
|
| 376 | + | .file_name() |
|
| 377 | + | .ok_or("Invalid file path")? |
|
| 378 | + | .to_string_lossy() |
|
| 379 | + | .to_string(); |
|
| 380 | + | let content = std::fs::read_to_string(file_path) |
|
| 381 | + | .map_err(|e| format!("Failed to read file: {}", e))?; |
|
| 382 | + | let snippet = backend |
|
| 383 | + | .create_snippet(&name, &content) |
|
| 384 | + | .map_err(|e| format!("{}", e))?; |
|
| 385 | + | let link = match &remote_url { |
|
| 386 | + | Some(url) => format!("{}/s/{}", url.trim_end_matches('/'), snippet.short_id), |
|
| 387 | + | None => snippet.short_id.clone(), |
|
| 388 | + | }; |
|
| 389 | + | println!("{}", link); |
|
| 390 | + | if let Ok(mut clipboard) = Clipboard::new() { |
|
| 391 | + | let _ = clipboard.set_text(&link); |
|
| 392 | + | println!("\u{2714} Copied to clipboard!"); |
|
| 393 | + | } |
|
| 394 | + | return Ok(()); |
|
| 395 | + | } |
|
| 396 | + | ||
| 348 | 397 | let snippets = match backend.list_snippets() { |
|
| 349 | 398 | Ok(s) => s, |
|
| 350 | 399 | Err(e) => { |
|
| 518 | 567 | if app.show_help { |
|
| 519 | 568 | let area = frame.area(); |
|
| 520 | 569 | let popup_width = 44u16.min(area.width.saturating_sub(4)); |
|
| 521 | - | let popup_height = 19u16.min(area.height.saturating_sub(4)); |
|
| 570 | + | let popup_height = 20u16.min(area.height.saturating_sub(4)); |
|
| 522 | 571 | let popup_area = ratatui::layout::Rect { |
|
| 523 | 572 | x: (area.width.saturating_sub(popup_width)) / 2, |
|
| 524 | 573 | y: (area.height.saturating_sub(popup_height)) / 2, |
|
| 584 | 633 | ]), |
|
| 585 | 634 | Line::from(vec![ |
|
| 586 | 635 | Span::styled( |
|
| 636 | + | " o ", |
|
| 637 | + | Style::default() |
|
| 638 | + | .fg(Color::Yellow) |
|
| 639 | + | .add_modifier(Modifier::BOLD), |
|
| 640 | + | ), |
|
| 641 | + | Span::raw("Open in browser"), |
|
| 642 | + | ]), |
|
| 643 | + | Line::from(vec![ |
|
| 644 | + | Span::styled( |
|
| 587 | 645 | " d ", |
|
| 588 | 646 | Style::default() |
|
| 589 | 647 | .fg(Color::Yellow) |
|
| 667 | 725 | KeyCode::Char('Y') => app.copy_link(), |
|
| 668 | 726 | KeyCode::Char('d') => app.delete_selected(backend), |
|
| 669 | 727 | KeyCode::Char('c') => app.start_create(), |
|
| 728 | + | KeyCode::Char('o') => app.open_in_browser(), |
|
| 670 | 729 | KeyCode::Char('r') if app.is_remote => app.refresh(backend), |
|
| 671 | 730 | KeyCode::Char('?') => app.show_help = true, |
|
| 672 | 731 | KeyCode::Enter | KeyCode::Char('l') => { |
|
| 686 | 745 | KeyCode::Char('k') | KeyCode::Up => app.scroll_up(), |
|
| 687 | 746 | KeyCode::Char('y') => app.copy_selected(), |
|
| 688 | 747 | KeyCode::Char('Y') => app.copy_link(), |
|
| 748 | + | KeyCode::Char('o') => app.open_in_browser(), |
|
| 689 | 749 | KeyCode::Char('?') => app.show_help = true, |
|
| 690 | 750 | _ => {} |
|
| 691 | 751 | }, |
|