package main import ( "flag" "fmt" "io/ioutil" "log" "os" tea "github.com/charmbracelet/bubbletea" viper "github.com/spf13/viper" ) const help = `go-sh-manymanuals: ??? ??? Options: -h output help ` var portFlag int var helpFlag bool type Configuration struct { ManualDir string } func handleCliFlags() { flag.BoolVar(&helpFlag, "h", false, "output help") flag.Parse() } type model struct { manuals []string cursor int } func initialModel() model { folder := viper.Get("ShareManualDir").(string) manuals, err := gatherManuals(folder) if err != nil { log.Fatalf("unable to read manuals: %s", err) os.Exit(1) } return model{ manuals: manuals, } } func gatherManuals(directory string) ([]string, error) { fileInfos, err := ioutil.ReadDir(fmt.Sprintf("./%s", directory)) if err != nil { return nil, err } var filenames []string for _, fileInfo := range fileInfos { filename := fileInfo.Name() if fileInfo.IsDir() { filename = fmt.Sprintf("📁 %s", fileInfo.Name()) } filenames = append(filenames, filename) } return filenames, nil } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": return m, tea.Quit case "up", "k": if m.cursor > 0 { m.cursor-- } case "down", "j": if m.cursor < len(m.manuals)-1 { m.cursor++ } } } return m, nil } func (m model) View() string { ui := "File listing for current working directory\n\n" for i, choice := range m.manuals { cursor := " " if m.cursor == i { cursor = ">" } ui += fmt.Sprintf("%s %s\n", cursor, choice) } ui += "\nPress q to quit.\n" ui += "\nPress forward or backward to navigate in and out of directories.\n" return ui } func main() { handleCliFlags() if helpFlag { fmt.Printf(help) os.Exit(0) } viper.SetConfigFile("config.json") viper.ReadInConfig() p := tea.NewProgram(initialModel(), tea.WithAltScreen()) if err := p.Start(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) } }