package ui import ( "sort" "charm.land/bubbles/v2/key" tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" "git.morlana.online/f.weber/vault-tui/internal/ui/keys" ) // textInputActive reports whether a free-text field currently owns the // keyboard, so global single-letter bindings (help "?", profiles "p", ...) // don't get eaten while the user is typing a value/filter that happens to // contain that letter. func (m *Model) textInputActive() bool { if m.top() == scrEditor { return true } if m.top() == scrAuth && m.auth.phase == 1 { return true } if (m.top() == scrMounts || m.top() == scrBrowser) && m.listReady && m.list.SettingFilter() { return true } return false } func (m *Model) openProfilePicker() (tea.Model, tea.Cmd) { names := make([]string, 0, len(m.a.File.Profiles)) for n := range m.a.File.Profiles { names = append(names, n) } sort.Strings(names) cursor := 0 for i, n := range names { if n == m.a.Settings.Profile { cursor = i } } m.profile = profileState{open: true, names: names, cursor: cursor} return m, nil } func (m *Model) updateProfilePicker(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { switch { case key.Matches(msg, m.keys.Up): if m.profile.cursor > 0 { m.profile.cursor-- } case key.Matches(msg, m.keys.Down): if m.profile.cursor < len(m.profile.names)-1 { m.profile.cursor++ } case key.Matches(msg, m.keys.Enter): return m.switchToSelectedProfile() case key.Matches(msg, m.keys.Back), key.Matches(msg, m.keys.Quit): m.profile.open = false } return m, nil } func (m *Model) switchToSelectedProfile() (tea.Model, tea.Cmd) { name := m.profile.names[m.profile.cursor] m.profile.open = false if name == m.a.Settings.Profile { return m, nil } if err := m.a.SwitchProfile(name); err != nil { return m, m.notify(toastErr, "switch profile: %v", err) } m.svc = m.a.Service() km := keys.Default() _ = km.Apply(m.a.File.Keys) // already validated once at startup; config hasn't changed if m.a.Settings.ReadOnly { km.DisableWrites() } m.keys = km m.tokenInfo = nil m.listSelect = "" return m, m.startAuth() } func (m *Model) confirmLogout() (tea.Model, tea.Cmd) { profile := m.a.Settings.Profile action := func() tea.Cmd { return func() tea.Msg { return logoutDoneMsg{err: m.a.Store.Erase(m.ctx)} } } m.confirm = &confirmSpec{ title: "Log out?", body: "Forgets the saved token for profile " + profile + ". This does not revoke it in Vault.", onConfirm: action, } return m, nil } func (m *Model) viewProfilePicker() string { lines := []string{m.styles.ModalTitle.Render("Switch profile"), ""} for i, n := range m.profile.names { marker := " " if n == m.a.Settings.Profile { marker = m.styles.SuccessText.Render(symCurrent) + " " } line := marker + n if i == m.profile.cursor { line = m.styles.RowSelected.Render(padRight(marker+n, 28)) } lines = append(lines, line) } lines = append(lines, "", m.styles.Help.Render("↑/↓ move • enter switch • esc cancel")) return m.styles.Modal.Render(lipgloss.JoinVertical(lipgloss.Left, lines...)) }