//! Настройки приложения (не секретные): недавние файлы, тема, таймеры. //! Windows: %APPDATA%\Qulyp\config.json, Linux: ~/.config/qulyp/config.json. use std::path::PathBuf; use serde::{Deserialize, Serialize}; use crate::generator::GenOptions; #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Default)] pub enum ThemeMode { #[default] System, Dark, Light, } #[derive(Serialize, Deserialize, Clone)] #[serde(default)] pub struct Config { pub recent: Vec, pub theme: ThemeMode, /// Автоблокировка при бездействии, минут (0 — никогда). pub auto_lock_min: u32, /// Очистка буфера обмена после копирования, секунд (0 — не очищать). pub clipboard_sec: u32, pub lock_on_minimize: bool, pub generator: GenOptions, } impl Default for Config { fn default() -> Self { Self { recent: Vec::new(), theme: ThemeMode::System, auto_lock_min: 10, clipboard_sec: 20, lock_on_minimize: false, generator: GenOptions::default(), } } } fn path() -> Option { // Переопределение — для тестов и «портативного» режима. if let Some(p) = std::env::var_os("QULYP_CONFIG") { return Some(PathBuf::from(p)); } let dir = if cfg!(windows) { "Qulyp" } else { "qulyp" }; Some(dirs::config_dir()?.join(dir).join("config.json")) } impl Config { pub fn load() -> Self { path().and_then(|p| std::fs::read(p).ok()).and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default() } pub fn save(&self) { let Some(p) = path() else { return }; if let Some(dir) = p.parent() { let _ = std::fs::create_dir_all(dir); } if let Ok(json) = serde_json::to_vec_pretty(self) { let _ = std::fs::write(p, json); } } pub fn push_recent(&mut self, file: PathBuf) { let file = file.canonicalize().map(strip_unc).unwrap_or(file); self.recent.retain(|p| p != &file); self.recent.insert(0, file); self.recent.truncate(8); self.save(); } pub fn default_vault_dir() -> PathBuf { dirs::document_dir().or_else(dirs::home_dir).unwrap_or_else(|| PathBuf::from(".")) } } /// `canonicalize` на Windows возвращает `\\?\C:\...` — убираем префикс для читаемости. fn strip_unc(p: PathBuf) -> PathBuf { let s = p.to_string_lossy(); match s.strip_prefix(r"\\?\") { Some(rest) if !rest.starts_with("UNC") => PathBuf::from(rest), _ => p, } }