steer_core/
preferences.rs1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3use strum::Display;
4
5#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default, Display)]
6#[strum(serialize_all = "kebab-case")]
7pub enum EditingMode {
8 #[default]
9 Simple, Vim, }
12
13#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default, Display)]
14#[serde(rename_all = "kebab-case")]
15#[strum(serialize_all = "kebab-case")]
16pub enum NotificationTransport {
17 #[default]
18 Auto,
19 Osc9,
20 Off,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, Default)]
24pub struct Preferences {
25 pub default_model: Option<String>,
26
27 #[serde(default)]
28 pub ui: UiPreferences,
29
30 #[serde(default)]
31 pub tools: ToolPreferences,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct UiPreferences {
36 pub theme: Option<String>,
37 #[serde(default)]
38 pub notifications: NotificationPreferences,
39 pub history_limit: Option<usize>,
40 pub provider_priority: Option<Vec<String>>,
41 #[serde(default)]
42 pub editing_mode: EditingMode,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct NotificationPreferences {
47 #[serde(default)]
48 pub transport: NotificationTransport,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, Default)]
52pub struct ToolPreferences {
53 pub pre_approved: Vec<String>,
54}
55
56impl Default for NotificationPreferences {
57 fn default() -> Self {
58 Self {
59 transport: NotificationTransport::Auto,
60 }
61 }
62}
63
64impl Preferences {
65 pub fn config_path() -> Result<PathBuf, crate::error::Error> {
67 let config_dir = dirs::config_dir().ok_or_else(|| {
68 crate::error::Error::Configuration("Could not determine config directory".to_string())
69 })?;
70 Ok(config_dir.join("steer").join("preferences.toml"))
71 }
72
73 pub fn load() -> Result<Self, crate::error::Error> {
75 let path = Self::config_path()?;
76
77 if path.exists() {
78 let contents = std::fs::read_to_string(&path)?;
79 match toml::from_str(&contents) {
80 Ok(prefs) => Ok(prefs),
81 Err(e) => {
82 tracing::warn!(
83 "Failed to parse preferences file at {:?}: {}. Using defaults.",
84 path,
85 e
86 );
87 Ok(Self::default())
88 }
89 }
90 } else {
91 Ok(Self::default())
92 }
93 }
94
95 pub fn save(&self) -> Result<(), crate::error::Error> {
97 let path = Self::config_path()?;
98
99 if let Some(parent) = path.parent() {
101 std::fs::create_dir_all(parent)?;
102 }
103
104 let contents = toml::to_string_pretty(self).map_err(|e| {
105 crate::error::Error::Configuration(format!("Failed to serialize preferences: {e}"))
106 })?;
107
108 std::fs::write(&path, contents)?;
109
110 Ok(())
111 }
112}