package steps import ( "miasma-installer/tui/styles" "strings" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) type UserModel struct { Username string Password string usernameInput textinput.Model passwordInput textinput.Model confirmInput textinput.Model focusIndex int Finished bool } func NewUserModel() UserModel { ui := textinput.New() ui.Placeholder = "username" ui.Focus() ui.CharLimit = 32 ui.Width = 40 pi := textinput.New() pi.Placeholder = "password" pi.EchoMode = textinput.EchoPassword pi.Width = 40 ci := textinput.New() ci.Placeholder = "confirm password" ci.EchoMode = textinput.EchoPassword ci.Width = 40 return UserModel{ usernameInput: ui, passwordInput: pi, confirmInput: ci, } } func (m UserModel) Init() tea.Cmd { return textinput.Blink } func (m UserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.Finished { return m, nil } switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "enter": if m.focusIndex == 0 && m.usernameInput.Value() != "" { m.focusIndex = 1 m.usernameInput.Blur() return m, m.passwordInput.Focus() } else if m.focusIndex == 1 && m.passwordInput.Value() != "" { m.focusIndex = 2 m.passwordInput.Blur() return m, m.confirmInput.Focus() } else if m.focusIndex == 2 { if m.passwordInput.Value() == m.confirmInput.Value() && m.passwordInput.Value() != "" { m.Username = m.usernameInput.Value() m.Password = m.passwordInput.Value() m.Finished = true return m, nil } } case "tab", "shift+tab": m.focusIndex = (m.focusIndex + 1) % 3 cmds := make([]tea.Cmd, 3) if m.focusIndex == 0 { cmds[0] = m.usernameInput.Focus() m.passwordInput.Blur() m.confirmInput.Blur() } else if m.focusIndex == 1 { cmds[1] = m.passwordInput.Focus() m.usernameInput.Blur() m.confirmInput.Blur() } else { cmds[2] = m.confirmInput.Focus() m.usernameInput.Blur() m.passwordInput.Blur() } return m, tea.Batch(cmds...) } } var cmd tea.Cmd if m.focusIndex == 0 { m.usernameInput, cmd = m.usernameInput.Update(msg) } else if m.focusIndex == 1 { m.passwordInput, cmd = m.passwordInput.Update(msg) } else { m.confirmInput, cmd = m.confirmInput.Update(msg) } return m, cmd } func (m UserModel) View() string { var content strings.Builder content.WriteString(styles.TitleStyle.Render("👤 User Account")) content.WriteString("\n\n") info := "Create your user account.\nThis user will have sudo privileges." content.WriteString(styles.InfoBoxStyle.Render(info)) content.WriteString("\n\n") content.WriteString(styles.LabelStyle.Render("Username:")) content.WriteString("\n") usernameStyle := styles.InputStyle if m.focusIndex == 0 { usernameStyle = styles.FocusedInputStyle } content.WriteString(usernameStyle.Render(m.usernameInput.View())) content.WriteString("\n\n") content.WriteString(styles.LabelStyle.Render("Password:")) content.WriteString("\n") passwordStyle := styles.InputStyle if m.focusIndex == 1 { passwordStyle = styles.FocusedInputStyle } content.WriteString(passwordStyle.Render(m.passwordInput.View())) content.WriteString("\n\n") content.WriteString(styles.LabelStyle.Render("Confirm Password:")) content.WriteString("\n") confirmStyle := styles.InputStyle if m.focusIndex == 2 { confirmStyle = styles.FocusedInputStyle } content.WriteString(confirmStyle.Render(m.confirmInput.View())) content.WriteString("\n\n") if m.passwordInput.Value() != "" && m.confirmInput.Value() != "" { if m.passwordInput.Value() != m.confirmInput.Value() { content.WriteString(styles.ErrorStyle.Render("✗ Passwords do not match")) content.WriteString("\n\n") } else { content.WriteString(styles.SuccessStyle.Render("✓ Passwords match")) content.WriteString("\n\n") } } content.WriteString(styles.RenderHelp("Tab", "Switch fields", "Enter", "Continue")) return styles.AppStyle.Render(content.String()) }