app.go changes: - Add loadConfigOrDefault() helper that returns sensible defaults when config loading fails, ensuring TUI can always start even with corrupted config files - Extract scroll navigation constants (scrollOverhead, minHalfPageScroll, minContentHeight) for clarity and consistency - Apply accent border styling to loading card for visual polish - Replace inline config.Load() calls with loadConfigOrDefault() setup.go changes: - Use loadConfigOrDefault() for consistent error handling during setup wizard initialization and config persistence The loadConfigOrDefault pattern improves user experience by gracefully degrading rather than failing hard when config issues occur. Users can still access the TUI and reconfigure via the Settings tab. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/theirongolddev/cburn/internal/config"
|
|
"github.com/theirongolddev/cburn/internal/tui/theme"
|
|
|
|
"github.com/charmbracelet/huh"
|
|
)
|
|
|
|
// setupValues holds the form-bound variables for the setup wizard.
|
|
type setupValues struct {
|
|
sessionKey string
|
|
adminKey string
|
|
days int
|
|
theme string
|
|
}
|
|
|
|
// newSetupForm builds the huh form for first-run configuration.
|
|
func newSetupForm(numSessions int, claudeDir string, vals *setupValues) *huh.Form {
|
|
cfg := loadConfigOrDefault()
|
|
|
|
// Pre-populate defaults
|
|
vals.days = cfg.General.DefaultDays
|
|
if vals.days == 0 {
|
|
vals.days = 30
|
|
}
|
|
vals.theme = cfg.Appearance.Theme
|
|
if vals.theme == "" {
|
|
vals.theme = "flexoki-dark"
|
|
}
|
|
|
|
// Build welcome text
|
|
welcomeDesc := "Let's configure your dashboard."
|
|
if numSessions > 0 {
|
|
welcomeDesc = fmt.Sprintf("Found %d sessions in %s.", numSessions, claudeDir)
|
|
}
|
|
|
|
// Placeholder text for key fields
|
|
sessionPlaceholder := "sk-ant-sid... (Enter to skip)"
|
|
if key := config.GetSessionKey(cfg); key != "" {
|
|
sessionPlaceholder = maskKey(key) + " (Enter to keep)"
|
|
}
|
|
adminPlaceholder := "sk-ant-admin-... (Enter to skip)"
|
|
if key := config.GetAdminAPIKey(cfg); key != "" {
|
|
adminPlaceholder = maskKey(key) + " (Enter to keep)"
|
|
}
|
|
|
|
// Build theme options from the registered theme list
|
|
themeOpts := make([]huh.Option[string], len(theme.All))
|
|
for i, t := range theme.All {
|
|
themeOpts[i] = huh.NewOption(t.Name, t.Name)
|
|
}
|
|
|
|
return huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewNote().
|
|
Title("Welcome to cburn").
|
|
Description(welcomeDesc).
|
|
Next(true).
|
|
NextLabel("Start"),
|
|
),
|
|
|
|
huh.NewGroup(
|
|
huh.NewInput().
|
|
Title("Claude.ai session key").
|
|
Description("For rate-limit and subscription data.\nclaude.ai > DevTools > Application > Cookies > sessionKey").
|
|
Placeholder(sessionPlaceholder).
|
|
EchoMode(huh.EchoModePassword).
|
|
Value(&vals.sessionKey),
|
|
|
|
huh.NewInput().
|
|
Title("Anthropic Admin API key").
|
|
Description("For real cost data from the billing API.").
|
|
Placeholder(adminPlaceholder).
|
|
EchoMode(huh.EchoModePassword).
|
|
Value(&vals.adminKey),
|
|
),
|
|
|
|
huh.NewGroup(
|
|
huh.NewSelect[int]().
|
|
Title("Default time range").
|
|
Options(
|
|
huh.NewOption("7 days", 7),
|
|
huh.NewOption("30 days", 30),
|
|
huh.NewOption("90 days", 90),
|
|
).
|
|
Value(&vals.days),
|
|
|
|
huh.NewSelect[string]().
|
|
Title("Color theme").
|
|
Options(themeOpts...).
|
|
Value(&vals.theme),
|
|
),
|
|
).WithTheme(huh.ThemeDracula()).WithShowHelp(false)
|
|
}
|
|
|
|
// saveSetupConfig persists the setup wizard values to the config file.
|
|
func (a *App) saveSetupConfig() error {
|
|
cfg := loadConfigOrDefault()
|
|
|
|
if a.setupVals.sessionKey != "" {
|
|
cfg.ClaudeAI.SessionKey = a.setupVals.sessionKey
|
|
}
|
|
if a.setupVals.adminKey != "" {
|
|
cfg.AdminAPI.APIKey = a.setupVals.adminKey
|
|
}
|
|
cfg.General.DefaultDays = a.setupVals.days
|
|
a.days = a.setupVals.days
|
|
|
|
cfg.Appearance.Theme = a.setupVals.theme
|
|
theme.SetActive(a.setupVals.theme)
|
|
|
|
return config.Save(cfg)
|
|
}
|
|
|
|
func maskKey(key string) string {
|
|
if len(key) > 16 {
|
|
return key[:8] + "..." + key[len(key)-4:]
|
|
}
|
|
if len(key) > 4 {
|
|
return key[:4] + "..."
|
|
}
|
|
return "****"
|
|
}
|