feat: better configuration
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/leonardotrapani/hyprvoice/internal/config"
|
||||
"github.com/leonardotrapani/hyprvoice/internal/provider"
|
||||
)
|
||||
|
||||
// getProviderDisplayName returns the display name for a provider
|
||||
func getProviderDisplayName(providerName string) string {
|
||||
if name, ok := providerDisplayNames[providerName]; ok {
|
||||
return name
|
||||
}
|
||||
return providerName
|
||||
}
|
||||
|
||||
// maskAPIKey returns a masked version of an API key for display
|
||||
func maskAPIKey(key string) string {
|
||||
if len(key) <= 8 {
|
||||
return "***"
|
||||
}
|
||||
return key[:7] + "..." + key[len(key)-4:]
|
||||
}
|
||||
|
||||
// getConfiguredProviders returns list of providers with API keys
|
||||
func getConfiguredProviders(cfg *config.Config) []string {
|
||||
var providers []string
|
||||
for name, pc := range cfg.Providers {
|
||||
if pc.APIKey != "" {
|
||||
providers = append(providers, name)
|
||||
}
|
||||
}
|
||||
return providers
|
||||
}
|
||||
|
||||
// editProviders handles the providers section edit with submenu
|
||||
func editProviders(cfg *config.Config) error {
|
||||
for {
|
||||
var options []huh.Option[string]
|
||||
for _, name := range AllProviders {
|
||||
options = append(options, huh.NewOption(formatProviderOption(cfg, name), name))
|
||||
}
|
||||
options = append(options, huh.NewOption("Back", "back"))
|
||||
|
||||
var selected string
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewSelect[string]().
|
||||
Title("Provider Settings").
|
||||
Description("Select a provider to configure API key").
|
||||
Options(options...).
|
||||
Value(&selected),
|
||||
),
|
||||
).WithTheme(getTheme())
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if selected == "back" {
|
||||
return nil
|
||||
}
|
||||
|
||||
apiKey, err := configureSingleProvider(cfg, selected)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if apiKey != "" {
|
||||
if cfg.Providers == nil {
|
||||
cfg.Providers = make(map[string]config.ProviderConfig)
|
||||
}
|
||||
cfg.Providers[selected] = config.ProviderConfig{APIKey: apiKey}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatProviderOption formats a provider menu option with status
|
||||
func formatProviderOption(cfg *config.Config, name string) string {
|
||||
var status string
|
||||
if pc, exists := cfg.Providers[name]; exists && pc.APIKey != "" {
|
||||
status = "(configured)"
|
||||
} else {
|
||||
status = "(not configured)"
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "openai":
|
||||
return fmt.Sprintf("OpenAI - Whisper + GPT %s", status)
|
||||
case "groq":
|
||||
return fmt.Sprintf("Groq - Whisper + Llama %s", status)
|
||||
case "mistral":
|
||||
return fmt.Sprintf("Mistral - Voxtral %s", status)
|
||||
case "elevenlabs":
|
||||
return fmt.Sprintf("ElevenLabs - Scribe %s", status)
|
||||
default:
|
||||
return fmt.Sprintf("%s %s", name, status)
|
||||
}
|
||||
}
|
||||
|
||||
// configureSingleProvider handles the complete flow for configuring a single provider's API key.
|
||||
// Shows confirm dialog if key exists, then prompts for new key if needed.
|
||||
// Returns the new API key (empty if user kept current) and any error.
|
||||
func configureSingleProvider(cfg *config.Config, providerName string) (string, error) {
|
||||
var existingKey string
|
||||
if pc, exists := cfg.Providers[providerName]; exists && pc.APIKey != "" {
|
||||
existingKey = pc.APIKey
|
||||
}
|
||||
|
||||
if existingKey != "" {
|
||||
displayName := getProviderDisplayName(providerName)
|
||||
masked := maskAPIKey(existingKey)
|
||||
|
||||
var update bool
|
||||
confirmForm := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title(fmt.Sprintf("%s API Key", displayName)).
|
||||
Description(fmt.Sprintf("Current: %s", masked)).
|
||||
Affirmative("Update key").
|
||||
Negative("Keep current").
|
||||
Value(&update),
|
||||
),
|
||||
).WithTheme(getTheme())
|
||||
|
||||
if err := confirmForm.Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !update {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
return inputAPIKey(providerName)
|
||||
}
|
||||
|
||||
func inputAPIKey(providerName string) (string, error) {
|
||||
p := provider.GetProvider(providerName)
|
||||
displayName := getProviderDisplayName(providerName)
|
||||
if p != nil {
|
||||
if name, ok := providerDisplayNames[p.Name()]; ok {
|
||||
displayName = name
|
||||
}
|
||||
}
|
||||
|
||||
var apiKey string
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title(fmt.Sprintf("%s API Key", displayName)).
|
||||
Description(fmt.Sprintf("Enter your %s API key", displayName)).
|
||||
EchoMode(huh.EchoModePassword).
|
||||
Value(&apiKey).
|
||||
Validate(func(s string) error {
|
||||
if s == "" {
|
||||
return fmt.Errorf("API key is required")
|
||||
}
|
||||
if p != nil && !p.ValidateAPIKey(s) {
|
||||
return fmt.Errorf("invalid API key format for %s", displayName)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
),
|
||||
).WithTheme(getTheme())
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return apiKey, nil
|
||||
}
|
||||
|
||||
// ensureProviderConfigured prompts for API key if provider not configured
|
||||
func ensureProviderConfigured(cfg *config.Config, selectedProvider string, configuredProviders []string) []string {
|
||||
providerName := selectedProvider
|
||||
switch selectedProvider {
|
||||
case "groq-transcription", "groq-translation":
|
||||
providerName = "groq"
|
||||
case "mistral-transcription":
|
||||
providerName = "mistral"
|
||||
}
|
||||
|
||||
for _, p := range configuredProviders {
|
||||
if p == providerName {
|
||||
return configuredProviders
|
||||
}
|
||||
}
|
||||
|
||||
apiKey, err := configureSingleProvider(cfg, providerName)
|
||||
if err != nil || apiKey == "" {
|
||||
return configuredProviders
|
||||
}
|
||||
|
||||
if cfg.Providers == nil {
|
||||
cfg.Providers = make(map[string]config.ProviderConfig)
|
||||
}
|
||||
cfg.Providers[providerName] = config.ProviderConfig{APIKey: apiKey}
|
||||
|
||||
return append(configuredProviders, providerName)
|
||||
}
|
||||
Reference in New Issue
Block a user