From b6eee0eb2d241b747c16e18ca4da4a1c145c3485 Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Sat, 3 May 2025 14:33:56 +0300 Subject: [PATCH] feat: add a way to view output --- main.go | 74 ++++++++++++++++++++++++++++++++++++++---------- styles/styles.go | 2 ++ types/keymaps.go | 34 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index e01f8b6..5eab76f 100644 --- a/main.go +++ b/main.go @@ -9,14 +9,17 @@ package main import ( + "bytes" "fmt" "os" "os/exec" "git.src.quest/~skye/tamma/styles" "git.src.quest/~skye/tamma/types" + "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "gopkg.in/yaml.v3" @@ -27,21 +30,29 @@ type state int const ( targetSelect state = iota actionSelect + viewOutput ) type model struct { - width int - height int - err string - state state - targetList list.Model - selectedTarget types.TargetItem - actionList list.Model - selectedAction types.ActionItem - actionListKeys *types.ActionListKeyMap + width int + height int + err string + out string + state state + targetList list.Model + selectedTarget types.TargetItem + actionList list.Model + selectedAction types.ActionItem + actionListKeys *types.ActionListKeyMap + outputViewport viewport.Model + outputViewportKeys *types.OutputViewportKeyMap + outputHelp help.Model } -type execFinishedMsg struct{ err error } +type execFinishedMsg struct { + err error + out bytes.Buffer +} func (m model) Init() tea.Cmd { return nil @@ -73,8 +84,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.err = err.Error() } else { c := exec.Command("sh", "-c", execString) + var out bytes.Buffer + c.Stdout = &out return m, tea.ExecProcess(c, func(err error) tea.Msg { - return execFinishedMsg{err} + return execFinishedMsg{err, out} }) } } @@ -88,6 +101,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.actionList.ResetSelected() m.state = targetSelect } + case viewOutput: + m.state = actionSelect } case "c": switch m.state { @@ -109,8 +124,15 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } } + case "o": + switch m.state { + case actionSelect: + m.state = viewOutput + } } case execFinishedMsg: + m.out = string(msg.out.Bytes()) + m.outputViewport.SetContent(m.out) if msg.err != nil { m.err = msg.err.Error() } @@ -125,6 +147,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd m.actionList, cmd = m.actionList.Update(msg) return m, cmd + case viewOutput: + var cmd tea.Cmd + m.outputViewport, cmd = m.outputViewport.Update(msg) + return m, cmd } return m, nil @@ -153,6 +179,13 @@ func (m model) View() string { } msg += styles.ActionExecString.Width(tlw).SetString(execString).String() } + case viewOutput: + m.outputViewport.Height = m.height - 20 + m.outputViewport.Width = m.width - 2 + msg += m.outputViewport.View() + msg += "\n\npager-like keys apply, plus the following:\n" + msg += m.outputHelp.View(m.outputViewportKeys) + msg += "\n" } if m.err != "" { @@ -229,22 +262,33 @@ func main() { return []key.Binding{ actionListKeys.Back, actionListKeys.CopyCommand, + actionListKeys.ViewOutput, } } actionList.AdditionalFullHelpKeys = func() []key.Binding { return []key.Binding{ actionListKeys.Back, actionListKeys.CopyCommand, + actionListKeys.ViewOutput, } } actionList.Styles.Title = styles.ListTitle actionList.Styles.PaginationStyle = styles.ListPaginaton + outputViewport := viewport.New(20, 20) + outputViewport.SetHorizontalStep(6) // 6 will be default in bubbles v2 + outputViewport.Style = styles.ViewportStyle + outputViewportKeys := types.NewOutputViewportKeyMap() + outputHelp := help.New() + p := tea.NewProgram(model{ - state: targetSelect, - targetList: targetList, - actionList: actionList, - actionListKeys: actionListKeys, + state: targetSelect, + targetList: targetList, + actionList: actionList, + actionListKeys: actionListKeys, + outputViewport: outputViewport, + outputViewportKeys: outputViewportKeys, + outputHelp: outputHelp, }, tea.WithAltScreen()) if _, err := p.Run(); err != nil { fmt.Printf("An error occured while running: %v\n", err) diff --git a/styles/styles.go b/styles/styles.go index aa7298a..ed73605 100644 --- a/styles/styles.go +++ b/styles/styles.go @@ -32,4 +32,6 @@ var ( ActionExecString = lipgloss.NewStyle(). AlignHorizontal(lipgloss.Center). Foreground(lipgloss.Color("145")) + + ViewportStyle = lipgloss.NewStyle().MarginLeft(2) ) diff --git a/types/keymaps.go b/types/keymaps.go index e0fe3aa..7dc680e 100644 --- a/types/keymaps.go +++ b/types/keymaps.go @@ -30,6 +30,7 @@ func NewTargetListKeyMap() *TargetListKeyMap { type ActionListKeyMap struct { Back key.Binding CopyCommand key.Binding + ViewOutput key.Binding } // NewActionListKeyMap returns a new ActionListKeyMap. @@ -43,5 +44,38 @@ func NewActionListKeyMap() *ActionListKeyMap { key.WithKeys("c"), key.WithHelp("c", "copy cmd"), ), + ViewOutput: key.NewBinding( + key.WithKeys("o"), + key.WithHelp("o", "view output"), + ), + } +} + +// OutputViewportKeyMap defines key bindings for the output viewport. +type OutputViewportKeyMap struct { + Back key.Binding +} + +// NewOutputViewportKeyMap returns a new OutputViewportKeyMap. +func NewOutputViewportKeyMap() *OutputViewportKeyMap { + return &OutputViewportKeyMap{ + Back: key.NewBinding( + key.WithKeys("q"), + key.WithHelp("q", "back"), + ), + } +} + +// ShortHelp returns keybindings to be shown in the mini help view. It's part +// of the key.Map interface. +func (k OutputViewportKeyMap) ShortHelp() []key.Binding { + return []key.Binding{k.Back} +} + +// FullHelp returns keybindings for the expanded help view. It's part of the +// key.Map interface. +func (k OutputViewportKeyMap) FullHelp() [][]key.Binding { + return [][]key.Binding{ + {k.Back}, } } -- 2.44.1