Add built-in theme support with various pre-defined themes
Some checks failed
ci/someci/tag/woodpecker/5 Pipeline is pending
ci/someci/tag/woodpecker/6 Pipeline is pending
ci/someci/tag/woodpecker/7 Pipeline is pending
ci/someci/tag/woodpecker/1 Pipeline failed
ci/someci/tag/woodpecker/2 Pipeline failed
ci/someci/tag/woodpecker/3 Pipeline failed
ci/someci/tag/woodpecker/4 Pipeline failed
Some checks failed
ci/someci/tag/woodpecker/5 Pipeline is pending
ci/someci/tag/woodpecker/6 Pipeline is pending
ci/someci/tag/woodpecker/7 Pipeline is pending
ci/someci/tag/woodpecker/1 Pipeline failed
ci/someci/tag/woodpecker/2 Pipeline failed
ci/someci/tag/woodpecker/3 Pipeline failed
ci/someci/tag/woodpecker/4 Pipeline failed
- Introduce multiple built-in themes (`default_dark`, `default_light`, `gruvbox`, `dracula`, `solarized`, `midnight-ocean`, `rose-pine`, `monokai`, `material-dark`, `material-light`). - Implement theming system with customizable color schemes for all UI components in the TUI. - Include documentation for themes in `themes/README.md`. - Add fallback mechanisms for default themes in case of parsing errors. - Support custom themes with overrides via configuration.
This commit is contained in:
@@ -45,10 +45,7 @@ impl StorageManager {
|
||||
// Ensure the directory exists
|
||||
if !sessions_dir.exists() {
|
||||
fs::create_dir_all(&sessions_dir).map_err(|e| {
|
||||
Error::Storage(format!(
|
||||
"Failed to create sessions directory: {}",
|
||||
e
|
||||
))
|
||||
Error::Storage(format!("Failed to create sessions directory: {}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -66,7 +63,11 @@ impl StorageManager {
|
||||
}
|
||||
|
||||
/// Save a conversation to disk
|
||||
pub fn save_conversation(&self, conversation: &Conversation, name: Option<String>) -> Result<PathBuf> {
|
||||
pub fn save_conversation(
|
||||
&self,
|
||||
conversation: &Conversation,
|
||||
name: Option<String>,
|
||||
) -> Result<PathBuf> {
|
||||
self.save_conversation_with_description(conversation, name, None)
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ impl StorageManager {
|
||||
&self,
|
||||
conversation: &Conversation,
|
||||
name: Option<String>,
|
||||
description: Option<String>
|
||||
description: Option<String>,
|
||||
) -> Result<PathBuf> {
|
||||
let filename = if let Some(ref session_name) = name {
|
||||
// Use provided name, sanitized
|
||||
@@ -101,26 +102,22 @@ impl StorageManager {
|
||||
save_conv.description = description;
|
||||
}
|
||||
|
||||
let json = serde_json::to_string_pretty(&save_conv).map_err(|e| {
|
||||
Error::Storage(format!("Failed to serialize conversation: {}", e))
|
||||
})?;
|
||||
let json = serde_json::to_string_pretty(&save_conv)
|
||||
.map_err(|e| Error::Storage(format!("Failed to serialize conversation: {}", e)))?;
|
||||
|
||||
fs::write(&path, json).map_err(|e| {
|
||||
Error::Storage(format!("Failed to write session file: {}", e))
|
||||
})?;
|
||||
fs::write(&path, json)
|
||||
.map_err(|e| Error::Storage(format!("Failed to write session file: {}", e)))?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Load a conversation from disk
|
||||
pub fn load_conversation(&self, path: impl AsRef<Path>) -> Result<Conversation> {
|
||||
let content = fs::read_to_string(path.as_ref()).map_err(|e| {
|
||||
Error::Storage(format!("Failed to read session file: {}", e))
|
||||
})?;
|
||||
let content = fs::read_to_string(path.as_ref())
|
||||
.map_err(|e| Error::Storage(format!("Failed to read session file: {}", e)))?;
|
||||
|
||||
let conversation: Conversation = serde_json::from_str(&content).map_err(|e| {
|
||||
Error::Storage(format!("Failed to parse session file: {}", e))
|
||||
})?;
|
||||
let conversation: Conversation = serde_json::from_str(&content)
|
||||
.map_err(|e| Error::Storage(format!("Failed to parse session file: {}", e)))?;
|
||||
|
||||
Ok(conversation)
|
||||
}
|
||||
@@ -129,14 +126,12 @@ impl StorageManager {
|
||||
pub fn list_sessions(&self) -> Result<Vec<SessionMeta>> {
|
||||
let mut sessions = Vec::new();
|
||||
|
||||
let entries = fs::read_dir(&self.sessions_dir).map_err(|e| {
|
||||
Error::Storage(format!("Failed to read sessions directory: {}", e))
|
||||
})?;
|
||||
let entries = fs::read_dir(&self.sessions_dir)
|
||||
.map_err(|e| Error::Storage(format!("Failed to read sessions directory: {}", e)))?;
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|e| {
|
||||
Error::Storage(format!("Failed to read directory entry: {}", e))
|
||||
})?;
|
||||
let entry = entry
|
||||
.map_err(|e| Error::Storage(format!("Failed to read directory entry: {}", e)))?;
|
||||
|
||||
let path = entry.path();
|
||||
if path.extension().and_then(|s| s.to_str()) != Some("json") {
|
||||
@@ -172,9 +167,8 @@ impl StorageManager {
|
||||
|
||||
/// Delete a saved session
|
||||
pub fn delete_session(&self, path: impl AsRef<Path>) -> Result<()> {
|
||||
fs::remove_file(path.as_ref()).map_err(|e| {
|
||||
Error::Storage(format!("Failed to delete session file: {}", e))
|
||||
})
|
||||
fs::remove_file(path.as_ref())
|
||||
.map_err(|e| Error::Storage(format!("Failed to delete session file: {}", e)))
|
||||
}
|
||||
|
||||
/// Get the sessions directory path
|
||||
@@ -237,7 +231,9 @@ mod tests {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// macOS should use ~/Library/Application Support
|
||||
assert!(path.to_string_lossy().contains("Library/Application Support"));
|
||||
assert!(path
|
||||
.to_string_lossy()
|
||||
.contains("Library/Application Support"));
|
||||
}
|
||||
|
||||
println!("Default sessions directory: {}", path.display());
|
||||
@@ -257,10 +253,13 @@ mod tests {
|
||||
|
||||
let mut conv = Conversation::new("test-model".to_string());
|
||||
conv.messages.push(Message::user("Hello".to_string()));
|
||||
conv.messages.push(Message::assistant("Hi there!".to_string()));
|
||||
conv.messages
|
||||
.push(Message::assistant("Hi there!".to_string()));
|
||||
|
||||
// Save conversation
|
||||
let path = storage.save_conversation(&conv, Some("test_session".to_string())).unwrap();
|
||||
let path = storage
|
||||
.save_conversation(&conv, Some("test_session".to_string()))
|
||||
.unwrap();
|
||||
assert!(path.exists());
|
||||
|
||||
// Load conversation
|
||||
@@ -280,7 +279,9 @@ mod tests {
|
||||
for i in 0..3 {
|
||||
let mut conv = Conversation::new("test-model".to_string());
|
||||
conv.messages.push(Message::user(format!("Message {}", i)));
|
||||
storage.save_conversation(&conv, Some(format!("session_{}", i))).unwrap();
|
||||
storage
|
||||
.save_conversation(&conv, Some(format!("session_{}", i)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// List sessions
|
||||
|
||||
Reference in New Issue
Block a user