package main import ( "fmt" "os" "sort" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" gloss "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/list" "golang.org/x/term" ) var width, height, _ = term.GetSize(int(os.Stdout.Fd())) type menu struct { order []string current int keys keyMap help help.Model inputStyle gloss.Style spinner spinner.Model quitting bool } const ( softwareInstructionsFile = "/Users/marley/hackin/install.fairie/home/.chezmoidata.yaml" softwareGroup = "_Full-Desktop" ) func initialModel() menu { s := spinner.New() s.Spinner = spinner.MiniDot s.Style = gloss.NewStyle().Foreground(gloss.Color("3")) return menu{ current: 0, keys: keys, help: help.New(), spinner: s, quitting: false, } } type keyMap struct { Quit key.Binding } var keys = keyMap{ Quit: key.NewBinding( key.WithKeys("q", "esc", "ctrl+c"), key.WithHelp("q", "quit"), ), } func (k keyMap) ShortHelp() []key.Binding { return []key.Binding{k.Quit} } func (k keyMap) FullHelp() [][]key.Binding { keys := k.ShortHelp() return [][]key.Binding{ keys, } } type softwareListMsg []string type errMsg struct{ err error } func (e errMsg) Error() string { return e.err.Error() } func (m menu) Init() tea.Cmd { return tea.Batch(getSoftwareList(softwareInstructionsFile), m.spinner.Tick) } func (m menu) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd var cmds []tea.Cmd switch msg := msg.(type) { case softwareListMsg: m.order = msg return m, nil case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": m.quitting = true return m, tea.Quit } case spinner.TickMsg: m.spinner, cmd = m.spinner.Update(msg) cmds = append(cmds, cmd) } return m, tea.Batch(cmds...) } func (m menu) View() string { borderStyle := gloss.NewStyle(). BorderStyle(gloss.RoundedBorder()). BorderForeground(gloss.Color("5")). Padding(0, 1) mainStyle := borderStyle. Width(int(float64(width) * 0.65)) sidebarStyle := borderStyle. Width(int(float64(width) * 0.3)) mainContent := "" softwareListEnumerator := func(l list.Items, i int) string { if m.current == i { return m.spinner.View() } else if m.current > i { return "" } return "" } software := list.New().Enumerator(softwareListEnumerator) for _, item := range m.order { software.Item(item) } sidebarContent := software.String() main := mainStyle.Render(mainContent) sidebar := sidebarStyle.Render(sidebarContent) content := gloss.JoinHorizontal(gloss.Top, main, sidebar) helpView := m.help.View(m.keys) last := "" if m.quitting { last = "\n" } page := gloss.JoinVertical(gloss.Left, content, helpView, last) return gloss.PlaceHorizontal(width, gloss.Center, page) } func main() { p := tea.NewProgram(initialModel()) if _, err := p.Run(); err != nil { fmt.Printf("There's been an error: %v", err) os.Exit(1) } } func sortMapKeys(m SoftwarePackages) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) return keys }