diff --git a/src/app/cred.rs b/src/app/cred.rs index cc2cac5..f4fe0f4 100644 --- a/src/app/cred.rs +++ b/src/app/cred.rs @@ -83,10 +83,18 @@ impl CredFormState { } impl FormNav for CredFormState { - fn nav_next(&mut self) { self.next_field(); } - fn nav_prev(&mut self) { self.prev_field(); } - fn active_is_toggle(&self) -> bool { self.active.is_toggle() } - fn active_is_text(&self) -> bool { self.active.is_text() } + fn nav_next(&mut self) { + self.next_field(); + } + fn nav_prev(&mut self) { + self.prev_field(); + } + fn active_is_toggle(&self) -> bool { + self.active.is_toggle() + } + fn active_is_text(&self) -> bool { + self.active.is_text() + } } impl TextEditing for CredFormState { diff --git a/src/app/form.rs b/src/app/form.rs index 4bc5ca7..64e7b69 100644 --- a/src/app/form.rs +++ b/src/app/form.rs @@ -212,10 +212,18 @@ impl FormState { } impl FormNav for FormState { - fn nav_next(&mut self) { self.next_field(); } - fn nav_prev(&mut self) { self.prev_field(); } - fn active_is_toggle(&self) -> bool { self.active.is_toggle() } - fn active_is_text(&self) -> bool { self.active.is_text() } + fn nav_next(&mut self) { + self.next_field(); + } + fn nav_prev(&mut self) { + self.prev_field(); + } + fn active_is_toggle(&self) -> bool { + self.active.is_toggle() + } + fn active_is_text(&self) -> bool { + self.active.is_text() + } } impl TextEditing for FormState { diff --git a/src/app/home_ops.rs b/src/app/home_ops.rs index f936ce8..3ac02a1 100644 --- a/src/app/home_ops.rs +++ b/src/app/home_ops.rs @@ -46,7 +46,8 @@ impl App { self.session.import.candidates = crate::import::load_candidates(&self.config)?; self.session.import.selected = vec![false; self.session.import.candidates.len()]; self.session.import.shell_candidates = self.config.local_shell_candidates(); - self.session.import.shell_selected = vec![false; self.session.import.shell_candidates.len()]; + self.session.import.shell_selected = + vec![false; self.session.import.shell_candidates.len()]; self.session.import.cursor = 0; self.session.mode = Mode::ImportSelector; Ok(()) diff --git a/src/app/settings.rs b/src/app/settings.rs index 3e6ecaf..86e3ba1 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -20,7 +20,11 @@ pub enum SettingsField { impl SettingsField { pub fn visible_fields(backend: SyncBackend) -> Vec { - let mut fields = vec![SettingsField::SyncPassword, SettingsField::Backend, SettingsField::SyncOnStart]; + let mut fields = vec![ + SettingsField::SyncPassword, + SettingsField::Backend, + SettingsField::SyncOnStart, + ]; match backend { SyncBackend::Gist => fields.push(SettingsField::GistId), SyncBackend::Webdav => { @@ -43,7 +47,7 @@ impl SettingsField { Self::WebdavUrl => "URL", Self::WebdavUser => "Username", Self::WebdavPassword => "Password", - } + } } pub fn is_toggle(self) -> bool { @@ -110,10 +114,18 @@ impl SettingsState { } impl FormNav for SettingsState { - fn nav_next(&mut self) { self.next_field(); } - fn nav_prev(&mut self) { self.prev_field(); } - fn active_is_toggle(&self) -> bool { self.active.is_toggle() } - fn active_is_text(&self) -> bool { self.active.is_text() } + fn nav_next(&mut self) { + self.next_field(); + } + fn nav_prev(&mut self) { + self.prev_field(); + } + fn active_is_toggle(&self) -> bool { + self.active.is_toggle() + } + fn active_is_text(&self) -> bool { + self.active.is_text() + } } impl Default for SettingsState { @@ -178,8 +190,12 @@ impl App { self.config.settings.webdav_url.clone().unwrap_or_default(); self.session.settings.webdav_user = self.config.settings.webdav_user.clone().unwrap_or_default(); - self.session.settings.webdav_password = - self.config.settings.webdav_password.clone().unwrap_or_default(); + self.session.settings.webdav_password = self + .config + .settings + .webdav_password + .clone() + .unwrap_or_default(); self.session.settings.active = SettingsField::SyncPassword; self.session.settings.cursor = char_len(self.session.settings.active_text()); self.session.mode = Mode::Settings; diff --git a/src/app/types.rs b/src/app/types.rs index f1a7ddc..d2377c7 100644 --- a/src/app/types.rs +++ b/src/app/types.rs @@ -251,7 +251,6 @@ impl Default for CredentialSession { } } - #[cfg(test)] mod tests { use super::*; diff --git a/src/cli.rs b/src/cli.rs index 3d4491b..539f729 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,6 @@ -use crate::config::{ConnectionType, CredentialEntry, SshellConfig, SyncBackend, config_path, find_binary}; +use crate::config::{ + ConnectionType, CredentialEntry, SshellConfig, SyncBackend, config_path, find_binary, +}; use crate::sync; use crate::{connection, import, ui}; use anyhow::{Context, Result, bail}; @@ -19,14 +21,10 @@ pub struct Cli { #[derive(Debug, Subcommand)] enum Command { Tui, - Connect { - name: String, - }, + Connect { name: String }, Import, Sync, - Doctor { - name: Option, - }, + Doctor { name: Option }, ConfigPath, } pub fn run() -> Result<()> { @@ -138,10 +136,7 @@ fn check_connection( "ssh command: ssh -o StrictHostKeyChecking=accept-new -p {port} {user}@{host}" ); } - ConnectionType::Shell { - command, - .. - } => { + ConnectionType::Shell { command, .. } => { println!("type: shell"); let merged_args = profile.merged_shell_args(); println!("command: {command} {}", merged_args.join(" ")); diff --git a/src/config.rs b/src/config.rs index 445b494..fee9386 100644 --- a/src/config.rs +++ b/src/config.rs @@ -253,7 +253,11 @@ impl ConnectionProfile { /// Returns an empty vec for SSH connections. pub fn merged_shell_args(&self) -> Vec { match &self.kind { - ConnectionType::Shell { sync_args, local_args, .. } => { + ConnectionType::Shell { + sync_args, + local_args, + .. + } => { let mut out = sync_args.clone(); out.extend(local_args.iter().cloned()); out diff --git a/src/config/config_shell.rs b/src/config/config_shell.rs index d439c17..33341d5 100644 --- a/src/config/config_shell.rs +++ b/src/config/config_shell.rs @@ -1,4 +1,6 @@ -use super::{ConnectionProfile, ConnectionSource, ConnectionType, ShellCandidate, ShellScanConflict}; +use super::{ + ConnectionProfile, ConnectionSource, ConnectionType, ShellCandidate, ShellScanConflict, +}; use anyhow::{Result, bail}; #[cfg(unix)] use std::fs; @@ -44,13 +46,13 @@ impl super::SshellConfig { }) { continue; } - let conflict = self - .connections - .contains_key(&format!("${name}")) - .then(|| ShellScanConflict { - name: name.clone(), - path: wsl_path.clone(), - }); + let conflict = + self.connections + .contains_key(&format!("${name}")) + .then(|| ShellScanConflict { + name: name.clone(), + path: wsl_path.clone(), + }); out.push(ShellCandidate { name, path: wsl_path.clone(), @@ -176,7 +178,11 @@ fn local_shell_paths() -> Vec { let system_root = std::env::var_os("SystemRoot").unwrap_or_else(|| r"C:\Windows".into()); for path in [ - PathBuf::from(&system_root).join("System32").join("WindowsPowerShell").join("v1.0").join("powershell.exe"), + PathBuf::from(&system_root) + .join("System32") + .join("WindowsPowerShell") + .join("v1.0") + .join("powershell.exe"), PathBuf::from(&system_root).join("System32").join("cmd.exe"), ] { if path.is_file() && !out.iter().any(|existing| same_file_name(existing, &path)) { @@ -199,7 +205,8 @@ fn local_shell_paths() -> Vec { #[cfg(not(unix))] fn same_file_name(a: &Path, b: &Path) -> bool { a.file_name().is_some_and(|a_name| { - b.file_name().is_some_and(|b_name| a_name.eq_ignore_ascii_case(b_name)) + b.file_name() + .is_some_and(|b_name| a_name.eq_ignore_ascii_case(b_name)) }) } @@ -252,7 +259,9 @@ fn wsl_distributions() -> Vec { if raw.len() < 2 { return Vec::new(); } - let u16_iter = raw.chunks_exact(2).map(|c| u16::from_le_bytes([c[0], c[1]])); + let u16_iter = raw + .chunks_exact(2) + .map(|c| u16::from_le_bytes([c[0], c[1]])); let decoded = String::from_utf16_lossy(&u16_iter.collect::>()); decoded .lines() @@ -262,4 +271,3 @@ fn wsl_distributions() -> Vec { .filter(|line| !line.is_empty()) .collect() } - diff --git a/src/connection.rs b/src/connection.rs index bc51b8d..faa4492 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -9,7 +9,8 @@ fn require_binary(name: &str) -> Result<()> { return Ok(()); } let hint = match name { - "ssh" => "\n\ + "ssh" => { + "\n\ sshell requires `ssh` to connect via SSH.\n\ \n\ Install it with:\n\ @@ -18,8 +19,9 @@ fn require_binary(name: &str) -> Result<()> { Arch: sudo pacman -S openssh\n\ Fedora: sudo dnf install openssh-clients\n\ Windows: Settings → Apps → Optional Features → OpenSSH Client" - , - "sshpass" => "\n\ + } + "sshpass" => { + "\n\ Password-based SSH login requires `sshpass`.\n\ Consider switching to private-key auth instead, or install it:\n\ macOS: brew install hudochenkov/sshpass/sshpass\n\ @@ -27,7 +29,7 @@ fn require_binary(name: &str) -> Result<()> { Arch: sudo pacman -S sshpass\n\ Fedora: sudo dnf install sshpass\n\ Windows: not available — use private-key auth" - , + } _ => "", }; bail!("command not found: `{name}`{hint}"); @@ -47,10 +49,7 @@ pub fn connect(name: &str, cfg: &SshellConfig) -> Result<()> { auth_ref, .. } => connect_ssh(cfg, host, *port, user, auth_ref), - ConnectionType::Shell { - command, - .. - } => { + ConnectionType::Shell { command, .. } => { let merged_args = profile.merged_shell_args(); exec_shell(command, &merged_args) } diff --git a/src/import.rs b/src/import.rs index 3df16ea..1564295 100644 --- a/src/import.rs +++ b/src/import.rs @@ -88,10 +88,9 @@ pub fn import_candidates(cfg: &mut SshellConfig, candidates: &[ImportCandidate]) } else { match key_content { Some(content) => { - cfg.credentials.entries.insert( - auth_ref.clone(), - CredentialEntry::private_key(content), - ); + cfg.credentials + .entries + .insert(auth_ref.clone(), CredentialEntry::private_key(content)); tags.push("key".to_string()); } None => { diff --git a/src/sync.rs b/src/sync.rs index 010a7cb..9ac6681 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -56,10 +56,7 @@ struct RemotePayload { deleted: IndexMap, } -fn parse_remote_payload( - remote: toml::Value, - sync_password: Option<&str>, -) -> Result { +fn parse_remote_payload(remote: toml::Value, sync_password: Option<&str>) -> Result { let mut connections = IndexMap::new(); if let Some(conns) = remote.get("connections").and_then(|v| v.as_table()) { for (name, profile_val) in conns { @@ -124,9 +121,7 @@ pub(crate) fn bidirectional_merge( report.skipped += 1; } } - Some(local_profile) - if remote_profile.modified_at > local_profile.modified_at => - { + Some(local_profile) if remote_profile.modified_at > local_profile.modified_at => { // Remote is newer → update local (preserve local-only fields) let mut p = remote_profile.clone(); p.local_tags = local_profile.local_tags.clone(); @@ -148,12 +143,7 @@ pub(crate) fn bidirectional_merge( } if let ConnectionType::Shell { .. } = &p.kind { // Re-localize the shell profile for this machine - let preserved = ( - p.local_tags.clone(), - p.usage_count, - p.added_order, - p.source, - ); + let preserved = (p.local_tags.clone(), p.usage_count, p.added_order, p.source); if localize_shell_profile(cfg, name, &mut p) { p.local_tags = preserved.0; p.usage_count = preserved.1; @@ -290,9 +280,10 @@ pub(crate) fn build_sync_payload( .filter_map(|(_, profile)| profile.auth_ref().map(String::from)) .collect(); - payload.credentials.entries.retain(|name, _| { - name != GIST_TOKEN_REF && synced_refs.iter().any(|r| r == name) - }); + payload + .credentials + .entries + .retain(|name, _| name != GIST_TOKEN_REF && synced_refs.iter().any(|r| r == name)); let encrypted = if !payload.credentials.entries.is_empty() { if let Some(pw) = sync_password { @@ -365,10 +356,7 @@ pub(crate) fn build_sync_payload( } pub(crate) fn count_synced(cfg: &SshellConfig) -> usize { - cfg.connections - .iter() - .filter(|(_, p)| p.sync()) - .count() + cfg.connections.iter().filter(|(_, p)| p.sync()).count() } pub(crate) fn to_toml_value(val: &T) -> Result { diff --git a/src/sync/gist.rs b/src/sync/gist.rs index bb27847..145371e 100644 --- a/src/sync/gist.rs +++ b/src/sync/gist.rs @@ -1,5 +1,5 @@ +use super::{GIST_TOKEN_REF, SyncReport, bidirectional_merge, build_sync_payload, count_synced}; use crate::config::{CredentialEntry, SshellConfig}; -use super::{GIST_TOKEN_REF, SyncReport, build_sync_payload, bidirectional_merge, count_synced}; use anyhow::{Context, Result, bail}; use reqwest::blocking::Client; use serde_json::json; @@ -20,9 +20,7 @@ pub fn sync(cfg: &mut SshellConfig) -> Result { if response.status().is_success() { let value: serde_json::Value = response.json()?; if let Some(content) = value["files"][FILE_NAME]["content"].as_str() { - Some( - toml::from_str(content).with_context(|| "failed to parse remote config")?, - ) + Some(toml::from_str(content).with_context(|| "failed to parse remote config")?) } else { None } diff --git a/src/sync/webdav.rs b/src/sync/webdav.rs index ba0fe8f..0e3fe7e 100644 --- a/src/sync/webdav.rs +++ b/src/sync/webdav.rs @@ -1,5 +1,5 @@ -use crate::config::SshellConfig; use super::{SyncReport, bidirectional_merge, build_sync_payload, count_synced}; +use crate::config::SshellConfig; use anyhow::{Context, Result, bail}; use reqwest::blocking::Client; use reqwest::header::{ACCEPT, CONTENT_TYPE}; @@ -29,10 +29,7 @@ pub fn sync(cfg: &mut SshellConfig) -> Result { .send()?; if response.status().is_success() { let content = response.text()?; - Some( - toml::from_str(&content) - .with_context(|| "failed to parse remote config")?, - ) + Some(toml::from_str(&content).with_context(|| "failed to parse remote config")?) } else { None } diff --git a/src/ui.rs b/src/ui.rs index a11008e..1b82439 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -54,7 +54,12 @@ pub fn draw(frame: &mut Frame<'_>, app: &mut crate::app::App) { .fg(ACCENT) .alignment(Alignment::Center) .render( - Rect { x: 0, y: area.height / 2, width: area.width, height: 1 }, + Rect { + x: 0, + y: area.height / 2, + width: area.width, + height: 1, + }, frame.buffer_mut(), ); return; diff --git a/src/ui/app.rs b/src/ui/app.rs index 6b25752..eb97a30 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -132,10 +132,13 @@ fn spawn_latency_probes(app: &App) { // Mark as "in-flight" by inserting a fresh entry { let mut cache = app.session.latency.lock().unwrap(); - cache.insert(key.clone(), CacheEntry { - status: crate::app::latency::LatencyStatus::Unknown, - checked_at: now, - }); + cache.insert( + key.clone(), + CacheEntry { + status: crate::app::latency::LatencyStatus::Unknown, + checked_at: now, + }, + ); } let cache_clone = app.session.latency.clone(); let host_port = key.clone(); @@ -147,10 +150,13 @@ fn spawn_latency_probes(app: &App) { }; let status = crate::app::latency::probe(host, port); if let Ok(mut cache) = cache_clone.lock() { - cache.insert(host_port, CacheEntry { - status, - checked_at: Instant::now(), - }); + cache.insert( + host_port, + CacheEntry { + status, + checked_at: Instant::now(), + }, + ); } }); } diff --git a/src/ui/view.rs b/src/ui/view.rs index 0da488e..2e59729 100644 --- a/src/ui/view.rs +++ b/src/ui/view.rs @@ -74,23 +74,62 @@ pub fn handle_form_nav(form: &mut F, key: KeyEvent) -> Option { form.nav_next(); None } - KeyCode::Up => { form.nav_prev(); None } + KeyCode::Down => { + form.nav_next(); + None + } + KeyCode::Up => { + form.nav_prev(); + None + } KeyCode::Tab if form.active_is_toggle() => Some(FormAction::Toggle), KeyCode::Enter => Some(FormAction::Save), KeyCode::Esc => Some(FormAction::Cancel), - KeyCode::Backspace if form.active_is_text() => { form.delete_char(); None } - KeyCode::Delete if form.active_is_text() => { form.delete_next_char(); None } - KeyCode::Left if form.active_is_text() => { form.move_cursor_left(); None } - KeyCode::Right if form.active_is_text() => { form.move_cursor_right(); None } - KeyCode::Home if form.active_is_text() => { form.cursor_home(); None } - KeyCode::End if form.active_is_text() => { form.cursor_end(); None } - KeyCode::Char('a') if ctrl && form.active_is_text() => { form.cursor_home(); None } - KeyCode::Char('e') if ctrl && form.active_is_text() => { form.cursor_end(); None } - KeyCode::Char('u') if ctrl && form.active_is_text() => { form.clear_field(); None } - KeyCode::Char(' ') if form.active_is_text() => { form.insert_char(' '); None } - KeyCode::Char(c) if !ctrl && form.active_is_text() => { form.insert_char(c); None } + KeyCode::Backspace if form.active_is_text() => { + form.delete_char(); + None + } + KeyCode::Delete if form.active_is_text() => { + form.delete_next_char(); + None + } + KeyCode::Left if form.active_is_text() => { + form.move_cursor_left(); + None + } + KeyCode::Right if form.active_is_text() => { + form.move_cursor_right(); + None + } + KeyCode::Home if form.active_is_text() => { + form.cursor_home(); + None + } + KeyCode::End if form.active_is_text() => { + form.cursor_end(); + None + } + KeyCode::Char('a') if ctrl && form.active_is_text() => { + form.cursor_home(); + None + } + KeyCode::Char('e') if ctrl && form.active_is_text() => { + form.cursor_end(); + None + } + KeyCode::Char('u') if ctrl && form.active_is_text() => { + form.clear_field(); + None + } + KeyCode::Char(' ') if form.active_is_text() => { + form.insert_char(' '); + None + } + KeyCode::Char(c) if !ctrl && form.active_is_text() => { + form.insert_char(c); + None + } _ => None, } } diff --git a/src/ui/view/action_menu.rs b/src/ui/view/action_menu.rs index 4454ef4..fc04fcd 100644 --- a/src/ui/view/action_menu.rs +++ b/src/ui/view/action_menu.rs @@ -24,13 +24,11 @@ const ACTIONS: &[(&str, &str)] = &[ pub struct ActionMenuView; impl View for ActionMenuView { - fn title(&self) -> &'static str { "Actions" } + fn title(&self) -> &'static str { + "Actions" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { - vec![ - ("j/k", "move"), - ("Enter", "select"), - ("Esc", "cancel"), - ] + vec![("j/k", "move"), ("Enter", "select"), ("Esc", "cancel")] } fn draw(&self, frame: &mut Frame<'_>, _app: &App, area: Rect) { diff --git a/src/ui/view/cred_form.rs b/src/ui/view/cred_form.rs index 9ef2433..af9f09f 100644 --- a/src/ui/view/cred_form.rs +++ b/src/ui/view/cred_form.rs @@ -11,7 +11,9 @@ use ratatui::{Frame, layout::Rect}; pub struct CredFormView; impl View for CredFormView { - fn title(&self) -> &'static str { "Cred Editor" } + fn title(&self) -> &'static str { + "Cred Editor" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { vec![ ("↑/↓", "move"), @@ -57,16 +59,17 @@ impl View for CredFormView { }); } else { let raw = form.field_value(field).to_string(); - let (display, secret_cursor) = if matches!(field, CredFormField::Value) && !raw.is_empty() { - if active && matches!(form.kind, AuthKind::Password) { - let d: String = "*".repeat(raw.chars().count()); - (d, form.cursor) + let (display, secret_cursor) = + if matches!(field, CredFormField::Value) && !raw.is_empty() { + if active && matches!(form.kind, AuthKind::Password) { + let d: String = "*".repeat(raw.chars().count()); + (d, form.cursor) + } else { + ("".into(), 0) + } } else { - ("".into(), 0) - } - } else { - (raw, form.cursor) - }; + (raw, form.cursor) + }; let cursor = if active && field.is_text() { secret_cursor.min(char_len(&display)) } else { diff --git a/src/ui/view/cred_list.rs b/src/ui/view/cred_list.rs index 5c0b490..16652be 100644 --- a/src/ui/view/cred_list.rs +++ b/src/ui/view/cred_list.rs @@ -17,7 +17,9 @@ use ratatui::{ pub struct CredListView; impl View for CredListView { - fn title(&self) -> &'static str { "Credentials" } + fn title(&self) -> &'static str { + "Credentials" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { vec![ ("j/k", "move"), diff --git a/src/ui/view/form.rs b/src/ui/view/form.rs index 971c6ea..1fe2c00 100644 --- a/src/ui/view/form.rs +++ b/src/ui/view/form.rs @@ -11,7 +11,9 @@ use ratatui::{Frame, layout::Rect}; pub struct FormView; impl View for FormView { - fn title(&self) -> &'static str { "Editor" } + fn title(&self) -> &'static str { + "Editor" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { vec![ ("↑/↓", "move"), diff --git a/src/ui/view/home_list.rs b/src/ui/view/home_list.rs index 1acdf71..d4f6aba 100644 --- a/src/ui/view/home_list.rs +++ b/src/ui/view/home_list.rs @@ -1,12 +1,12 @@ -use crate::app::{App, Mode}; use crate::app::display_name; use crate::app::latency::LatencyStatus; +use crate::app::{App, Mode}; use crate::config::{ConnectionSource, ConnectionType, CredentialEntry}; use crate::ui::component::{badge_span, draw_input, panel, tag_badge}; use crate::ui::{ACCENT, BLUE, GREEN, MUTED, PANEL_ALT, PURPLE, RED, SELECTED_BG, TEXT, YELLOW}; use super::View; -use super::{section_row, scroll_indexed_rows}; +use super::{scroll_indexed_rows, section_row}; use anyhow::Result; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; @@ -21,7 +21,9 @@ use ratatui::{ pub struct HomeListView; impl View for HomeListView { - fn title(&self) -> &'static str { "Home" } + fn title(&self) -> &'static str { + "Home" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { vec![ ("j/k", "move"), @@ -226,10 +228,7 @@ fn connection_row( format!("{user}@{host}:{port}") } } - ConnectionType::Shell { - command, - .. - } => { + ConnectionType::Shell { command, .. } => { let merged_args = profile.merged_shell_args(); if merged_args.is_empty() { command.clone() @@ -263,16 +262,24 @@ fn connection_row( }; let badge_style = if selected { - Style::default().fg(badge_color).bg(SELECTED_BG).add_modifier(Modifier::BOLD) + Style::default() + .fg(badge_color) + .bg(SELECTED_BG) + .add_modifier(Modifier::BOLD) } else { - Style::default().fg(crate::ui::BG).bg(badge_color).add_modifier(Modifier::BOLD) + Style::default() + .fg(crate::ui::BG) + .bg(badge_color) + .add_modifier(Modifier::BOLD) }; Row::new([ Cell::from(format!("{marker} {}", display_name(name))).style(row_style), - Cell::from(Line::from(vec![ - Span::styled(format!(" {} ", type_badge), badge_style), - ])).style(row_style), + Cell::from(Line::from(vec![Span::styled( + format!(" {} ", type_badge), + badge_style, + )])) + .style(row_style), Cell::from(target).style(row_style), Cell::from(ping_text).style(if selected { Style::default().fg(ping_color).bg(SELECTED_BG) @@ -312,7 +319,10 @@ pub fn draw_detail_panel(frame: &mut Frame<'_>, app: &App, area: Rect) { Span::raw(" "), badge_span(badge, badge_color), Span::raw(" "), - Span::styled(display_name(name).to_string(), Style::default().fg(TEXT).bold()), + Span::styled( + display_name(name).to_string(), + Style::default().fg(TEXT).bold(), + ), ])); lines.push(Line::from(vec![ Span::raw(" "), @@ -466,13 +476,11 @@ fn handle_home(app: &mut App, key: KeyEvent) -> Result<()> { pub struct SearchView; impl View for SearchView { - fn title(&self) -> &'static str { "Search" } + fn title(&self) -> &'static str { + "Search" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { - vec![ - ("type", "filter"), - ("j/k", "move"), - ("Esc", "close"), - ] + vec![("type", "filter"), ("j/k", "move"), ("Esc", "close")] } fn draw(&self, frame: &mut Frame<'_>, app: &App, area: Rect) { @@ -509,13 +517,11 @@ impl View for SearchView { pub struct QuickSelectView; impl View for QuickSelectView { - fn title(&self) -> &'static str { "Quick Select" } + fn title(&self) -> &'static str { + "Quick Select" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { - vec![ - ("1-9", "connect"), - ("Tab", "sort"), - ("Esc", "cancel"), - ] + vec![("1-9", "connect"), ("Tab", "sort"), ("Esc", "cancel")] } fn draw(&self, frame: &mut Frame<'_>, app: &App, area: Rect) { @@ -567,12 +573,11 @@ impl View for QuickSelectView { pub struct DeleteConfirmView; impl View for DeleteConfirmView { - fn title(&self) -> &'static str { "Delete" } + fn title(&self) -> &'static str { + "Delete" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { - vec![ - ("Y", "yes"), - ("N", "no"), - ] + vec![("Y", "yes"), ("N", "no")] } fn draw(&self, frame: &mut Frame<'_>, app: &App, area: Rect) { diff --git a/src/ui/view/import.rs b/src/ui/view/import.rs index dcda973..3ded3d6 100644 --- a/src/ui/view/import.rs +++ b/src/ui/view/import.rs @@ -3,7 +3,7 @@ use crate::ui::component::{ListAction, handle_list_nav, panel, panel_with_subtit use crate::ui::{BLUE, GREEN, MUTED, SELECTED_BG, TEXT}; use super::View; -use super::{section_row, scroll_indexed_rows}; +use super::{scroll_indexed_rows, section_row}; use anyhow::Result; use crossterm::event::{KeyCode, KeyEvent}; @@ -17,7 +17,9 @@ use ratatui::{ pub struct ImportView; impl View for ImportView { - fn title(&self) -> &'static str { "Import" } + fn title(&self) -> &'static str { + "Import" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { vec![ ("j/k", "move"), @@ -62,10 +64,19 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) { for (idx, item) in app.session.import.shell_candidates.iter().enumerate() { entry_row.push(rows.len()); let selected_row = idx == app.session.import.cursor; - let checked = app.session.import.shell_selected.get(idx).copied().unwrap_or(false); + let checked = app + .session + .import + .shell_selected + .get(idx) + .copied() + .unwrap_or(false); let has_conflict = item.conflict.is_some(); let style = if selected_row { - Style::default().bg(SELECTED_BG).fg(TEXT).add_modifier(Modifier::BOLD) + Style::default() + .bg(SELECTED_BG) + .fg(TEXT) + .add_modifier(Modifier::BOLD) } else if has_conflict { Style::default().fg(MUTED) } else { @@ -87,7 +98,8 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) { Style::default() }; rows.push(Row::new([ - Cell::from(if checked { " [x]" } else { " [ ]" }).style(check_style.patch(check_cell_style)), + Cell::from(if checked { " [x]" } else { " [ ]" }) + .style(check_style.patch(check_cell_style)), Cell::from(item.name.clone()).style(style), Cell::from(item.path.display().to_string()).style(style), Cell::from(status).style(style), @@ -104,9 +116,18 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) { for (idx, item) in app.session.import.candidates.iter().enumerate() { entry_row.push(rows.len()); let selected_row = (shell_len + idx) == app.session.import.cursor; - let checked = app.session.import.selected.get(idx).copied().unwrap_or(false); + let checked = app + .session + .import + .selected + .get(idx) + .copied() + .unwrap_or(false); let style = if selected_row { - Style::default().bg(SELECTED_BG).fg(TEXT).add_modifier(Modifier::BOLD) + Style::default() + .bg(SELECTED_BG) + .fg(TEXT) + .add_modifier(Modifier::BOLD) } else { Style::default().fg(TEXT) }; @@ -121,7 +142,8 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) { Style::default() }; rows.push(Row::new([ - Cell::from(if checked { " [x]" } else { " [ ]" }).style(check_style.patch(check_cell_style)), + Cell::from(if checked { " [x]" } else { " [ ]" }) + .style(check_style.patch(check_cell_style)), Cell::from(item.name.clone()).style(style), Cell::from(format!("{}@{}:{}", item.user, item.host, item.port)).style(style), Cell::from( @@ -219,8 +241,11 @@ fn handle_import(app: &mut App, key: KeyEvent) -> Result<()> { .get(app.session.import.cursor) .is_some_and(|c| c.conflict.is_none()); if can_toggle - && let Some(v) = - app.session.import.shell_selected.get_mut(app.session.import.cursor) + && let Some(v) = app + .session + .import + .shell_selected + .get_mut(app.session.import.cursor) { *v = !*v; } diff --git a/src/ui/view/settings.rs b/src/ui/view/settings.rs index f84e7ea..4249a42 100644 --- a/src/ui/view/settings.rs +++ b/src/ui/view/settings.rs @@ -12,7 +12,9 @@ use ratatui::{Frame, layout::Rect}; pub struct SettingsView; impl View for SettingsView { - fn title(&self) -> &'static str { "Settings" } + fn title(&self) -> &'static str { + "Settings" + } fn hints(&self) -> Vec<(&'static str, &'static str)> { vec![ ("↑/↓", "move"), @@ -41,11 +43,13 @@ impl View for SettingsView { SyncBackend::Gist => badge_span("Gist", ACCENT), SyncBackend::Webdav => badge_span("WebDAV", ORANGE), }, - SettingsField::SyncOnStart => if settings.sync_on_start { - badge_span("on", GREEN) - } else { - badge_span("off", MUTED) - }, + SettingsField::SyncOnStart => { + if settings.sync_on_start { + badge_span("on", GREEN) + } else { + badge_span("off", MUTED) + } + } _ => unreachable!(), }; rows.push(FormRow::Toggle { @@ -57,8 +61,7 @@ impl View for SettingsView { let raw = settings.field_text(field).to_string(); let is_secret = matches!( field, - SettingsField::SyncPassword - | SettingsField::WebdavPassword + SettingsField::SyncPassword | SettingsField::WebdavPassword ); let (display, secret_cursor) = if is_secret { if raw.is_empty() {