commit dda6707ebc0cf9565f88755de4856b71423eb732
parent 4169bc867ac7666d5d8d63ee5acd09c4e97cf835
Author: William Casarin <jb55@jb55.com>
Date: Fri, 20 Feb 2026 10:22:16 -0800
dave: persist settings to dave_settings.json
Save Dave settings (API key, relay URL, provider, model, endpoint) to
disk so they survive app restarts. Uses the existing TimedSerializer
pattern and settings directory, which works on all platforms including
Android.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
4 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/crates/notedeck/src/lib.rs b/crates/notedeck/src/lib.rs
@@ -36,7 +36,7 @@ mod style;
pub mod theme;
mod time;
mod timecache;
-mod timed_serializer;
+pub mod timed_serializer;
pub mod ui;
mod unknowns;
mod urls;
diff --git a/crates/notedeck_chrome/src/chrome.rs b/crates/notedeck_chrome/src/chrome.rs
@@ -159,6 +159,7 @@ impl Chrome {
cc.wgpu_render_state.as_ref(),
context.ndb.clone(),
cc.egui_ctx.clone(),
+ context.path,
);
let mut chrome = Chrome::default();
diff --git a/crates/notedeck_dave/src/config.rs b/crates/notedeck_dave/src/config.rs
@@ -1,5 +1,6 @@
use crate::backend::BackendType;
use async_openai::config::OpenAIConfig;
+use serde::{Deserialize, Serialize};
/// AI interaction mode - determines UI complexity and feature set
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -11,7 +12,7 @@ pub enum AiMode {
}
/// Available AI providers for Dave
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum AiProvider {
#[default]
OpenAI,
@@ -77,7 +78,7 @@ impl AiProvider {
}
/// User-configurable settings for Dave AI
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DaveSettings {
pub provider: AiProvider,
pub model: String,
diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs
@@ -30,7 +30,10 @@ use egui_wgpu::RenderState;
use enostr::KeypairUnowned;
use focus_queue::FocusQueue;
use nostrdb::{Subscription, Transaction};
-use notedeck::{try_process_events_core, ui::is_narrow, AppAction, AppContext, AppResponse};
+use notedeck::{
+ timed_serializer::TimedSerializer, try_process_events_core, ui::is_narrow, AppAction,
+ AppContext, AppResponse, DataPath, DataPathType,
+};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::string::ToString;
@@ -148,6 +151,8 @@ pub struct Dave {
hostname: String,
/// PNS relay URL (configurable via DAVE_RELAY env or settings UI).
pns_relay_url: String,
+ /// Persists DaveSettings to dave_settings.json
+ settings_serializer: TimedSerializer<DaveSettings>,
}
use update::PermissionPublish;
@@ -297,9 +302,25 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
))
}
- pub fn new(render_state: Option<&RenderState>, ndb: nostrdb::Ndb, ctx: egui::Context) -> Self {
- let model_config = ModelConfig::default();
- //let model_config = ModelConfig::ollama();
+ pub fn new(
+ render_state: Option<&RenderState>,
+ ndb: nostrdb::Ndb,
+ ctx: egui::Context,
+ path: &DataPath,
+ ) -> Self {
+ let settings_serializer =
+ TimedSerializer::new(path, DataPathType::Setting, "dave_settings.json".to_owned());
+
+ // Load saved settings, falling back to env-var-based defaults
+ let (model_config, settings) = if let Some(saved_settings) = settings_serializer.get_item()
+ {
+ let config = ModelConfig::from_settings(&saved_settings);
+ (config, saved_settings)
+ } else {
+ let config = ModelConfig::default();
+ let settings = DaveSettings::from_model_config(&config);
+ (config, settings)
+ };
// Determine AI mode from backend type
let ai_mode = model_config.ai_mode();
@@ -327,7 +348,6 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
tools.insert(tool.name().to_string(), tool);
}
- let settings = DaveSettings::from_model_config(&model_config);
let pns_relay_url = model_config
.pns_relay
.clone()
@@ -386,6 +406,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
pending_summaries: Vec::new(),
hostname,
pns_relay_url,
+ settings_serializer,
}
}
@@ -394,13 +415,15 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
&self.settings
}
- /// Apply new settings. Note: Provider changes require app restart to take effect.
+ /// Apply new settings and persist to disk.
+ /// Note: Provider changes require app restart to take effect.
pub fn apply_settings(&mut self, settings: DaveSettings) {
self.model_config = ModelConfig::from_settings(&settings);
self.pns_relay_url = settings
.pns_relay
.clone()
.unwrap_or_else(|| DEFAULT_PNS_RELAY.to_string());
+ self.settings_serializer.try_save(settings.clone());
self.settings = settings;
}