Skip to content

Something broke since v1.2.0, I don't know what happened, can anyone help? #1520

@lonnywong

Description

@lonnywong

Describe the bug
What I return from the View function does not display. It works with v1.1.2, but not v1.2.0.

Setup
Should not be related to the environment:

  • OS: macOS
  • Shell: bash
  • Terminal Emulator: iTerm2
  • Terminal Multiplexer: No

To Reproduce
Steps to reproduce the behavior:

  1. Test the code below.
  2. Check the output.

Source Code

package main

import (
	"fmt"
	"os"
	"strings"
	"time"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
)

const (
	redColor     = lipgloss.Color("1")
	greenColor   = lipgloss.Color("2")
	yellowColor  = lipgloss.Color("3")
	blueColor    = lipgloss.Color("4")
	magentaColor = lipgloss.Color("5")
	cyanColor    = lipgloss.Color("6")
)

type inputValidator struct {
	validate func(string) error
}

type passwordModel struct {
	promptLabel   string
	helpMessage   string
	passwordInput string
	validator     *inputValidator
	cursorVisible bool
	done          bool
	quit          bool
	err           error
}

type tickMsg time.Time

func tickEvery(d time.Duration) tea.Cmd {
	return tea.Tick(d, func(t time.Time) tea.Msg {
		return tickMsg(t)
	})
}

func (m *passwordModel) Init() tea.Cmd {
	return tickEvery(500 * time.Millisecond)
}

func (m *passwordModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyCtrlC:
			m.quit = true
			return m, tea.Quit
		case tea.KeyCtrlW:
			m.passwordInput = ""
			return m, nil
		case tea.KeyEnter:
			err := m.validator.validate(m.passwordInput)
			if err != nil {
				m.err = err
				return m, nil
			}
			m.done = true
			return m, tea.Quit
		case tea.KeyBackspace:
			if len(m.passwordInput) > 0 {
				m.passwordInput = m.passwordInput[:len(m.passwordInput)-1]
			}
		case tea.KeyRunes, tea.KeySpace:
			if len(msg.Runes) > 0 && msg.Runes[0] != 0 {
				m.passwordInput += string(msg.Runes)
			}
			m.err = nil
		}
	case error:
		m.err = msg
		return m, nil
	case tickMsg:
		m.cursorVisible = !m.cursorVisible
		return m, tickEvery(500 * time.Millisecond)
	}
	return m, nil
}

func (m *passwordModel) View() string {
	if m.done {
		return fmt.Sprintf("%s%s%s\r\n\r\n", lipgloss.NewStyle().Foreground(greenColor).Render(m.promptLabel),
			lipgloss.NewStyle().Faint(true).Render(": "), strings.Repeat("*", len(m.passwordInput)))
	}

	var builder strings.Builder
	builder.WriteString(lipgloss.NewStyle().Foreground(cyanColor).Render(m.promptLabel))
	builder.WriteString(": ")
	for i := 0; i < len(m.passwordInput); i++ {
		builder.WriteByte('*')
	}
	if !m.quit && m.cursorVisible {
		builder.WriteRune('█')
	} else {
		builder.WriteRune(' ')
	}
	builder.WriteString("\r\n")
	if m.err != nil {
		builder.WriteString(lipgloss.NewStyle().Foreground(redColor).Render(m.err.Error()))
	} else if m.helpMessage != "" {
		builder.WriteString(lipgloss.NewStyle().Faint(true).Render(m.helpMessage))
	}
	return builder.String()
}

func promptPassword(promptLabel, helpMessage string, validator *inputValidator) string {
	m, err := tea.NewProgram(&passwordModel{
		promptLabel: promptLabel,
		helpMessage: helpMessage,
		validator:   validator,
	}).Run()

	if model, ok := m.(*passwordModel); err == nil && ok {
		if model.quit {
			os.Exit(0)
		}
		return model.passwordInput
	}
	fmt.Printf("input error: %v\n", err)
	return ""
}

func main() {
	secret := promptPassword("Password or secret to be encoded", "",
		&inputValidator{func(secret string) error {
			if secret == "" {
				return fmt.Errorf("empty password or secret")
			}
			return nil
		}})
	fmt.Printf("secret: %s\n", secret)
}

Expected behavior
Should see the output Password or secret to be encoded: █

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions