wraith/src-tauri/src/lib.rs
Vantz Stockwell 99ecbe739e
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 2m49s
feat: RDP clipboard paste, keyboard grab default ON, frame dirty flag
1. Clipboard paste (rdp_send_clipboard): simulates typing each character
   via scancode key press/release events. Full ASCII coverage including
   all symbols, numbers, and shifted characters. Handles 32-char
   generated passwords without manual typing.

2. Keyboard grab defaults to ON so RDP sessions accept keyboard input
   immediately without requiring the user to click the toolbar toggle.

3. Frame dirty flag: GraphicsUpdate sets an AtomicBool, get_frame only
   encodes + returns base64 when dirty (returns empty string otherwise).
   Eliminates ~8MB/frame base64 encoding on unchanged frames at 30fps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:57:16 -04:00

115 lines
5.1 KiB
Rust

pub mod db;
pub mod vault;
pub mod settings;
pub mod connections;
pub mod credentials;
pub mod ssh;
pub mod sftp;
pub mod rdp;
pub mod theme;
pub mod workspace;
pub mod ai;
pub mod commands;
use std::path::PathBuf;
use std::sync::Mutex;
use db::Database;
use vault::VaultService;
use credentials::CredentialService;
use settings::SettingsService;
use connections::ConnectionService;
use sftp::SftpService;
use ssh::session::SshService;
use rdp::RdpService;
use theme::ThemeService;
use workspace::WorkspaceService;
pub struct AppState {
pub db: Database,
pub vault: Mutex<Option<VaultService>>,
pub settings: SettingsService,
pub connections: ConnectionService,
pub credentials: Mutex<Option<CredentialService>>,
pub ssh: SshService,
pub sftp: SftpService,
pub rdp: RdpService,
pub theme: ThemeService,
pub workspace: WorkspaceService,
pub gemini: Mutex<Option<ai::GeminiClient>>,
}
impl AppState {
pub fn new(data_dir: PathBuf) -> Result<Self, Box<dyn std::error::Error>> {
std::fs::create_dir_all(&data_dir)?;
let database = Database::open(&data_dir.join("wraith.db"))?;
database.migrate()?;
Ok(Self {
db: database.clone(),
vault: Mutex::new(None),
settings: SettingsService::new(database.clone()),
connections: ConnectionService::new(database.clone()),
credentials: Mutex::new(None),
ssh: SshService::new(database.clone()),
sftp: SftpService::new(),
rdp: RdpService::new(),
theme: ThemeService::new(database.clone()),
workspace: WorkspaceService::new(SettingsService::new(database.clone())),
gemini: Mutex::new(None),
})
}
pub fn is_first_run(&self) -> bool {
self.settings.get("vault_salt").unwrap_or_default().is_empty()
}
pub fn is_unlocked(&self) -> bool {
self.vault.lock().unwrap().is_some()
}
}
pub fn data_directory() -> PathBuf {
if let Ok(appdata) = std::env::var("APPDATA") { return PathBuf::from(appdata).join("Wraith"); }
if let Ok(home) = std::env::var("HOME") {
if cfg!(target_os = "macos") { return PathBuf::from(home).join("Library").join("Application Support").join("Wraith"); }
if let Ok(xdg) = std::env::var("XDG_DATA_HOME") { return PathBuf::from(xdg).join("wraith"); }
return PathBuf::from(home).join(".local").join("share").join("wraith");
}
PathBuf::from(".")
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let app_state = AppState::new(data_directory()).expect("Failed to init AppState");
app_state.theme.seed_builtins();
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.manage(app_state)
.setup(|app| {
#[cfg(debug_assertions)]
{
use tauri::Manager;
if let Some(window) = app.get_webview_window("main") {
window.open_devtools();
}
}
let _ = app;
Ok(())
})
.invoke_handler(tauri::generate_handler![
commands::vault::is_first_run, commands::vault::create_vault, commands::vault::unlock, commands::vault::is_unlocked,
commands::settings::get_setting, commands::settings::set_setting,
commands::connections::list_connections, commands::connections::create_connection, commands::connections::get_connection, commands::connections::update_connection, commands::connections::delete_connection,
commands::connections::list_groups, commands::connections::create_group, commands::connections::delete_group, commands::connections::rename_group, commands::connections::search_connections,
commands::credentials::list_credentials, commands::credentials::create_password, commands::credentials::create_ssh_key, commands::credentials::delete_credential, commands::credentials::decrypt_password, commands::credentials::decrypt_ssh_key,
commands::ssh_commands::connect_ssh, commands::ssh_commands::connect_ssh_with_key, commands::ssh_commands::ssh_write, commands::ssh_commands::ssh_resize, commands::ssh_commands::disconnect_ssh, commands::ssh_commands::disconnect_session, commands::ssh_commands::list_ssh_sessions,
commands::sftp_commands::sftp_list, commands::sftp_commands::sftp_read_file, commands::sftp_commands::sftp_write_file, commands::sftp_commands::sftp_mkdir, commands::sftp_commands::sftp_delete, commands::sftp_commands::sftp_rename,
commands::rdp_commands::connect_rdp, commands::rdp_commands::rdp_get_frame, commands::rdp_commands::rdp_send_mouse, commands::rdp_commands::rdp_send_key, commands::rdp_commands::rdp_send_clipboard, commands::rdp_commands::disconnect_rdp, commands::rdp_commands::list_rdp_sessions,
commands::theme_commands::list_themes, commands::theme_commands::get_theme,
commands::ai_commands::set_gemini_auth, commands::ai_commands::gemini_chat, commands::ai_commands::is_gemini_authenticated,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}