generated from nhcarrigan/template
4c46d4c8fd
## Summary This PR adds a collection of productivity features and UI enhancements to improve the Hikari Desktop experience: ### New Features - **Clipboard History** (#25) - Track and manage copied code snippets with language detection, search, filtering, and pinning - **Quick Actions Panel** (#15) - Buttons for common quick actions like "Review PR", "Run tests", "Explain file", with customizable actions - **Git Integration Panel** (#24) - View current branch, changed/staged files, quick git actions (commit, push, pull), and branch management - **Session Import/Export** (#8) - Export conversations to JSON and import previously saved sessions - **Snippet Library** (#22) - Save and reuse common prompts with categories and quick insert - **Session History** (#14) - Auto-save conversations with browsable history and search - **High Contrast Mode** (#20) - Accessibility theme with improved visibility - **Minimize to System Tray** (#11) - System tray support with right-click menu ### UI Enhancements - Trans-pride gradient theme applied across UI elements - Copy button added to code blocks - Linter formatting and eslint-disable comments for cleaner code ## Closes Closes #8 Closes #11 Closes #14 Closes #15 Closes #20 Closes #22 Closes #24 Closes #25 Closes #34 Closes #35 Closes #36 Closes #37 Closes #69 Closes #70 ## Test Plan - [ ] Verify clipboard history captures code from code block copy buttons - [ ] Verify clipboard history captures manually selected text from terminal - [ ] Test snippet library CRUD operations and insertion - [ ] Test quick actions panel with default and custom actions - [ ] Test git panel shows correct status, branch, and performs git operations - [ ] Test session history auto-save and restore - [ ] Test session import/export roundtrip - [ ] Verify high contrast mode provides adequate contrast - [ ] Test minimize to tray functionality and tray menu - [ ] Verify trans-pride gradient theme displays correctly in all themes --- *✨ This PR was created with help from Hikari~ 🌸* Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Reviewed-on: #68 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
268 lines
7.3 KiB
Rust
268 lines
7.3 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct ClaudeStartOptions {
|
|
#[serde(default)]
|
|
pub working_dir: String,
|
|
|
|
#[serde(default)]
|
|
pub model: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub api_key: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub custom_instructions: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub mcp_servers_json: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub allowed_tools: Vec<String>,
|
|
|
|
#[serde(default)]
|
|
pub skip_greeting: bool,
|
|
|
|
#[serde(default)]
|
|
pub resume_session_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HikariConfig {
|
|
#[serde(default)]
|
|
pub model: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub api_key: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub custom_instructions: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub mcp_servers_json: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub auto_granted_tools: Vec<String>,
|
|
|
|
#[serde(default)]
|
|
pub theme: Theme,
|
|
|
|
#[serde(default = "default_greeting_enabled")]
|
|
pub greeting_enabled: bool,
|
|
|
|
#[serde(default)]
|
|
pub greeting_custom_prompt: Option<String>,
|
|
|
|
#[serde(default = "default_notifications_enabled")]
|
|
pub notifications_enabled: bool,
|
|
|
|
#[serde(default = "default_notification_volume")]
|
|
pub notification_volume: f32,
|
|
|
|
#[serde(default)]
|
|
pub always_on_top: bool,
|
|
|
|
#[serde(default = "default_update_checks_enabled")]
|
|
pub update_checks_enabled: bool,
|
|
|
|
#[serde(default)]
|
|
pub character_panel_width: Option<u32>,
|
|
|
|
#[serde(default = "default_font_size")]
|
|
pub font_size: u32,
|
|
|
|
#[serde(default)]
|
|
pub minimize_to_tray: bool,
|
|
|
|
#[serde(default)]
|
|
pub streamer_mode: bool,
|
|
|
|
#[serde(default)]
|
|
pub streamer_hide_paths: bool,
|
|
|
|
#[serde(default)]
|
|
pub compact_mode: bool,
|
|
|
|
// Profile fields
|
|
#[serde(default)]
|
|
pub profile_name: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub profile_avatar_path: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub profile_bio: Option<String>,
|
|
|
|
// Custom theme colors
|
|
#[serde(default)]
|
|
pub custom_theme_colors: CustomThemeColors,
|
|
}
|
|
|
|
impl Default for HikariConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
model: None,
|
|
api_key: None,
|
|
custom_instructions: None,
|
|
mcp_servers_json: None,
|
|
auto_granted_tools: Vec::new(),
|
|
theme: Theme::default(),
|
|
greeting_enabled: true,
|
|
greeting_custom_prompt: None,
|
|
notifications_enabled: true,
|
|
notification_volume: 0.7,
|
|
always_on_top: false,
|
|
update_checks_enabled: true,
|
|
character_panel_width: None,
|
|
font_size: 14,
|
|
minimize_to_tray: false,
|
|
streamer_mode: false,
|
|
streamer_hide_paths: false,
|
|
compact_mode: false,
|
|
profile_name: None,
|
|
profile_avatar_path: None,
|
|
profile_bio: None,
|
|
custom_theme_colors: CustomThemeColors::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn default_update_checks_enabled() -> bool {
|
|
true
|
|
}
|
|
|
|
fn default_greeting_enabled() -> bool {
|
|
true
|
|
}
|
|
|
|
fn default_notifications_enabled() -> bool {
|
|
true
|
|
}
|
|
|
|
fn default_notification_volume() -> f32 {
|
|
0.7
|
|
}
|
|
|
|
fn default_font_size() -> u32 {
|
|
14
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum Theme {
|
|
#[default]
|
|
Dark,
|
|
Light,
|
|
#[serde(rename = "high-contrast")]
|
|
HighContrast,
|
|
Custom,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
|
pub struct CustomThemeColors {
|
|
#[serde(default)]
|
|
pub bg_primary: Option<String>,
|
|
#[serde(default)]
|
|
pub bg_secondary: Option<String>,
|
|
#[serde(default)]
|
|
pub bg_terminal: Option<String>,
|
|
#[serde(default)]
|
|
pub accent_primary: Option<String>,
|
|
#[serde(default)]
|
|
pub accent_secondary: Option<String>,
|
|
#[serde(default)]
|
|
pub text_primary: Option<String>,
|
|
#[serde(default)]
|
|
pub text_secondary: Option<String>,
|
|
#[serde(default)]
|
|
pub border_color: Option<String>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_default_config() {
|
|
let config = HikariConfig::default();
|
|
assert!(config.model.is_none());
|
|
assert!(config.api_key.is_none());
|
|
assert!(config.custom_instructions.is_none());
|
|
assert!(config.mcp_servers_json.is_none());
|
|
assert!(config.auto_granted_tools.is_empty());
|
|
assert_eq!(config.theme, Theme::Dark);
|
|
assert!(config.greeting_enabled);
|
|
assert!(config.greeting_custom_prompt.is_none());
|
|
assert!(!config.always_on_top);
|
|
assert!(config.update_checks_enabled);
|
|
assert!(config.character_panel_width.is_none());
|
|
assert_eq!(config.font_size, 14);
|
|
assert!(!config.minimize_to_tray);
|
|
assert!(!config.streamer_mode);
|
|
assert!(!config.streamer_hide_paths);
|
|
assert!(!config.compact_mode);
|
|
assert!(config.profile_name.is_none());
|
|
assert!(config.profile_avatar_path.is_none());
|
|
assert!(config.profile_bio.is_none());
|
|
assert_eq!(config.custom_theme_colors, CustomThemeColors::default());
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_serialization() {
|
|
let config = HikariConfig {
|
|
model: Some("claude-sonnet-4-20250514".to_string()),
|
|
api_key: None,
|
|
custom_instructions: Some("Be helpful".to_string()),
|
|
mcp_servers_json: None,
|
|
auto_granted_tools: vec!["Read".to_string(), "Glob".to_string()],
|
|
theme: Theme::Light,
|
|
greeting_enabled: true,
|
|
greeting_custom_prompt: Some("Hello!".to_string()),
|
|
notifications_enabled: true,
|
|
notification_volume: 0.7,
|
|
always_on_top: true,
|
|
update_checks_enabled: true,
|
|
character_panel_width: Some(400),
|
|
font_size: 16,
|
|
minimize_to_tray: true,
|
|
streamer_mode: false,
|
|
streamer_hide_paths: false,
|
|
compact_mode: false,
|
|
profile_name: Some("Test User".to_string()),
|
|
profile_avatar_path: None,
|
|
profile_bio: Some("A test bio".to_string()),
|
|
custom_theme_colors: CustomThemeColors::default(),
|
|
};
|
|
|
|
let json = serde_json::to_string(&config).unwrap();
|
|
let deserialized: HikariConfig = serde_json::from_str(&json).unwrap();
|
|
|
|
assert_eq!(deserialized.model, config.model);
|
|
assert_eq!(deserialized.custom_instructions, config.custom_instructions);
|
|
assert_eq!(deserialized.auto_granted_tools, config.auto_granted_tools);
|
|
assert_eq!(deserialized.theme, Theme::Light);
|
|
assert!(deserialized.greeting_enabled);
|
|
assert_eq!(
|
|
deserialized.greeting_custom_prompt,
|
|
Some("Hello!".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_theme_serialization() {
|
|
let dark = Theme::Dark;
|
|
let light = Theme::Light;
|
|
let high_contrast = Theme::HighContrast;
|
|
|
|
assert_eq!(serde_json::to_string(&dark).unwrap(), "\"dark\"");
|
|
assert_eq!(serde_json::to_string(&light).unwrap(), "\"light\"");
|
|
assert_eq!(
|
|
serde_json::to_string(&high_contrast).unwrap(),
|
|
"\"high-contrast\""
|
|
);
|
|
|
|
let custom = Theme::Custom;
|
|
assert_eq!(serde_json::to_string(&custom).unwrap(), "\"custom\"");
|
|
}
|
|
}
|