Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Focus not given to first field when redirected to a huh.Form from a tea.Model #421

Open
keyneston opened this issue Sep 20, 2024 · 7 comments
Assignees
Labels
bug Something isn't working input

Comments

@keyneston
Copy link

Describe the bug

When directed to a huh model from another bubbletea model, the initial input does not have focus. This is exacerbated by, but does not require, the use of a field validation. If a field validation is used it soft-locks the program. If a field validation is not used on the first field then one can simply tab and shift-tab back.

To Reproduce

  1. Create some kind of navigation model that directs to a huh model. (Alternatively run the reproduction code below)
  2. Put a validation on the first field of the huh model in order to soft lock it.
  3. Run the code, proceeding through the navigation model to the huh model.
  4. Try and type into the Input
  5. Try and tab/shift-tab off the Input.
package main

import (
	"fmt"

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

func main() {
	tea.NewProgram(NavModel{}).Run()
}

type NavModel struct{}

func (n NavModel) Init() tea.Cmd {
	return nil
}

func (n NavModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	if msg, ok := msg.(tea.KeyMsg); ok {
		if msg.String() == "ctrl+c" {
			return n, tea.Quit
		}
		if msg.String() == "enter" {
                        // Hardcoding 80,80 because I'm being lazy in this reproduction code and not saving them after we receive them. 
                        // Sending this windowSizeMsg is necessary to get it to properly render the NewModel at all.
			return NewModel(), func() tea.Msg { return tea.WindowSizeMsg{Height: 80, Width: 80} }
		}
	}

	return n, nil
}

func (n NavModel) View() string {
	return "Press enter to proceed."
}

type Model struct {
	form *huh.Form // huh.Form is just a tea.Model
}

func NewModel() Model {
	return Model{
		form: huh.NewForm(
			huh.NewGroup(
				huh.NewInput().
					Validate(func(in string) error {
						if in == "" {
							return fmt.Errorf("Class must be set.")
						}

						return nil
					}).
					Key("class").
					Title("Choose your class"),

				huh.NewSelect[int]().
					Key("level").
					Options(huh.NewOptions(1, 20, 9999)...).
					Title("Choose your level"),
			),
		),
	}
}

func (m Model) Init() tea.Cmd {
	return m.form.Init()
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	if msg, ok := msg.(tea.KeyMsg); ok {
		if msg.String() == "ctrl+c" {
			return m, tea.Quit
		}
	}

	form, cmd := m.form.Update(msg)
	if f, ok := form.(*huh.Form); ok {
		m.form = f
	}

	return m, cmd
}

func (m Model) View() string {
	if m.form.State == huh.StateCompleted {
		class := m.form.GetString("class")
		level := m.form.GetString("level")
		return fmt.Sprintf("You selected: %s, Lvl. %d", class, level)
	}
	return m.form.View()
}

Expected behavior

When the huh model is rendered the initial input field should have focus and you should be able to type into it.

Screenshots

What isn't visible is I'm trying to type into the Input field.

CleanShot 2024-09-20 at 12 17 25

Desktop:

  • Mac OS Sonoma 14.6.1
  • iTerm2 Build 3.5.4

go.mod

module github.com/keyneston/bubbletea-huh-test

go 1.22.4

require (
	github.com/atotto/clipboard v0.1.4 // indirect
	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
	github.com/catppuccin/go v0.2.0 // indirect
	github.com/charmbracelet/bubbles v0.20.0 // indirect
	github.com/charmbracelet/bubbletea v1.1.1 // indirect
	github.com/charmbracelet/huh v0.6.0 // indirect
	github.com/charmbracelet/lipgloss v0.13.0 // indirect
	github.com/charmbracelet/x/ansi v0.2.3 // indirect
	github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
	github.com/charmbracelet/x/term v0.2.0 // indirect
	github.com/dustin/go-humanize v1.0.1 // indirect
	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/mattn/go-localereader v0.0.1 // indirect
	github.com/mattn/go-runewidth v0.0.16 // indirect
	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
	github.com/muesli/cancelreader v0.2.2 // indirect
	github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect
	github.com/rivo/uniseg v0.4.7 // indirect
	golang.org/x/sync v0.8.0 // indirect
	golang.org/x/sys v0.25.0 // indirect
	golang.org/x/text v0.18.0 // indirect
)
@keyneston
Copy link
Author

I was able to work around this by calling:

	m.form.NextField()
	m.form.PrevField()

After initialising my struct.

@cloverLynn
Copy link

Seconding This issue, thanks @keyneston for a workaround 🖤

@marcusprice
Copy link

I think I might be running into this issue as well? Though I'm brand new to this library/charm.sh in general so I could also be skill issuing this too.

If I set an input as the first field in the form, it doesn't focus take any input. However if I have the first form element be huh.NewSelect it does focus and I can interact with it. I see @keyneston's fix, but I couldn't seem to get it to work for me? Am I just called the Next/PrevField's in the wrong place? Any help would be greatly appreciated.

package client

import (
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/huh"
)

type ServerLanding struct {
	server *string
	form   *huh.Form
}

func NewServerLanding(text string) *ServerLanding {
	server := "placeholder text"
	form := huh.NewForm(
		huh.NewGroup(
			huh.NewInput().
				Value(&server),
		),
	)

	return &ServerLanding{form: form, server: &server}
}

func (s ServerLanding) Init() tea.Cmd {
	var cmds []tea.Cmd
	cmds = append(
		cmds,
		s.form.Init(),
		s.form.NextField(),
		s.form.PrevField(),
	)

	return tea.Batch(cmds...)
}

func (s ServerLanding) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	// Process the form
	var cmds []tea.Cmd
	form, cmd := s.form.Update(msg)
	if f, ok := form.(*huh.Form); ok {
		s.form = f
		cmds = append(cmds, cmd)
	}

	return s, tea.Batch(cmds...)
}

func (s ServerLanding) View() string {
	return s.form.View()
}

@keyneston
Copy link
Author

Hey @marcusprice try changing it like:

func NewServerLanding(text string) *ServerLanding {
	server := "placeholder text"
	form := huh.NewForm(
		huh.NewGroup(
			huh.NewInput().
				Value(&server),
		),
	)

        form.NextField()
        form.PrevField()

	return &ServerLanding{form: form, server: &server}
}

This is instead of doing it in the Init function.

@marcusprice
Copy link

Thank you for the response, but it doesn't work for me :(

Was able to get an input/forms working in a non-nested model, but doesn't seem to work otherwise. Prob going to see if I can get away without using huh at least for now. I've been stuck trying to figure this out for days now

@caarlos0 caarlos0 added bug Something isn't working input labels Nov 26, 2024
@bashbunni
Copy link
Member

Hey @keyneston, in this specific instance, it looks like your problem is in the main function. You're running tea.NewProgram(NavModel{}).Run() where it should be tea.NewProgram(NewModel()).Run() as that function is where you initialize your form. I changed that one line and was able to type in the input field without issue.

Please let me know if that fix works on your end too :)

@bashbunni
Copy link
Member

@cloverLynn @marcusprice if you could provide me with a minimum reproducible version of your code, I would be happy to take a look :)

@bashbunni bashbunni self-assigned this Nov 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working input
Projects
None yet
Development

No branches or pull requests

5 participants