Files
What_That_Claude_Do/src/config.rs
T
2026-05-04 18:57:20 +02:00

164 lines
5.2 KiB
Rust

use anyhow::{Context, Result};
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
/// Top-level application configuration.
/// Serialized to/from ~/.config/rs-pictures/config.toml
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
/// Directory where screenshots are saved.
pub save_directory: PathBuf,
/// File format for saved screenshots: "png" or "jpeg".
pub save_format: String,
/// Filename template. Supports strftime-style tokens via chrono.
/// Example: "screenshot_%Y-%m-%d_%H-%M-%S"
pub filename_template: String,
/// When true, automatically save to disk after capture without opening
/// the review window.
#[serde(default)]
pub auto_save: bool,
/// When true, automatically copy to the clipboard after capture without
/// opening the review window.
#[serde(default)]
pub auto_copy: bool,
/// Milliseconds to wait after launch before capturing the desktop snapshot.
/// Increase this if the overlay background still shows the terminal/launcher
/// that started rs-pictures. Default: 200.
#[serde(default = "default_capture_delay_ms")]
pub capture_delay_ms: u64,
/// If true, the selection overlay will be transparent (live preview) instead of
/// a frozen screenshot. The final capture happens after selection.
#[serde(default)]
pub live_mode: bool,
/// Visual effects applied after capture.
pub effects: EffectsConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EffectsConfig {
/// Apply rounded corners to the screenshot.
pub rounded_corners: bool,
/// Radius in pixels for rounded corners.
pub corner_radius: f32,
/// Apply a drop shadow beneath the screenshot.
pub drop_shadow: bool,
/// Blur radius for the drop shadow (higher = softer shadow).
pub shadow_blur_radius: f32,
/// Horizontal offset of the shadow in pixels.
pub shadow_offset_x: f32,
/// Vertical offset of the shadow in pixels.
pub shadow_offset_y: f32,
/// Shadow color as [R, G, B, A] in 0..=255.
pub shadow_color: [u8; 4],
}
impl Default for EffectsConfig {
fn default() -> Self {
Self {
rounded_corners: false,
corner_radius: 12.0,
drop_shadow: false,
shadow_blur_radius: 20.0,
shadow_offset_x: 5.0,
shadow_offset_y: 8.0,
shadow_color: [0, 0, 0, 160],
}
}
}
impl Default for Config {
fn default() -> Self {
let save_directory = dirs_default_pictures().unwrap_or_else(|| PathBuf::from("."));
Self {
save_directory,
save_format: "png".into(),
filename_template: "screenshot_%Y-%m-%d_%H-%M-%S".into(),
auto_save: false,
auto_copy: false,
capture_delay_ms: default_capture_delay_ms(),
live_mode: false,
effects: EffectsConfig::default(),
}
}
}
impl Config {
/// Returns the path to the config file, creating parent directories if needed.
pub fn config_path() -> Option<PathBuf> {
ProjectDirs::from("", "", "rs-pictures")
.map(|pd| pd.config_dir().join("config.toml"))
}
/// Load config from disk, or return the default config if the file doesn't exist.
pub fn load() -> Result<Self> {
let path = match Self::config_path() {
Some(p) => p,
None => return Ok(Self::default()),
};
if !path.exists() {
let config = Self::default();
config.save()?;
return Ok(config);
}
let raw = std::fs::read_to_string(&path)
.with_context(|| format!("Failed to read config at {}", path.display()))?;
toml::from_str(&raw)
.with_context(|| format!("Failed to parse config at {}", path.display()))
}
/// Persist the current config to disk.
pub fn save(&self) -> Result<()> {
let path = Self::config_path()
.context("Could not determine config directory")?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("Failed to create config dir {}", parent.display()))?;
}
let serialized = toml::to_string_pretty(self)
.context("Failed to serialize config")?;
std::fs::write(&path, serialized)
.with_context(|| format!("Failed to write config to {}", path.display()))?;
Ok(())
}
/// Build the full output path for a new screenshot using chrono formatting.
pub fn output_path(&self) -> PathBuf {
let now = chrono::Local::now();
let filename = now.format(&self.filename_template).to_string();
let ext = &self.save_format;
self.save_directory.join(format!("{filename}.{ext}"))
}
}
fn default_capture_delay_ms() -> u64 { 200 }
fn dirs_default_pictures() -> Option<PathBuf> {
// Use XDG_PICTURES_DIR if available, otherwise ~/Pictures
if let Ok(val) = std::env::var("XDG_PICTURES_DIR") {
return Some(PathBuf::from(val));
}
directories::UserDirs::new()
.and_then(|ud| ud.picture_dir().map(|p| p.to_path_buf()))
}