package main import ( "encoding/json" "fmt" "os" "path/filepath" ) type ProviderConfig struct { Name string `json:"name,omitempty"` BaseURL string `json:"base_url"` APIKey string `json:"api_key,omitempty"` Type string `json:"type,omitempty"` } type Config struct { Theme string `json:"theme,omitempty"` DefaultProvider string `json:"default_provider,omitempty"` Providers map[string]ProviderConfig `json:"providers,omitempty"` } func configDir() string { dir := os.Getenv("XDG_CONFIG_HOME") if dir == "" { home, _ := os.UserHomeDir() dir = filepath.Join(home, ".config") } return filepath.Join(dir, "ludmila-ai-tui") } func configPath() string { return filepath.Join(configDir(), "config.json") } func themesDir() string { return filepath.Join(configDir(), "themes") } var defaultProviders = map[string]ProviderConfig{ "Mathink": { Name: "Mathink (FLM)", BaseURL: "http://localhost:52625", Type: "openai-compat", }, } func ensureConfigDir() error { dir := configDir() if err := os.MkdirAll(dir, 0o755); err != nil { return fmt.Errorf("create config dir %s: %w", dir, err) } if err := os.MkdirAll(themesDir(), 0o755); err != nil { return fmt.Errorf("create themes dir: %w", err) } return nil } func LoadConfig() (*Config, error) { cfg := &Config{ Theme: "system", Providers: defaultProviders, } path := configPath() data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { if mkErr := ensureConfigDir(); mkErr != nil { return cfg, fmt.Errorf("ensure config dir: %w", mkErr) } defaultJSON, _ := json.MarshalIndent(cfg, "", " ") _ = os.WriteFile(path, defaultJSON, 0o644) return cfg, nil } return cfg, fmt.Errorf("read config %s: %w", path, err) } if err := json.Unmarshal(data, cfg); err != nil { return cfg, fmt.Errorf("parse config %s: %w", path, err) } if cfg.Theme == "" { cfg.Theme = "system" } if cfg.Providers == nil || len(cfg.Providers) == 0 { cfg.Providers = defaultProviders } for id, p := range cfg.Providers { p.APIKey = os.ExpandEnv(p.APIKey) if p.Type == "" { p.Type = "openai-compat" } cfg.Providers[id] = p } var found bool if cfg.DefaultProvider != "" { for id := range cfg.Providers { if id == cfg.DefaultProvider { found = true break } } } if !found { for id := range cfg.Providers { cfg.DefaultProvider = id break } } return cfg, nil }