musealpha / src-tauri /src /lib.rs
asdf98's picture
feat: register storage_info, storage_clear_library, storage_clear_projects, storage_reveal_folder commands; add opener crate
68032ff verified
mod adblock;
mod browser;
mod credentials;
mod library;
mod board;
mod color_tools;
mod persistence;
mod sessions;
mod study;
mod downloads;
mod settings;
mod state;
mod history;
mod projects;
mod refs_format;
use crate::state::AppState;
use tauri::{Emitter, Manager};
use tauri_plugin_sql::{Migration, MigrationKind};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_sql::Builder::default().add_migrations("sqlite:muse_alpha_v2.db", migrations()).build())
.plugin(tauri_plugin_stronghold::Builder::new(|password| { let salt = b"muse-vault-kdf-salt-2026-v1-prod"; let mut key = [0u8; 32]; argon2::Argon2::default().hash_password_into(password.as_bytes(), salt, &mut key).expect("argon2 KDF failed"); key.to_vec() }).build())
.register_uri_scheme_protocol("muse-action", |ctx, request| {
let uri = request.uri().to_string();
let app = ctx.app_handle().clone();
let action = uri.split("://").nth(1).unwrap_or("").split('?').next().unwrap_or("").trim_end_matches('/').to_string();
let query = uri.split('?').nth(1).unwrap_or("");
let params: std::collections::HashMap<String, String> = query.split('&').filter_map(|pair| { let (k, v) = pair.split_once('=')?; Some((percent_decode(k), percent_decode(v))) }).collect();
match action.as_str() {
"board" | "library" => {}
"vault" => { let va = params.get("action").cloned().unwrap_or_default(); match va.as_str() { "save-prompt" => { let _ = app.emit("vault://save-prompt", serde_json::json!({"origin": params.get("origin").cloned().unwrap_or_default(), "username": params.get("username").cloned().unwrap_or_default(), "password": params.get("password").cloned().unwrap_or_default()})); } "has-login-form" => { let _ = app.emit("vault://login-detected", serde_json::json!({"origin": params.get("origin").cloned().unwrap_or_default(), "fields": params.get("fields").cloned().unwrap_or_default()})); } _ => {} } }
_ => {}
}
tauri::http::Response::builder().status(200).header("Access-Control-Allow-Origin", "*").body(Vec::new()).unwrap()
})
.manage(state::AppState::default())
.manage(adblock::engine::AdBlockState::new())
.manage(library::LibraryState::default())
.manage(board::BoardState::default())
.manage(downloads::DownloadState::default())
.manage(study::StudyState::default())
.invoke_handler(tauri::generate_handler![
settings::phase0_status, settings::board_load_state, settings::board_save_state, settings::board_export_file, settings::board_import_file, settings::screen_capture_full, settings::screen_capture_region, settings::screen_capture_window_region,
history::history_list, history::history_clear,
refs_format::refs_export, refs_format::refs_import,
projects::projects_list, projects::projects_get_active_id, projects::project_create, projects::project_save, projects::project_load, projects::project_delete, projects::project_rename,
browser::capture::browser_capture_viewport, browser::capture::browser_capture_clip, browser::capture::browser_capture_full_page,
browser::autofill::tab_autofill,
browser::commands::browser_init, browser::commands::browser_set_visible, browser::commands::browser_hide_all, browser::commands::tab_create, browser::commands::tab_activate, browser::commands::tab_close, browser::commands::tab_restore, browser::commands::tab_navigate, browser::commands::tab_reload, browser::commands::tab_back, browser::commands::tab_forward, browser::commands::tab_zoom, browser::commands::tab_resize, browser::commands::tab_get_all, browser::commands::tab_pin, browser::commands::tab_find, browser::commands::tab_find_clear,
browser::context_menu::browser_context_menu,
adblock::commands::shield_get_report, adblock::commands::shield_check_url, adblock::commands::shield_cosmetic_css, adblock::commands::shield_toggle_domain, adblock::commands::shield_is_allowed, adblock::commands::shield_update_lists, adblock::commands::shield_add_user_rule, adblock::commands::shield_list_subscriptions,
library::library_add_item, library::library_import_local, library::library_import_data_url, library::library_update_metadata, library::library_remove_tag, library::library_load, library::library_items, library::library_search, library::library_remove_item, library::library_add_tag,
board::board_list, board::board_current, board::board_create, board::board_open, board::board_save_as, board::board_load, board::board_items, board::board_add_image, board::board_add_note, board::board_add_palette, board::board_extract_palette_from_item, board::board_add_palette_from_item, board::board_update_item, board::board_delete_item,
persistence::storage_info, persistence::storage_clear_library, persistence::storage_clear_projects, persistence::storage_reveal_folder,
sessions::sessions_save, sessions::sessions_load, sessions::sessions_list, sessions::sessions_auto_save, sessions::sessions_delete, sessions::sessions_rename,
downloads::downloads_list, downloads::downloads_clear_completed, downloads::download_to_library, downloads::web_clip_page,
credentials::credentials_list, credentials::credentials_generate_password,
study::study_start, study::study_complete, study::study_list,
color_tools::color_export, color_tools::color_search_library,
])
.setup(|app| {
#[cfg(desktop)]
app.handle().plugin(tauri_plugin_global_shortcut::Builder::new().build())?;
if let Ok(zoom_mem) = crate::persistence::load_json::<std::collections::HashMap<String, f64>>(app.handle(), "zoom_memory.json") { if !zoom_mem.is_empty() { let app_state = app.state::<AppState>(); let mut tabs = app_state.tabs.lock().expect("tabs lock"); tabs.zoom_memory = zoom_mem; } }
adblock::updater::spawn_updater(app.handle().clone());
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running Refstudio");
}
fn migrations() -> Vec<Migration> { vec![ Migration { version: 1, description: "phase0_init", sql: include_str!("../migrations/001_phase0_init.sql"), kind: MigrationKind::Up }, Migration { version: 2, description: "phase3_tables", sql: include_str!("../migrations/002_phase3_tables.sql"), kind: MigrationKind::Up } ] }
fn percent_decode(s: &str) -> String { let bytes = s.as_bytes(); let mut out = Vec::with_capacity(bytes.len()); let mut i = 0; while i < bytes.len() { if bytes[i] == b'%' && i + 2 < bytes.len() { if let Ok(hex) = std::str::from_utf8(&bytes[i+1..i+3]) { if let Ok(v) = u8::from_str_radix(hex, 16) { out.push(v); i += 3; continue; } } } out.push(if bytes[i] == b'+' { b' ' } else { bytes[i] }); i += 1; } String::from_utf8_lossy(&out).to_string() }