style: formatting

This commit is contained in:
ByteAtATime 2025-06-19 20:49:39 -07:00
parent 41c880f9d5
commit 00eb6d6e18
No known key found for this signature in database
6 changed files with 253 additions and 236 deletions

View file

@ -56,7 +56,8 @@ enum IncomingMessage {
pub struct WsState { pub struct WsState {
pub connection: Arc<Mutex<Option<tokio::sync::mpsc::Sender<String>>>>, pub connection: Arc<Mutex<Option<tokio::sync::mpsc::Sender<String>>>>,
pub is_connected: Arc<Mutex<bool>>, pub is_connected: Arc<Mutex<bool>>,
pub pending_requests: Arc<Mutex<HashMap<u64, oneshot::Sender<Result<serde_json::Value, String>>>>>, pub pending_requests:
Arc<Mutex<HashMap<u64, oneshot::Sender<Result<serde_json::Value, String>>>>>,
pub request_id_counter: Arc<Mutex<u64>>, pub request_id_counter: Arc<Mutex<u64>>,
} }
@ -90,7 +91,11 @@ async fn handle_connection(stream: TcpStream, app_handle: AppHandle) {
let sender_task = tokio::spawn(async move { let sender_task = tokio::spawn(async move {
while let Some(msg_to_send) = rx.recv().await { while let Some(msg_to_send) = rx.recv().await {
if ws_sender.send(Message::Binary(msg_to_send.into())).await.is_err() { if ws_sender
.send(Message::Binary(msg_to_send.into()))
.await
.is_err()
{
break; break;
} }
} }
@ -113,14 +118,24 @@ async fn handle_connection(stream: TcpStream, app_handle: AppHandle) {
"result": null, "result": null,
"id": id "id": id
}); });
let tx = app_clone_for_receiver.state::<WsState>().connection.lock().unwrap().clone(); let tx = app_clone_for_receiver
.state::<WsState>()
.connection
.lock()
.unwrap()
.clone();
if let Some(tx) = tx { if let Some(tx) = tx {
let _ = tx.send(response.to_string()).await; let _ = tx.send(response.to_string()).await;
} }
} }
} }
Ok(IncomingMessage::Response { id, result, error }) => { Ok(IncomingMessage::Response { id, result, error }) => {
let sender = app_clone_for_receiver.state::<WsState>().pending_requests.lock().unwrap().remove(&id); let sender = app_clone_for_receiver
.state::<WsState>()
.pending_requests
.lock()
.unwrap()
.remove(&id);
if let Some(sender) = sender { if let Some(sender) = sender {
if !error.is_null() { if !error.is_null() {
let _ = sender.send(Err(error.to_string())); let _ = sender.send(Err(error.to_string()));
@ -161,7 +176,9 @@ pub async fn run_server(app_handle: AppHandle) {
} }
#[tauri::command] #[tauri::command]
pub async fn browser_extension_check_connection(state: tauri::State<'_, WsState>) -> Result<bool, String> { pub async fn browser_extension_check_connection(
state: tauri::State<'_, WsState>,
) -> Result<bool, String> {
Ok(*state.is_connected.lock().unwrap()) Ok(*state.is_connected.lock().unwrap())
} }
@ -172,7 +189,7 @@ pub async fn browser_extension_request(
state: tauri::State<'_, WsState>, state: tauri::State<'_, WsState>,
) -> Result<serde_json::Value, String> { ) -> Result<serde_json::Value, String> {
use std::time::Duration; use std::time::Duration;
let tx = { let tx = {
let lock = state.connection.lock().unwrap(); let lock = state.connection.lock().unwrap();
lock.clone() lock.clone()
@ -212,4 +229,3 @@ pub async fn browser_extension_request(
Err("Browser extension not connected".into()) Err("Browser extension not connected".into())
} }
} }

View file

@ -5,119 +5,121 @@ use tauri_plugin_clipboard_manager::ClipboardExt;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ReadResult { pub struct ReadResult {
text: Option<String>, text: Option<String>,
html: Option<String>, html: Option<String>,
file: Option<String>, file: Option<String>,
} }
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ClipboardContent { pub struct ClipboardContent {
text: Option<String>, text: Option<String>,
html: Option<String>, html: Option<String>,
file: Option<String>, file: Option<String>,
} }
#[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)] #[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CopyOptions { pub struct CopyOptions {
concealed: Option<bool>, concealed: Option<bool>,
} }
#[tauri::command] #[tauri::command]
pub async fn clipboard_read_text(app: tauri::AppHandle) -> Result<ReadResult, String> { pub async fn clipboard_read_text(app: tauri::AppHandle) -> Result<ReadResult, String> {
let clipboard = app.clipboard(); let clipboard = app.clipboard();
let text = clipboard.read_text().ok(); let text = clipboard.read_text().ok();
Ok(ReadResult { Ok(ReadResult {
text, text,
html: None, html: None,
file: None file: None,
}) })
} }
#[tauri::command] #[tauri::command]
pub async fn clipboard_read(app: tauri::AppHandle) -> Result<ReadResult, String> { pub async fn clipboard_read(app: tauri::AppHandle) -> Result<ReadResult, String> {
let clipboard = app.clipboard(); let clipboard = app.clipboard();
let text = clipboard.read_text().ok(); let text = clipboard.read_text().ok();
let html = None; // read_html is not supported by the plugin let html = None; // read_html is not supported by the plugin
let file = if let Some(ref text_content) = text { let file = if let Some(ref text_content) = text {
if text_content.lines().count() == 1 if text_content.lines().count() == 1
&& (text_content.starts_with('/') || text_content.starts_with("file://")) && (text_content.starts_with('/') || text_content.starts_with("file://"))
{ {
Some(text_content.clone()) Some(text_content.clone())
} else { } else {
None None
} }
} else { } else {
None None
}; };
Ok(ReadResult { text, html, file }) Ok(ReadResult { text, html, file })
} }
#[tauri::command] #[tauri::command]
pub async fn clipboard_copy( pub async fn clipboard_copy(
app: tauri::AppHandle, app: tauri::AppHandle,
content: ClipboardContent, content: ClipboardContent,
_options: Option<CopyOptions> _options: Option<CopyOptions>,
) -> Result<(), String> { ) -> Result<(), String> {
let clipboard = app.clipboard(); let clipboard = app.clipboard();
if let Some(file_path) = &content.file { if let Some(file_path) = &content.file {
clipboard clipboard
.write_text(file_path.clone()) .write_text(file_path.clone())
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
} else if let Some(html) = &content.html { } else if let Some(html) = &content.html {
clipboard clipboard
.write_html(html.clone(), content.text) .write_html(html.clone(), content.text)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
} else if let Some(text) = &content.text { } else if let Some(text) = &content.text {
clipboard.write_text(text.clone()).map_err(|e| e.to_string())?; clipboard
} .write_text(text.clone())
.map_err(|e| e.to_string())?;
}
Ok(()) Ok(())
} }
#[tauri::command] #[tauri::command]
pub async fn clipboard_paste( pub async fn clipboard_paste(
app: tauri::AppHandle, app: tauri::AppHandle,
content: ClipboardContent content: ClipboardContent,
) -> Result<(), String> { ) -> Result<(), String> {
let clipboard = app.clipboard(); let clipboard = app.clipboard();
let original_text = clipboard.read_text().ok(); let original_text = clipboard.read_text().ok();
clipboard_copy(app.clone(), content, None).await?; clipboard_copy(app.clone(), content, None).await?;
thread::sleep(Duration::from_millis(100)); thread::sleep(Duration::from_millis(100));
let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?; let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
enigo.key(Key::Meta, enigo::Direction::Press).ok(); enigo.key(Key::Meta, enigo::Direction::Press).ok();
enigo.key(Key::Unicode('v'), enigo::Direction::Click).ok(); enigo.key(Key::Unicode('v'), enigo::Direction::Click).ok();
enigo.key(Key::Meta, enigo::Direction::Release).ok(); enigo.key(Key::Meta, enigo::Direction::Release).ok();
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
{ {
enigo.key(Key::Control, enigo::Direction::Press).ok(); enigo.key(Key::Control, enigo::Direction::Press).ok();
enigo.key(Key::Unicode('v'), enigo::Direction::Click).ok(); enigo.key(Key::Unicode('v'), enigo::Direction::Click).ok();
enigo.key(Key::Control, enigo::Direction::Release).ok(); enigo.key(Key::Control, enigo::Direction::Release).ok();
} }
thread::sleep(Duration::from_millis(100)); thread::sleep(Duration::from_millis(100));
if let Some(text) = original_text { if let Some(text) = original_text {
clipboard.write_text(text).map_err(|e| e.to_string())?; clipboard.write_text(text).map_err(|e| e.to_string())?;
} else { } else {
clipboard.clear().map_err(|e| e.to_string())?; clipboard.clear().map_err(|e| e.to_string())?;
} }
Ok(()) Ok(())
} }
#[tauri::command] #[tauri::command]
pub async fn clipboard_clear(app: tauri::AppHandle) -> Result<(), String> { pub async fn clipboard_clear(app: tauri::AppHandle) -> Result<(), String> {
app.clipboard().clear().map_err(|e| e.to_string()) app.clipboard().clear().map_err(|e| e.to_string())
} }

View file

@ -112,4 +112,4 @@ pub async fn install_extension(
} }
Ok(()) Ok(())
} }

View file

@ -263,4 +263,4 @@ async fn get_selected_finder_items_linux() -> Result<Vec<FileSystemItem>, String
} }
Err("Could not determine selected files. Please copy them to your clipboard.".to_string()) Err("Could not determine selected files. Please copy them to your clipboard.".to_string())
} }

View file

@ -14,142 +14,142 @@ use selection::get_text;
use std::process::Command; use std::process::Command;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use tauri::{Manager, Emitter}; use tauri::{Emitter, Manager};
use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_deep_link::DeepLinkExt;
#[tauri::command] #[tauri::command]
fn get_installed_apps() -> Vec<App> { fn get_installed_apps() -> Vec<App> {
match AppCache::get_apps() { match AppCache::get_apps() {
Ok(apps) => apps, Ok(apps) => apps,
Err(e) => { Err(e) => {
eprintln!("Failed to get apps: {:?}", e); eprintln!("Failed to get apps: {:?}", e);
Vec::new() Vec::new()
} }
} }
} }
#[tauri::command] #[tauri::command]
fn launch_app(exec: String) -> Result<(), String> { fn launch_app(exec: String) -> Result<(), String> {
let exec_parts: Vec<&str> = exec.split_whitespace().collect(); let exec_parts: Vec<&str> = exec.split_whitespace().collect();
if exec_parts.is_empty() { if exec_parts.is_empty() {
return Err("Empty exec command".to_string()); return Err("Empty exec command".to_string());
} }
let mut command = Command::new(exec_parts[0]); let mut command = Command::new(exec_parts[0]);
for arg in &exec_parts[1..] { for arg in &exec_parts[1..] {
if !arg.starts_with('%') { if !arg.starts_with('%') {
command.arg(arg); command.arg(arg);
} }
} }
command command
.spawn() .spawn()
.map_err(|e| format!("Failed to launch app: {}", e))?; .map_err(|e| format!("Failed to launch app: {}", e))?;
Ok(()) Ok(())
} }
#[tauri::command] #[tauri::command]
fn get_selected_text() -> String { fn get_selected_text() -> String {
get_text() get_text()
} }
fn setup_background_refresh() { fn setup_background_refresh() {
thread::spawn(|| { thread::spawn(|| {
thread::sleep(Duration::from_secs(60)); thread::sleep(Duration::from_secs(60));
loop { loop {
AppCache::refresh_background(); AppCache::refresh_background();
thread::sleep(Duration::from_secs(300)); thread::sleep(Duration::from_secs(300));
} }
}); });
} }
fn setup_global_shortcut(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { fn setup_global_shortcut(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
use tauri_plugin_global_shortcut::{ use tauri_plugin_global_shortcut::{
Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState,
}; };
let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space); let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space);
let handle = app.handle().clone(); let handle = app.handle().clone();
println!("Spotlight shortcut: {:?}", spotlight_shortcut); println!("Spotlight shortcut: {:?}", spotlight_shortcut);
app.handle().plugin( app.handle().plugin(
tauri_plugin_global_shortcut::Builder::new() tauri_plugin_global_shortcut::Builder::new()
.with_handler(move |_app, shortcut, event| { .with_handler(move |_app, shortcut, event| {
println!("Shortcut: {:?}, Event: {:?}", shortcut, event); println!("Shortcut: {:?}, Event: {:?}", shortcut, event);
if shortcut == &spotlight_shortcut && event.state() == ShortcutState::Pressed { if shortcut == &spotlight_shortcut && event.state() == ShortcutState::Pressed {
let spotlight_window = handle.get_webview_window("raycast-linux").unwrap(); let spotlight_window = handle.get_webview_window("raycast-linux").unwrap();
println!("Spotlight window: {:?}", spotlight_window); println!("Spotlight window: {:?}", spotlight_window);
if spotlight_window.is_visible().unwrap_or(false) { if spotlight_window.is_visible().unwrap_or(false) {
spotlight_window.hide().unwrap(); spotlight_window.hide().unwrap();
} else { } else {
spotlight_window.show().unwrap(); spotlight_window.show().unwrap();
spotlight_window.set_focus().unwrap(); spotlight_window.set_focus().unwrap();
} }
} }
}) })
.build() .build(),
)?; )?;
app.global_shortcut().register(spotlight_shortcut)?; app.global_shortcut().register(spotlight_shortcut)?;
Ok(()) Ok(())
} }
#[cfg_attr(mobile, tauri::mobile_entry_point)] #[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() { pub fn run() {
tauri::Builder::default() tauri::Builder::default()
.manage(WsState::default()) .manage(WsState::default())
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| { .plugin(tauri_plugin_single_instance::init(|app, args, _cwd| {
if args.len() > 1 && args[1].starts_with("raycast://") { if args.len() > 1 && args[1].starts_with("raycast://") {
if let Some(window) = app.get_webview_window("main") { if let Some(window) = app.get_webview_window("main") {
let _ = window.emit("deep-link", args[1].to_string()); let _ = window.emit("deep-link", args[1].to_string());
window.show().unwrap(); window.show().unwrap();
} }
return; return;
} }
if let Some(window) = app.get_webview_window("main") { if let Some(window) = app.get_webview_window("main") {
if let Ok(true) = window.is_visible() { if let Ok(true) = window.is_visible() {
let _ = window.hide(); let _ = window.hide();
} else { } else {
let _ = window.show(); let _ = window.show();
let _ = window.set_focus(); let _ = window.set_focus();
} }
} }
})) }))
.plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
get_installed_apps, get_installed_apps,
launch_app, launch_app,
get_selected_text, get_selected_text,
filesystem::get_selected_finder_items, filesystem::get_selected_finder_items,
extensions::install_extension, extensions::install_extension,
browser_extension::browser_extension_check_connection, browser_extension::browser_extension_check_connection,
browser_extension::browser_extension_request, browser_extension::browser_extension_request,
clipboard::clipboard_read_text, clipboard::clipboard_read_text,
clipboard::clipboard_read, clipboard::clipboard_read,
clipboard::clipboard_copy, clipboard::clipboard_copy,
clipboard::clipboard_paste, clipboard::clipboard_paste,
clipboard::clipboard_clear, clipboard::clipboard_clear,
oauth::oauth_set_tokens, oauth::oauth_set_tokens,
oauth::oauth_get_tokens, oauth::oauth_get_tokens,
oauth::oauth_remove_tokens oauth::oauth_remove_tokens
]) ])
.setup(|app| { .setup(|app| {
let app_handle = app.handle().clone(); let app_handle = app.handle().clone();
tauri::async_runtime::spawn(browser_extension::run_server(app_handle)); tauri::async_runtime::spawn(browser_extension::run_server(app_handle));
setup_background_refresh(); setup_background_refresh();
setup_global_shortcut(app)?; setup_global_shortcut(app)?;
Ok(()) Ok(())
}) })
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");
} }

View file

@ -8,80 +8,79 @@ use tauri::Manager;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct StoredTokenSet { struct StoredTokenSet {
access_token: String, access_token: String,
refresh_token: Option<String>, refresh_token: Option<String>,
expires_in: Option<u64>, expires_in: Option<u64>,
scope: Option<String>, scope: Option<String>,
id_token: Option<String>, id_token: Option<String>,
updated_at: String, updated_at: String,
} }
type TokenStore = HashMap<String, StoredTokenSet>; type TokenStore = HashMap<String, StoredTokenSet>;
fn get_storage_path(app: &tauri::AppHandle) -> Result<PathBuf, String> { fn get_storage_path(app: &tauri::AppHandle) -> Result<PathBuf, String> {
let data_dir = app let data_dir = app
.path() .path()
.app_local_data_dir() .app_local_data_dir()
.map_err(|_| "Failed to get app local data dir".to_string())?; .map_err(|_| "Failed to get app local data dir".to_string())?;
if !data_dir.exists() { if !data_dir.exists() {
fs::create_dir_all(&data_dir).map_err(|e| e.to_string())?; fs::create_dir_all(&data_dir).map_err(|e| e.to_string())?;
} }
Ok(data_dir.join("oauth_tokens.json")) Ok(data_dir.join("oauth_tokens.json"))
} }
fn read_store(path: &Path) -> Result<TokenStore, String> { fn read_store(path: &Path) -> Result<TokenStore, String> {
if !path.exists() { if !path.exists() {
return Ok(HashMap::new()); return Ok(HashMap::new());
} }
let content = fs::read_to_string(path).map_err(|e| e.to_string())?; let content = fs::read_to_string(path).map_err(|e| e.to_string())?;
if content.trim().is_empty() { if content.trim().is_empty() {
return Ok(HashMap::new()); return Ok(HashMap::new());
} }
serde_json::from_str(&content).map_err(|e| e.to_string()) serde_json::from_str(&content).map_err(|e| e.to_string())
} }
fn write_store(path: &Path, store: &TokenStore) -> Result<(), String> { fn write_store(path: &Path, store: &TokenStore) -> Result<(), String> {
let content = serde_json::to_string_pretty(store).map_err(|e| e.to_string())?; let content = serde_json::to_string_pretty(store).map_err(|e| e.to_string())?;
fs::write(path, content).map_err(|e| e.to_string()) fs::write(path, content).map_err(|e| e.to_string())
} }
#[tauri::command] #[tauri::command]
pub fn oauth_set_tokens( pub fn oauth_set_tokens(
app: tauri::AppHandle, app: tauri::AppHandle,
provider_id: String, provider_id: String,
tokens: serde_json::Value, tokens: serde_json::Value,
) -> Result<(), String> { ) -> Result<(), String> {
let path = get_storage_path(&app)?; let path = get_storage_path(&app)?;
let mut store = read_store(&path)?; let mut store = read_store(&path)?;
let token_set: StoredTokenSet = let token_set: StoredTokenSet = serde_json::from_value(tokens).map_err(|e| e.to_string())?;
serde_json::from_value(tokens).map_err(|e| e.to_string())?;
store.insert(provider_id, token_set); store.insert(provider_id, token_set);
write_store(&path, &store) write_store(&path, &store)
} }
#[tauri::command] #[tauri::command]
pub fn oauth_get_tokens( pub fn oauth_get_tokens(
app: tauri::AppHandle, app: tauri::AppHandle,
provider_id: String, provider_id: String,
) -> Result<Option<serde_json::Value>, String> { ) -> Result<Option<serde_json::Value>, String> {
let path = get_storage_path(&app)?; let path = get_storage_path(&app)?;
let store = read_store(&path)?; let store = read_store(&path)?;
if let Some(token_set) = store.get(&provider_id) { if let Some(token_set) = store.get(&provider_id) {
let value = serde_json::to_value(token_set).map_err(|e| e.to_string())?; let value = serde_json::to_value(token_set).map_err(|e| e.to_string())?;
Ok(Some(value)) Ok(Some(value))
} else { } else {
Ok(None) Ok(None)
} }
} }
#[tauri::command] #[tauri::command]
pub fn oauth_remove_tokens(app: tauri::AppHandle, provider_id: String) -> Result<(), String> { pub fn oauth_remove_tokens(app: tauri::AppHandle, provider_id: String) -> Result<(), String> {
let path = get_storage_path(&app)?; let path = get_storage_path(&app)?;
let mut store = read_store(&path)?; let mut store = read_store(&path)?;
store.remove(&provider_id); store.remove(&provider_id);
write_store(&path, &store) write_store(&path, &store)
} }