package internal import ( "os" "path/filepath" "sync" "gopkg.in/yaml.v3" ) type ProjectConfig struct { Streaming StreamingConfig `yaml:"streaming"` Markdown MarkdownConfig `yaml:"markdown"` UI UIConfig `yaml:"ui"` TTS TTSConfig `yaml:"tts"` } type StreamingConfig struct { LineDelayMs int `yaml:"line_delay_ms"` LastLineDelayMs int `yaml:"last_line_delay_ms"` } type MarkdownConfig struct { GlamourStyle string `yaml:"glamour_style"` WrapWidth int `yaml:"wrap_width"` } type UIConfig struct { Logo string `yaml:"logo"` UserPrefix string `yaml:"user_prefix"` } type TTSConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` Auto bool `yaml:"auto"` } var ( defaultCfg = ProjectConfig{ Streaming: StreamingConfig{ LineDelayMs: 1000, LastLineDelayMs: 600, }, Markdown: MarkdownConfig{ GlamourStyle: "dark", WrapWidth: 0, }, UI: UIConfig{ Logo: "🦐", UserPrefix: "👀 ", }, TTS: TTSConfig{ Enabled: false, Port: 9876, Auto: true, }, } projCfg *ProjectConfig projCfgLock sync.RWMutex ) func LoadProjectConfig() error { projCfgLock.Lock() defer projCfgLock.Unlock() cfgPath := getConfigPath() if cfgPath == "" { projCfg = &defaultCfg return nil } data, err := os.ReadFile(cfgPath) if err != nil { if os.IsNotExist(err) { projCfg = &defaultCfg return nil } return err } var cfg ProjectConfig if err := yaml.Unmarshal(data, &cfg); err != nil { return err } if cfg.Streaming.LineDelayMs <= 0 { cfg.Streaming.LineDelayMs = defaultCfg.Streaming.LineDelayMs } if cfg.Streaming.LastLineDelayMs <= 0 { cfg.Streaming.LastLineDelayMs = defaultCfg.Streaming.LastLineDelayMs } if cfg.Markdown.GlamourStyle == "" { cfg.Markdown.GlamourStyle = defaultCfg.Markdown.GlamourStyle } if cfg.Markdown.WrapWidth < 0 { cfg.Markdown.WrapWidth = 0 } if cfg.UI.Logo == "" { cfg.UI.Logo = defaultCfg.UI.Logo } if cfg.UI.UserPrefix == "" { cfg.UI.UserPrefix = defaultCfg.UI.UserPrefix } if cfg.TTS.Port <= 0 { cfg.TTS.Port = defaultCfg.TTS.Port } projCfg = &cfg return nil } func GetProjectConfig() *ProjectConfig { projCfgLock.RLock() defer projCfgLock.RUnlock() if projCfg == nil { return &defaultCfg } return projCfg } func getConfigPath() string { if path := os.Getenv("HXCLAW_CONFIG"); path != "" { return path } return filepath.Join(".", "project.config.yml") }