generated from nhcarrigan/template
b88f25a61b
## Summary Implements support for Claude Code CLI v2.1.81 features and adds a global CLAUDE.md editor, closing issues #237, #239, #244, #245, #246, #247, #248, and #262. ### Stream-JSON forward-compatibility (#245, #246, #247, #248) - **#248** — `output_style` field added to `System` init message; silently accepted for forward-compat - **#245** — `fast_mode_state` field added to `Result` message; logged at debug level - **#246** — `model_usage` field added to `Result` message; per-model breakdown logged at debug level - **#247** — `total_cost_usd` field added to `Result` message; authoritative cost logged at debug level ### New config options (#237, #239, #244) - **#237** — `bare_mode` config toggle: passes `--bare` to Claude Code, suppressing UI chrome for scripted headless `-p` calls - **#239** — `show_clear_context_on_plan_accept` toggle: passes `showClearContextOnPlanAccept: false` in `--settings` when disabled - **#244** — `custom_model_option` text field: sets `ANTHROPIC_CUSTOM_MODEL_OPTION` env var for custom model providers ### Global CLAUDE.md editor (#262) - New Tauri commands `get_global_claude_md` / `save_global_claude_md` read/write `~/.claude/CLAUDE.md` (creates file + directory if absent) - New "Global Instructions" section in the Config Sidebar with a textarea and Save button ### Bug fix (pre-existing) `disable_cron` and `disable_skill_shell_execution` were saved to `HikariConfig` but never passed to `start_claude` invocations — fixed in all 9 call sites. All 3 new config fields are also wired through all 9 call sites. All changes pass `check-all.sh` (ESLint → Prettier → svelte-check → Vitest → Clippy → cargo test with llvm-cov). ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #263 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
619 lines
19 KiB
Rust
619 lines
19 KiB
Rust
use std::collections::HashMap;
|
|
|
|
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>,
|
|
|
|
#[serde(default)]
|
|
pub use_worktree: bool,
|
|
|
|
#[serde(default)]
|
|
pub disable_1m_context: bool,
|
|
|
|
#[serde(default)]
|
|
pub max_output_tokens: Option<u64>,
|
|
|
|
#[serde(default)]
|
|
pub disable_cron: bool,
|
|
|
|
#[serde(default = "default_include_git_instructions")]
|
|
pub include_git_instructions: bool,
|
|
|
|
#[serde(default = "default_enable_claudeai_mcp_servers")]
|
|
pub enable_claudeai_mcp_servers: bool,
|
|
|
|
#[serde(default)]
|
|
pub auto_memory_directory: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub model_overrides: Option<HashMap<String, String>>,
|
|
|
|
#[serde(default)]
|
|
pub session_name: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub disable_skill_shell_execution: bool,
|
|
|
|
/// Pass `--bare` flag to suppress UI chrome, useful for scripted headless `-p` calls (v2.1.81+).
|
|
#[serde(default)]
|
|
pub bare_mode: bool,
|
|
|
|
/// Controls `showClearContextOnPlanAccept` in `--settings` (v2.1.81+).
|
|
/// Defaults to true (matching CLI default). Set to false to suppress the dialog.
|
|
#[serde(default = "default_show_clear_context")]
|
|
pub show_clear_context_on_plan_accept: bool,
|
|
|
|
/// Sets `ANTHROPIC_CUSTOM_MODEL_OPTION` env var for custom model providers (v2.1.81+).
|
|
#[serde(default)]
|
|
pub custom_model_option: 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,
|
|
|
|
#[serde(default)]
|
|
pub use_worktree: bool,
|
|
|
|
#[serde(default)]
|
|
pub disable_1m_context: bool,
|
|
|
|
#[serde(default)]
|
|
pub max_output_tokens: Option<u64>,
|
|
|
|
#[serde(default)]
|
|
pub trusted_workspaces: Vec<String>,
|
|
|
|
// Background image settings
|
|
#[serde(default)]
|
|
pub background_image_path: Option<String>,
|
|
|
|
#[serde(default = "default_background_image_opacity")]
|
|
pub background_image_opacity: f32,
|
|
|
|
#[serde(default)]
|
|
pub show_thinking_blocks: bool,
|
|
|
|
// Custom terminal font settings
|
|
#[serde(default)]
|
|
pub custom_font_path: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub custom_font_family: Option<String>,
|
|
|
|
// Custom UI font settings
|
|
#[serde(default)]
|
|
pub custom_ui_font_path: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub custom_ui_font_family: Option<String>,
|
|
|
|
// Task Loop auto-commit settings
|
|
#[serde(default)]
|
|
pub task_loop_auto_commit: bool,
|
|
|
|
#[serde(default = "default_task_loop_commit_prefix")]
|
|
pub task_loop_commit_prefix: String,
|
|
|
|
#[serde(default)]
|
|
pub task_loop_include_summary: bool,
|
|
|
|
#[serde(default)]
|
|
pub disable_cron: bool,
|
|
|
|
#[serde(default = "default_include_git_instructions")]
|
|
pub include_git_instructions: bool,
|
|
|
|
#[serde(default = "default_enable_claudeai_mcp_servers")]
|
|
pub enable_claudeai_mcp_servers: bool,
|
|
|
|
#[serde(default)]
|
|
pub auto_memory_directory: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub model_overrides: Option<HashMap<String, String>>,
|
|
|
|
/// Prevents skill scripts from executing shell commands (Claude Code v2.1.91+).
|
|
/// Passes `"disableSkillShellExecution": true` via the `--settings` flag.
|
|
#[serde(default)]
|
|
pub disable_skill_shell_execution: bool,
|
|
|
|
/// Pass `--bare` flag to suppress UI chrome, useful for scripted headless `-p` calls (v2.1.81+).
|
|
#[serde(default)]
|
|
pub bare_mode: bool,
|
|
|
|
/// Controls `showClearContextOnPlanAccept` in `--settings` (v2.1.81+).
|
|
#[serde(default = "default_show_clear_context")]
|
|
pub show_clear_context_on_plan_accept: bool,
|
|
|
|
/// Sets `ANTHROPIC_CUSTOM_MODEL_OPTION` env var for custom model providers (v2.1.81+).
|
|
#[serde(default)]
|
|
pub custom_model_option: Option<String>,
|
|
}
|
|
|
|
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,
|
|
use_worktree: false,
|
|
disable_1m_context: false,
|
|
max_output_tokens: None,
|
|
trusted_workspaces: Vec::new(),
|
|
background_image_path: None,
|
|
background_image_opacity: 0.3,
|
|
show_thinking_blocks: false,
|
|
custom_font_path: None,
|
|
custom_font_family: None,
|
|
custom_ui_font_path: None,
|
|
custom_ui_font_family: None,
|
|
task_loop_auto_commit: false,
|
|
task_loop_commit_prefix: "feat".to_string(),
|
|
task_loop_include_summary: false,
|
|
disable_cron: false,
|
|
include_git_instructions: true,
|
|
enable_claudeai_mcp_servers: true,
|
|
auto_memory_directory: None,
|
|
model_overrides: None,
|
|
disable_skill_shell_execution: false,
|
|
bare_mode: false,
|
|
show_clear_context_on_plan_accept: true,
|
|
custom_model_option: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
fn default_background_image_opacity() -> f32 {
|
|
0.3
|
|
}
|
|
|
|
fn default_task_loop_commit_prefix() -> String {
|
|
"feat".to_string()
|
|
}
|
|
|
|
fn default_include_git_instructions() -> bool {
|
|
true
|
|
}
|
|
|
|
fn default_enable_claudeai_mcp_servers() -> bool {
|
|
true
|
|
}
|
|
|
|
fn default_show_clear_context() -> 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,
|
|
Dracula,
|
|
Catppuccin,
|
|
Nord,
|
|
Solarized,
|
|
#[serde(rename = "solarized-light")]
|
|
SolarizedLight,
|
|
#[serde(rename = "catppuccin-latte")]
|
|
CatppuccinLatte,
|
|
#[serde(rename = "gruvbox-light")]
|
|
GruvboxLight,
|
|
#[serde(rename = "rose-pine-dawn")]
|
|
RosePineDawn,
|
|
}
|
|
|
|
#[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);
|
|
assert!(!config.use_worktree);
|
|
assert!(!config.disable_1m_context);
|
|
assert!(config.trusted_workspaces.is_empty());
|
|
assert!(!config.show_thinking_blocks);
|
|
assert!(config.custom_font_path.is_none());
|
|
assert!(config.custom_font_family.is_none());
|
|
assert!(config.custom_ui_font_path.is_none());
|
|
assert!(config.custom_ui_font_family.is_none());
|
|
assert!(!config.task_loop_auto_commit);
|
|
assert_eq!(config.task_loop_commit_prefix, "feat");
|
|
assert!(!config.task_loop_include_summary);
|
|
assert!(!config.disable_cron);
|
|
assert!(config.include_git_instructions);
|
|
assert!(config.enable_claudeai_mcp_servers);
|
|
assert!(config.auto_memory_directory.is_none());
|
|
assert!(config.model_overrides.is_none());
|
|
assert!(!config.disable_skill_shell_execution);
|
|
assert!(!config.bare_mode);
|
|
assert!(config.show_clear_context_on_plan_accept);
|
|
assert!(config.custom_model_option.is_none());
|
|
}
|
|
|
|
#[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,
|
|
use_worktree: true,
|
|
disable_1m_context: false,
|
|
max_output_tokens: Some(32000),
|
|
trusted_workspaces: vec!["/home/naomi/projects/trusted".to_string()],
|
|
background_image_path: Some("/home/naomi/bg.png".to_string()),
|
|
background_image_opacity: 0.25,
|
|
show_thinking_blocks: true,
|
|
custom_font_path: Some("/home/naomi/.fonts/MyFont.ttf".to_string()),
|
|
custom_font_family: Some("MyFont".to_string()),
|
|
custom_ui_font_path: None,
|
|
custom_ui_font_family: None,
|
|
task_loop_auto_commit: true,
|
|
task_loop_commit_prefix: "fix".to_string(),
|
|
task_loop_include_summary: true,
|
|
disable_cron: true,
|
|
include_git_instructions: false,
|
|
enable_claudeai_mcp_servers: false,
|
|
auto_memory_directory: Some("/custom/memory".to_string()),
|
|
model_overrides: Some(HashMap::from([(
|
|
"claude-opus-4-6".to_string(),
|
|
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-opus-4-6-v1".to_string(),
|
|
)])),
|
|
disable_skill_shell_execution: true,
|
|
bare_mode: false,
|
|
show_clear_context_on_plan_accept: true,
|
|
custom_model_option: None,
|
|
};
|
|
|
|
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())
|
|
);
|
|
assert!(deserialized.task_loop_auto_commit);
|
|
assert_eq!(deserialized.task_loop_commit_prefix, "fix");
|
|
assert!(deserialized.task_loop_include_summary);
|
|
assert!(deserialized.disable_cron);
|
|
assert!(!deserialized.include_git_instructions);
|
|
assert!(!deserialized.enable_claudeai_mcp_servers);
|
|
assert_eq!(
|
|
deserialized.auto_memory_directory,
|
|
Some("/custom/memory".to_string())
|
|
);
|
|
assert!(deserialized.model_overrides.is_some());
|
|
let overrides = deserialized.model_overrides.unwrap();
|
|
assert_eq!(
|
|
overrides.get("claude-opus-4-6").map(String::as_str),
|
|
Some("arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-opus-4-6-v1")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_theme_serialization() {
|
|
assert_eq!(serde_json::to_string(&Theme::Dark).unwrap(), "\"dark\"");
|
|
assert_eq!(serde_json::to_string(&Theme::Light).unwrap(), "\"light\"");
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::HighContrast).unwrap(),
|
|
"\"high-contrast\""
|
|
);
|
|
assert_eq!(serde_json::to_string(&Theme::Custom).unwrap(), "\"custom\"");
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::Dracula).unwrap(),
|
|
"\"dracula\""
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::Catppuccin).unwrap(),
|
|
"\"catppuccin\""
|
|
);
|
|
assert_eq!(serde_json::to_string(&Theme::Nord).unwrap(), "\"nord\"");
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::Solarized).unwrap(),
|
|
"\"solarized\""
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::SolarizedLight).unwrap(),
|
|
"\"solarized-light\""
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::CatppuccinLatte).unwrap(),
|
|
"\"catppuccin-latte\""
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::GruvboxLight).unwrap(),
|
|
"\"gruvbox-light\""
|
|
);
|
|
assert_eq!(
|
|
serde_json::to_string(&Theme::RosePineDawn).unwrap(),
|
|
"\"rose-pine-dawn\""
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_theme_deserialization() {
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"dracula\"").unwrap(),
|
|
Theme::Dracula
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"catppuccin\"").unwrap(),
|
|
Theme::Catppuccin
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"nord\"").unwrap(),
|
|
Theme::Nord
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"solarized\"").unwrap(),
|
|
Theme::Solarized
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"solarized-light\"").unwrap(),
|
|
Theme::SolarizedLight
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"catppuccin-latte\"").unwrap(),
|
|
Theme::CatppuccinLatte
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"gruvbox-light\"").unwrap(),
|
|
Theme::GruvboxLight
|
|
);
|
|
assert_eq!(
|
|
serde_json::from_str::<Theme>("\"rose-pine-dawn\"").unwrap(),
|
|
Theme::RosePineDawn
|
|
);
|
|
}
|
|
}
|