package pages import ( "log/slog" "slices" "tasksquire/common" "tasksquire/components/picker" "tasksquire/taskwarrior" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) type ContextPickerPage struct { common *common.Common contexts taskwarrior.Contexts picker *picker.Picker } func NewContextPickerPage(common *common.Common) *ContextPickerPage { p := &ContextPickerPage{ common: common, contexts: common.TW.GetContexts(), } selected := common.TW.GetActiveContext().Name itemProvider := func() []list.Item { contexts := common.TW.GetContexts() options := make([]string, 0) for _, c := range contexts { if c.Name != "none" { options = append(options, c.Name) } } slices.Sort(options) options = append([]string{"(none)"}, options...) items := []list.Item{} for _, opt := range options { items = append(items, picker.NewItem(opt)) } return items } onSelect := func(item list.Item) tea.Cmd { return func() tea.Msg { return contextSelectedMsg{item: item} } } p.picker = picker.New(common, "Contexts", itemProvider, onSelect) // Set active context if selected == "" { selected = "(none)" } p.picker.SelectItemByFilterValue(selected) p.SetSize(common.Width(), common.Height()) return p } func (p *ContextPickerPage) SetSize(width, height int) { p.common.SetSize(width, height) // Set list size with some padding/limits to look like a picker listWidth := width - 4 if listWidth > 40 { listWidth = 40 } listHeight := height - 6 if listHeight > 20 { listHeight = 20 } p.picker.SetSize(listWidth, listHeight) } func (p *ContextPickerPage) Init() tea.Cmd { return p.picker.Init() } type contextSelectedMsg struct { item list.Item } func (p *ContextPickerPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.WindowSizeMsg: p.SetSize(msg.Width, msg.Height) case contextSelectedMsg: name := msg.item.FilterValue() // Use FilterValue (which is the name/text) if name == "(none)" { name = "" } ctx := p.common.TW.GetContext(name) model, err := p.common.PopPage() if err != nil { slog.Error("page stack empty") return nil, tea.Quit } return model, func() tea.Msg { return UpdateContextMsg(ctx) } case tea.KeyMsg: if !p.picker.IsFiltering() { switch { case key.Matches(msg, p.common.Keymap.Back): model, err := p.common.PopPage() if err != nil { slog.Error("page stack empty") return nil, tea.Quit } return model, BackCmd } } } _, cmd = p.picker.Update(msg) return p, cmd } func (p *ContextPickerPage) View() string { width := p.common.Width() - 4 if width > 40 { width = 40 } content := p.picker.View() styledContent := lipgloss.NewStyle().Width(width).Render(content) return lipgloss.Place( p.common.Width(), p.common.Height(), lipgloss.Center, lipgloss.Center, p.common.Styles.Base.Render(styledContent), ) } type UpdateContextMsg *taskwarrior.Context