217 lines
5.3 KiB
Go
217 lines
5.3 KiB
Go
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, onboarding bool) error {
|
|
exitLabel := "Done"
|
|
if onboarding {
|
|
exitLabel = "Next"
|
|
}
|
|
|
|
// track if we should default to "back" (Next) after configuring a provider
|
|
defaultToExit := false
|
|
|
|
for {
|
|
var options []huh.Option[string]
|
|
for _, name := range AllProviders {
|
|
options = append(options, huh.NewOption(formatProviderOption(cfg, name), name))
|
|
}
|
|
options = append(options, huh.NewOption(exitLabel, "back"))
|
|
|
|
selected := ""
|
|
if defaultToExit {
|
|
selected = "back"
|
|
}
|
|
|
|
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}
|
|
defaultToExit = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|