1use serde::{Deserialize, Serialize};
6use yewdux::prelude::*;
7
8#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
10pub enum Theme {
11 Light,
12 Dark,
13 #[default]
14 System,
15}
16
17impl Theme {
18 pub fn as_str(&self) -> &'static str {
19 match self {
20 Self::Light => "light",
21 Self::Dark => "dark",
22 Self::System => "system",
23 }
24 }
25}
26
27#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
29pub enum OutputFormat {
30 #[default]
31 Json,
32 Raw,
33 Formatted,
34}
35
36impl OutputFormat {
37 pub fn as_str(&self) -> &'static str {
38 match self {
39 Self::Json => "json",
40 Self::Raw => "raw",
41 Self::Formatted => "formatted",
42 }
43 }
44}
45
46#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
48pub enum EmbeddingProvider {
49 #[default]
50 FastEmbed,
51 OpenAI,
52 Ollama,
53}
54
55impl EmbeddingProvider {
56 pub fn as_str(&self) -> &'static str {
57 match self {
58 Self::FastEmbed => "fastembed",
59 Self::OpenAI => "openai",
60 Self::Ollama => "ollama",
61 }
62 }
63
64 pub fn display_name(&self) -> &'static str {
65 match self {
66 Self::FastEmbed => "FastEmbed (Local)",
67 Self::OpenAI => "OpenAI",
68 Self::Ollama => "Ollama",
69 }
70 }
71}
72
73#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
75pub enum VectorBackend {
76 #[default]
77 InMemory,
78 Qdrant,
79}
80
81impl VectorBackend {
82 pub fn as_str(&self) -> &'static str {
83 match self {
84 Self::InMemory => "inmemory",
85 Self::Qdrant => "qdrant",
86 }
87 }
88
89 pub fn display_name(&self) -> &'static str {
90 match self {
91 Self::InMemory => "In-Memory",
92 Self::Qdrant => "Qdrant",
93 }
94 }
95}
96
97#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
99pub struct SearchSettings {
100 pub embedding_provider: EmbeddingProvider,
102 pub embedding_model: String,
104 pub vector_backend: VectorBackend,
106 pub enable_hybrid: bool,
108 pub enable_reranking: bool,
110 pub qdrant_url: Option<String>,
112 pub ollama_url: Option<String>,
114}
115
116impl Default for SearchSettings {
117 fn default() -> Self {
118 Self {
119 embedding_provider: EmbeddingProvider::FastEmbed,
120 embedding_model: "BAAI/bge-small-en-v1.5".to_string(),
121 vector_backend: VectorBackend::InMemory,
122 enable_hybrid: false,
123 enable_reranking: false,
124 qdrant_url: None,
125 ollama_url: Some("http://localhost:11434".to_string()),
126 }
127 }
128}
129
130#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
132pub struct ApiSettings {
133 pub base_url: String,
135 pub timeout_secs: u32,
137}
138
139impl Default for ApiSettings {
140 fn default() -> Self {
141 Self {
142 base_url: "http://127.0.0.1:3000".to_string(),
143 timeout_secs: 30,
144 }
145 }
146}
147
148#[derive(Clone, Debug, PartialEq, Store, Serialize, Deserialize)]
150#[store(storage = "local", storage_tab_sync)]
151pub struct SettingsStore {
152 pub theme: Theme,
154 pub default_timeout: u32,
156 pub output_format: OutputFormat,
158 pub include_metadata: bool,
160 pub history_retention: u32,
162 pub onboarding_completed: bool,
164 pub search: SearchSettings,
166 pub api: ApiSettings,
168 pub keyboard_shortcuts: bool,
170 pub expand_parameters: bool,
172 pub auto_refresh_interval: u32,
174}
175
176impl Default for SettingsStore {
177 fn default() -> Self {
178 Self {
179 theme: Theme::System,
180 default_timeout: 30,
181 output_format: OutputFormat::Json,
182 include_metadata: false,
183 history_retention: 1000,
184 onboarding_completed: false,
185 search: SearchSettings::default(),
186 api: ApiSettings::default(),
187 keyboard_shortcuts: true,
188 expand_parameters: false,
189 auto_refresh_interval: 0,
190 }
191 }
192}
193
194impl SettingsStore {
195 pub fn needs_onboarding(&self) -> bool {
197 !self.onboarding_completed
198 }
199
200 pub fn effective_theme(&self) -> Theme {
202 self.theme.clone()
205 }
206}
207
208pub enum SettingsAction {
210 SetTheme(Theme),
211 SetDefaultTimeout(u32),
212 SetOutputFormat(OutputFormat),
213 SetIncludeMetadata(bool),
214 SetHistoryRetention(u32),
215 CompleteOnboarding,
216 SetEmbeddingProvider(EmbeddingProvider),
218 SetEmbeddingModel(String),
219 SetVectorBackend(VectorBackend),
220 SetEnableHybrid(bool),
221 SetEnableReranking(bool),
222 SetQdrantUrl(Option<String>),
223 SetOllamaUrl(Option<String>),
224 SetApiBaseUrl(String),
226 SetApiTimeout(u32),
227 SetKeyboardShortcuts(bool),
229 SetExpandParameters(bool),
230 SetAutoRefreshInterval(u32),
231 ResetToDefaults,
233 ResetSearchSettings,
234}
235
236impl Reducer<SettingsStore> for SettingsAction {
237 fn apply(self, mut store: std::rc::Rc<SettingsStore>) -> std::rc::Rc<SettingsStore> {
238 let state = std::rc::Rc::make_mut(&mut store);
239
240 match self {
241 SettingsAction::SetTheme(theme) => {
242 state.theme = theme;
243 }
244 SettingsAction::SetDefaultTimeout(timeout) => {
245 state.default_timeout = timeout;
246 }
247 SettingsAction::SetOutputFormat(format) => {
248 state.output_format = format;
249 }
250 SettingsAction::SetIncludeMetadata(include) => {
251 state.include_metadata = include;
252 }
253 SettingsAction::SetHistoryRetention(count) => {
254 state.history_retention = count;
255 }
256 SettingsAction::CompleteOnboarding => {
257 state.onboarding_completed = true;
258 }
259 SettingsAction::SetEmbeddingProvider(provider) => {
261 state.search.embedding_provider = provider;
262 }
263 SettingsAction::SetEmbeddingModel(model) => {
264 state.search.embedding_model = model;
265 }
266 SettingsAction::SetVectorBackend(backend) => {
267 state.search.vector_backend = backend;
268 }
269 SettingsAction::SetEnableHybrid(enable) => {
270 state.search.enable_hybrid = enable;
271 }
272 SettingsAction::SetEnableReranking(enable) => {
273 state.search.enable_reranking = enable;
274 }
275 SettingsAction::SetQdrantUrl(url) => {
276 state.search.qdrant_url = url;
277 }
278 SettingsAction::SetOllamaUrl(url) => {
279 state.search.ollama_url = url;
280 }
281 SettingsAction::SetApiBaseUrl(url) => {
283 state.api.base_url = url;
284 }
285 SettingsAction::SetApiTimeout(timeout) => {
286 state.api.timeout_secs = timeout;
287 }
288 SettingsAction::SetKeyboardShortcuts(enable) => {
290 state.keyboard_shortcuts = enable;
291 }
292 SettingsAction::SetExpandParameters(expand) => {
293 state.expand_parameters = expand;
294 }
295 SettingsAction::SetAutoRefreshInterval(interval) => {
296 state.auto_refresh_interval = interval;
297 }
298 SettingsAction::ResetToDefaults => {
300 *state = SettingsStore::default();
301 }
302 SettingsAction::ResetSearchSettings => {
303 state.search = SearchSettings::default();
304 }
305 }
306
307 store
308 }
309}