generated from nhcarrigan/template
d16644a1d6
This fixes two severe bugs: 1. Missing debugConsoleStore import causing undefined variable errors 2. Replace non-existent .log() method with console.log() 3. Add #[serde(default)] to HikariConfig to handle missing fields gracefully The undefined variable was causing initialization to fail, which prevented the permission modal from displaying properly.
320 lines
8.8 KiB
Rust
320 lines
8.8 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)]
|
|
#[serde(default)]
|
|
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 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,
|
|
|
|
// Token budget settings
|
|
#[serde(default)]
|
|
pub budget_enabled: bool,
|
|
|
|
#[serde(default)]
|
|
pub session_token_budget: Option<u64>,
|
|
|
|
#[serde(default)]
|
|
pub session_cost_budget: Option<f64>,
|
|
|
|
#[serde(default = "default_budget_action")]
|
|
pub budget_action: BudgetAction,
|
|
|
|
#[serde(default = "default_budget_warning_threshold")]
|
|
pub budget_warning_threshold: f32,
|
|
|
|
#[serde(default = "default_discord_rpc_enabled")]
|
|
pub discord_rpc_enabled: bool,
|
|
}
|
|
|
|
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,
|
|
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(),
|
|
budget_enabled: false,
|
|
session_token_budget: None,
|
|
session_cost_budget: None,
|
|
budget_action: BudgetAction::Warn,
|
|
budget_warning_threshold: 0.8,
|
|
discord_rpc_enabled: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
fn default_budget_action() -> BudgetAction {
|
|
BudgetAction::Warn
|
|
}
|
|
|
|
fn default_budget_warning_threshold() -> f32 {
|
|
0.8
|
|
}
|
|
|
|
fn default_discord_rpc_enabled() -> bool {
|
|
true
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum BudgetAction {
|
|
#[default]
|
|
Warn,
|
|
Block,
|
|
}
|
|
|
|
#[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.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());
|
|
assert!(!config.budget_enabled);
|
|
assert!(config.session_token_budget.is_none());
|
|
assert!(config.session_cost_budget.is_none());
|
|
assert_eq!(config.budget_action, BudgetAction::Warn);
|
|
assert!((config.budget_warning_threshold - 0.8).abs() < f32::EPSILON);
|
|
assert!(config.discord_rpc_enabled);
|
|
}
|
|
|
|
#[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,
|
|
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(),
|
|
budget_enabled: true,
|
|
session_token_budget: Some(100000),
|
|
session_cost_budget: Some(1.50),
|
|
budget_action: BudgetAction::Block,
|
|
budget_warning_threshold: 0.75,
|
|
discord_rpc_enabled: true,
|
|
};
|
|
|
|
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\"");
|
|
}
|
|
}
|