package views import ( "unicode" "github.com/charmbracelet/bubbles/list" "task/internal/project" ) // ProjectItem wraps a project for bubbles/list. type ProjectItem struct { Project project.Project Active bool } func (i ProjectItem) Title() string { if i.Active { return i.Project.Name + " *" } return i.Project.Name } func (i ProjectItem) Description() string { return i.Project.ID } func (i ProjectItem) FilterValue() string { return i.Project.Name } // NewProjectList builds a list of projects. func NewProjectList(projects []project.Project, activeID string, width, height int) list.Model { items := make([]list.Item, len(projects)) selected := 0 for i, p := range projects { items[i] = ProjectItem{Project: p, Active: p.ID == activeID} if p.ID == activeID { selected = i } } delegate := list.NewDefaultDelegate() delegate.ShowDescription = false l := list.New(items, delegate, width, height) l.Title = "Projects" l.SetShowStatusBar(false) l.SetShowHelp(false) l.SetFilteringEnabled(false) l.DisableQuitKeybindings() if len(items) > 0 { l.Select(selected) } return l } // JumpProjectByLetter cycles to the next project whose name starts with r. func JumpProjectByLetter(l *list.Model, r rune) bool { r = unicode.ToLower(r) items := l.Items() n := len(items) if n == 0 { return false } start := l.Index() for step := 1; step <= n; step++ { i := (start + step) % n pi, ok := items[i].(ProjectItem) if !ok { continue } name := []rune(pi.Project.Name) if len(name) == 0 { continue } if unicode.ToLower(name[0]) == r { l.Select(i) return true } } return false } // ProjectsToItems converts projects to list items. func ProjectsToItems(projects []project.Project, activeID string) []list.Item { items := make([]list.Item, len(projects)) for i, p := range projects { items[i] = ProjectItem{Project: p, Active: p.ID == activeID} } return items }