Files
hikari-desktop/src-tauri/src/config.rs
T
hikari d16644a1d6 fix: resolve critical runtime errors blocking permission modal
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.
2026-02-06 22:36:00 -08:00

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\"");
}
}