diff --git a/data/commands.json b/data/commands.json index a162a8f..57f20f8 100644 --- a/data/commands.json +++ b/data/commands.json @@ -8,6 +8,10 @@ "command": "df -h /path/to/mount", "desc": "Show statistics about a drive (remove path for all)" }, + { + "command": "free -h", + "desc": "Display memory usage in human-readable units" + }, { "command": "find /path/to/directory -type f | wc -l", "desc": "Show amount of files in directory and its sub-directories" @@ -23,6 +27,10 @@ { "command": "echo Hostname: $(hostname); echo Local_IP: $(hostname -i | awk '{print $1}'); echo Default_Gateway: $(ip route | awk '/default/ {print $3}')", "desc": "Print IP Address, Hostname, Default Route" + }, + { + "command": "tar -czf backup.tgz /path/to/dir", + "desc": "Create a quick compressed backup of a directory" } { "command": "find . -type f -iname 'myfile.txt'", @@ -38,6 +46,18 @@ "command": "python3 -m venv .venv && source .venv/bin/activate", "desc": "Initialise a (simple) .venv and activate it" }, + { + "command": "python -m http.server 8000", + "desc": "Serve the current directory over HTTP on port 8000" + }, + { + "command": "python -m pip install --upgrade pip", + "desc": "Upgrade pip to the latest version" + }, + { + "command": "python -m pip freeze > requirements.txt", + "desc": "Export current Python dependencies to requirements.txt" + }, { "command": "pip install jupyterlab ipykernel && python3 -m ipykernel install --user --name=project-name", "desc": "Install and initialise the kernel to use in jupyter" @@ -52,6 +72,18 @@ "command": "go mod init github.com/username/projectname", "desc": "Initialise a go project and create a mod file" }, + { + "command": "go test ./...", + "desc": "Run all Go tests recursively" + }, + { + "command": "go test -bench=. ./...", + "desc": "Run all benchmarks in the module" + }, + { + "command": "go list -m all", + "desc": "List all resolved module dependencies" + }, { "command": "go get github.com/some/package@latest", "desc": "Add dependencies" diff --git a/src/tui.go b/src/tui.go index 49288d2..d091efc 100644 --- a/src/tui.go +++ b/src/tui.go @@ -6,18 +6,17 @@ import ( //"sort" //"strings" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) - type item struct { - title string - description string + 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 } @@ -25,52 +24,59 @@ func (i item) FilterValue() string { return i.title } type view int type model struct { - list list.Model - commands CmdList - currentView view - currentKey string - selectedCmd string + list list.Model + commands CmdList + currentView view + currentKey string + selectedCmd string + categoryIndex int } + 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: + case tea.KeyMsg: - switch msg.String() { + switch msg.String() { - case "ctrl+c", "q": + case "ctrl+c", "q": + return m, tea.Quit + + case "enter": + selected, ok := m.list.SelectedItem().(item) + if ok { + if m.currentView == viewCategories { + m.categoryIndex = m.list.Index() + m.currentKey = selected.title + m.currentView = viewCommands + m.list.Title = m.currentKey + m.list.SetItems(cmdItemsToList(m.commands.Get(m.currentKey))) + m.list.Select(0) + } else if m.currentView == viewCommands { + m.selectedCmd = selected.title return m, tea.Quit - - case "enter": - selected, ok := m.list.SelectedItem().(item) - if ok { - if m.currentView == viewCategories { - m.currentKey = selected.title - m.currentView = viewCommands - m.list.Title = m.currentKey - m.list.SetItems(cmdItemsToList(m.commands.Get(m.currentKey))) - } else if m.currentView == viewCommands { - m.selectedCmd = selected.title - return m, tea.Quit - } - } - - case "b": - - if m.currentView == viewCommands { - m.currentView = viewCategories - m.list.Title = "Choose a list of commands" - m.list.SetItems(cmdListKeysToList(m.commands)) - } - + } } - case tea.WindowSizeMsg: - h, v := docStyle.GetFrameSize() - m.list.SetSize(msg.Width-h, msg.Height-v) + case "b": + + if m.currentView == viewCommands { + m.currentView = viewCategories + m.list.Title = "Choose a list of commands" + m.list.SetItems(cmdListKeysToList(m.commands)) + m.list.Select(m.categoryIndex) + } else if m.currentView == viewCategories { + return m, tea.Quit + } + + } + + case tea.WindowSizeMsg: + h, v := docStyle.GetFrameSize() + m.list.SetSize(msg.Width-h, msg.Height-v) } @@ -84,7 +90,9 @@ func (m model) View() string { return docStyle.Render(m.list.View()) } -var docStyle = lipgloss.NewStyle().Margin(1, 2) +var docStyle = lipgloss.NewStyle(). + Margin(1, 2). + Background(lipgloss.Color("235")) const ( viewCategories view = iota @@ -93,25 +101,12 @@ const ( func StartTui(commands CmdList) { - delegate := list.NewDefaultDelegate() - backKey := key.NewBinding( - key.WithKeys("b"), - key.WithHelp("b", "back"), - ) - delegate.ShortHelpFunc = func() []key.Binding{ - return []key.Binding{ - backKey, - } - } - delegate.FullHelpFunc = func() [][]key.Binding{ - return [][]key.Binding{ - {backKey}, - } - } + delegate := newStyledDelegate() items := cmdListKeysToList(commands) l := list.New(items, delegate, 0, 0) l.Title = "Choose a list of commands" + l.Styles = newListStyles() m := model{ list: l, @@ -132,15 +127,15 @@ func StartTui(commands CmdList) { } func cmdListKeysToList(cmds CmdList) []list.Item { - items := []list.Item{} - for _, group := range cmds { - listItem := item{ - title: group.Category, - description: "", - } - items = append(items, listItem) - } - return items + items := []list.Item{} + for _, group := range cmds { + listItem := item{ + title: group.Category, + description: "", + } + items = append(items, listItem) + } + return items } func cmdItemsToList(cmds []CmdItem) []list.Item { @@ -149,10 +144,85 @@ func cmdItemsToList(cmds []CmdItem) []list.Item { for _, command := range cmds { listItem := item{ - title: command.CommandName, + title: command.CommandName, description: command.CommandDescription, } items = append(items, listItem) } return items } + +func newStyledDelegate() list.DefaultDelegate { + delegate := list.NewDefaultDelegate() + + backKey := key.NewBinding( + key.WithKeys("b"), + key.WithHelp("b", "back/exit"), + ) + delegate.ShortHelpFunc = func() []key.Binding { + return []key.Binding{backKey} + } + delegate.FullHelpFunc = func() [][]key.Binding { + return [][]key.Binding{ + {backKey}, + } + } + + delegate.SetSpacing(0) + + styles := list.NewDefaultItemStyles() + styles.NormalTitle = styles.NormalTitle. + Foreground(lipgloss.Color("110")). + PaddingLeft(1) + styles.NormalDesc = styles.NormalDesc. + Foreground(lipgloss.Color("247")). + PaddingLeft(2) + styles.SelectedTitle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("0")). + Background(lipgloss.Color("187")). + BorderStyle(lipgloss.RoundedBorder()). + BorderForeground(lipgloss.Color("186")). + Padding(0, 1) + styles.SelectedDesc = styles.SelectedTitle. + Foreground(lipgloss.Color("232")). + Background(lipgloss.Color("230")). + Padding(0, 1) + styles.FilterMatch = styles.FilterMatch. + Foreground(lipgloss.Color("226")) + + delegate.Styles = styles + + return delegate +} + +func newListStyles() list.Styles { + styles := list.DefaultStyles() + + styles.Title = lipgloss.NewStyle(). + BorderStyle(lipgloss.ThickBorder()). + BorderForeground(lipgloss.Color("180")). + Background(lipgloss.Color("23")). + Foreground(lipgloss.Color("230")). + Padding(0, 2) + styles.TitleBar = styles.TitleBar.MarginBottom(1) + + styles.StatusBar = styles.StatusBar. + Background(lipgloss.Color("236")). + Foreground(lipgloss.Color("252")). + Padding(0, 1) + styles.StatusEmpty = styles.StatusEmpty.Foreground(lipgloss.Color("60")) + + styles.PaginationStyle = styles.PaginationStyle. + Foreground(lipgloss.Color("244")). + PaddingLeft(4) + styles.HelpStyle = styles.HelpStyle. + MarginLeft(1). + Foreground(lipgloss.Color("244")) + + styles.ActivePaginationDot = styles.ActivePaginationDot. + Foreground(lipgloss.Color("220")) + styles.InactivePaginationDot = styles.InactivePaginationDot. + Foreground(lipgloss.Color("238")) + + return styles +}