charmbracelet / huh

Build terminal forms and prompts 🤷🏻‍♀️
MIT License
4.72k stars 132 forks source link

Dynamic fields sometimes don't render when initially switching to a new group #419

Open chriskuehl opened 2 months ago

chriskuehl commented 2 months ago

Describe the bug When creating a multi-group form, dynamic fields don't always populate correctly when switching to a new group until you press a keyboard input, which triggers an update. This causes dynamic descriptions to be completely blank.

To Reproduce Tested using huh 0.6.0 as well as the current main branch (c9b2c9cdb7b6c76d636bfdf1d5609818a28779ed) with this reproduction script:

package main

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

func main() {
        path := "initial path"

        form := huh.NewForm(
                huh.NewGroup(
                        // Note: behavior is different depending on whether this is an Input or FilePicker; try changing this
                        // to huh.NewInput() as well.
                        huh.NewFilePicker().
                                Title("Select a file").
                                Value(&path),
                ),

                huh.NewGroup(
                        huh.NewNote().
                                DescriptionFunc(
                                        func() string {
                                                return "Selected path: " + path
                                        },
                                        &path,
                                ).
                                Title("Summary"),

                        huh.NewConfirm().
                                Title("Confirm?"),
                ),
        )

        if err := form.Run(); err != nil {
                panic(err)
        }
}

To reproduce:

  1. Create a main.go file with the above contents.
  2. go mod init example
  3. go get github.com/charmbracelet/huh@v0.6.0
  4. go run .
  5. You should now be on the first group with a file picker. Pick any file and press enter.
  6. You should now be on the second group with an empty summary note.
  7. Press any key (e.g. arrow key) and observe that the empty summary note suddenly re-renders with the expected description.

Expected behavior I expect the "summary" note in the second group to display the dynamic description as soon as the user navigates to that group, like this: Screenshot 2024-09-17 at 1 54 23 PM

Observed behavior The "summary" note is completely empty: Screenshot 2024-09-17 at 1 56 04 PM

Once I press any key on the keyboard, it re-renders and now looks like the first screenshot above, as expected.

Desktop (please complete the following information):

Potential patch?

I was able to resolve the issue by making this patch to trigger updating fields when switching to a new group:

diff --git a/group.go b/group.go
index d890751..bb2aae2 100644
--- a/group.go
+++ b/group.go
@@ -190,6 +190,8 @@ func PrevField() tea.Msg {
 func (g *Group) Init() tea.Cmd {
        var cmds []tea.Cmd

+       cmds = append(cmds, func() tea.Msg { return updateFieldMsg{} })
+
        if g.selector.Selected().Skip() {
                if g.selector.OnLast() {
                        cmds = append(cmds, g.prevField()...)

This appears to fix the issue for me and I don't notice any side effects, but it does break a test and I don't fully understand the architecture of this project so it may not be a correct fix.

Additional context

The behavior seems to vary depending on the type of input. If you replace NewFilePicker() with NewInput() in the minimal example, the second group now initially renders empty but then quickly re-renders with the dynamic description. This is different from the FilePicker which never re-renders until you press a key.

Screen recording showing the minimal reproduction with NewInput() so you can see the flash of missing dynamic fields: https://github.com/user-attachments/assets/d7ae294f-393c-4e89-9769-5207f51c92e0

zhammer commented 1 month ago

would love to see this change PR'd and merged!