/* Copyright © 2024 Thomas von Dein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package ui import ( "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/tlinden/anydb/app" "github.com/tlinden/anydb/cfg" ) var ( appStyle = lipgloss.NewStyle().Padding(1, 2) titleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#FFFDF5")). Background(lipgloss.Color("#25A065")). Padding(0, 1) statusMessageStyle = lipgloss.NewStyle(). Foreground(lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#04B575"}). Render ) type Loader struct { items []list.Item conf *cfg.Config } func (loader *Loader) Update() error { entries, err := loader.conf.DB.List(&app.DbAttr{}, loader.conf.Fulltext) if err != nil { return err } loader.items = nil for _, entry := range entries { loader.items = append(loader.items, item{ title: entry.Key, description: entry.Preview, }) } return nil } type model struct { conf *cfg.Config loader *Loader quitting bool err error list list.Model keys *listKeyMap delegateKeys *delegateKeyMap } type listKeyMap struct { toggleSpinner key.Binding toggleTitleBar key.Binding toggleStatusBar key.Binding togglePagination key.Binding toggleHelpMenu key.Binding insertItem key.Binding } type item struct { title string description string } func (i item) Title() string { return i.title } func (i item) Description() string { return i.description } func (i item) FilterValue() string { return i.title } func newListKeyMap() *listKeyMap { return &listKeyMap{ insertItem: key.NewBinding( key.WithKeys("a"), key.WithHelp("a", "add item"), ), toggleSpinner: key.NewBinding( key.WithKeys("s"), key.WithHelp("s", "toggle spinner"), ), toggleTitleBar: key.NewBinding( key.WithKeys("T"), key.WithHelp("T", "toggle title"), ), toggleStatusBar: key.NewBinding( key.WithKeys("S"), key.WithHelp("S", "toggle status"), ), togglePagination: key.NewBinding( key.WithKeys("P"), key.WithHelp("P", "toggle pagination"), ), toggleHelpMenu: key.NewBinding( key.WithKeys("H"), key.WithHelp("H", "toggle help"), ), } } func NewModel(config *cfg.Config, entries app.DbEntries) model { var ( delegateKeys = newDelegateKeyMap() listKeys = newListKeyMap() loader = Loader{conf: config} ) // Setup list if err := loader.Update(); err != nil { panic(err) } delegate := newItemDelegate(delegateKeys, config) dbList := list.New(loader.items, delegate, 0, 0) dbList.Title = "DB Entries" dbList.Styles.Title = titleStyle dbList.AdditionalFullHelpKeys = func() []key.Binding { return []key.Binding{ listKeys.toggleSpinner, listKeys.insertItem, listKeys.toggleTitleBar, listKeys.toggleStatusBar, listKeys.togglePagination, listKeys.toggleHelpMenu, } } return model{ list: dbList, keys: listKeys, delegateKeys: delegateKeys, } } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.WindowSizeMsg: h, v := appStyle.GetFrameSize() m.list.SetSize(msg.Width-h, msg.Height-v) case tea.KeyMsg: // Don't match any of the keys below if we're actively filtering. if m.list.FilterState() == list.Filtering { break } switch { case key.Matches(msg, m.keys.toggleSpinner): cmd := m.list.ToggleSpinner() return m, cmd case key.Matches(msg, m.keys.toggleTitleBar): v := !m.list.ShowTitle() m.list.SetShowTitle(v) m.list.SetShowFilter(v) m.list.SetFilteringEnabled(v) return m, nil case key.Matches(msg, m.keys.toggleStatusBar): m.list.SetShowStatusBar(!m.list.ShowStatusBar()) return m, nil case key.Matches(msg, m.keys.togglePagination): m.list.SetShowPagination(!m.list.ShowPagination()) return m, nil case key.Matches(msg, m.keys.toggleHelpMenu): m.list.SetShowHelp(!m.list.ShowHelp()) return m, nil case key.Matches(msg, m.keys.insertItem): panic(1) } } // This will also call our delegate's update function. newListModel, cmd := m.list.Update(msg) m.list = newListModel cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } func (m model) View() string { return appStyle.Render(m.list.View()) }