charmbracelet / huh

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

fix: set YOffset + cursor based on first selected item #331

Open bashbunni opened 2 months ago

bashbunni commented 2 months ago

image tested against gum @ main-huh (basically just made it depend on this branch of huh) image

examples all work normally

you can test with Gum (after a lil go get -u github.com/charmbracelet/huh@refactor-select) then try seq 22 | go run . choose --selected=2,21 --limit=2 (or set limit to one or none for single select)

maaslalani commented 2 months ago

Nice! This is a great UX improvement!

meowgorithm commented 2 months ago

Totally, would almost call this a feature versus a fix. @bashbunni, can you add example Go code we can use to vet this?

bashbunni commented 2 months ago

@meowgorithm Just added some instructions. I mainly tested it with Gum.

  1. go to gum and replace the huh dep with this branch
  2. seq 22 | go run . choose --selected=2,21 --limit=2
meowgorithm commented 2 months ago

We should test with huh the library itself as well. It looks like there's still a bug when navigating through groups with tab. Example here:

package main

import (
    "fmt"
    "os"

    "github.com/charmbracelet/huh"
)

func main() {
    f := huh.NewForm(
        huh.NewGroup(
            huh.NewSelect[string]().
                Options(
                    huh.NewOption("A", "a"),
                    huh.NewOption("B", "b"),
                    huh.NewOption("C", "c"),
                    huh.NewOption("D", "d"),
                    huh.NewOption("E", "e"),
                    huh.NewOption("F", "f"),
                    huh.NewOption("G", "g"),
                    huh.NewOption("H", "h"),
                    huh.NewOption("I", "i"),
                    huh.NewOption("J", "j"),
                    huh.NewOption("K", "k").Selected(true),
                    huh.NewOption("L", "l"),
                    huh.NewOption("M", "m"),
                    huh.NewOption("N", "n"),
                    huh.NewOption("O", "o"),
                    huh.NewOption("P", "p"),
                ),
        ).WithHeight(8),
        huh.NewGroup(
            huh.NewMultiSelect[string]().
                Options(
                    huh.NewOption("A", "a"),
                    huh.NewOption("B", "b"),
                    huh.NewOption("C", "c"),
                    huh.NewOption("D", "d"),
                    huh.NewOption("E", "e"),
                    huh.NewOption("F", "f"),
                    huh.NewOption("G", "g"),
                    huh.NewOption("H", "h"),
                    huh.NewOption("I", "i"),
                    huh.NewOption("K", "k").Selected(true),
                    huh.NewOption("L", "l"),
                    huh.NewOption("M", "m"),
                    huh.NewOption("N", "n"),
                    huh.NewOption("O", "o").Selected(true),
                    huh.NewOption("P", "p"),
                ),
        ).WithHeight(8),
    )

    if err := f.Run(); err != nil {
        fmt.Fprintf(os.Stderr, "Oof: %v", err)
    }
}
bashbunni commented 2 months ago

Going to test more edge cases before merging this one :)

meowgorithm commented 2 months ago

Yeah, please do look at the example I posted above as well

maaslalani commented 2 months ago

You may also need to test with Dynamic Huh? That is, what happens when options are swapped out and some are selected.

bashbunni commented 2 months ago

The group-related issue is actually caused by a different bug I found and documented here. WithHeight wasn't setting the viewport height and was instead just cutting content. That's why the calculations in this branch weren't working properly. Will continue trying to break it though :)

bashbunni commented 2 months ago

Maybe I'm doing something wrong, but I can't seem to get pre-selected options to work in dynamic huh. You can try for yourself by checking out the dynamic-selection-example branch and running the dynamic/dynamic-selections example. https://github.com/charmbracelet/huh/blob/dynamic-selection-example/examples/dynamic/dynamic-selections/main.go

I created this example to test pre-selected elements in dynamic huh, but the cursor always starts at the top of the options anyway. Let me know if this is by design!