All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 2m49s
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>
115 lines
5.1 KiB
Rust
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");
|
|
}
|