diff options
| author | Kujtim Hoxha <[email protected]> | 2025-03-23 14:56:32 +0100 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-03-23 15:00:12 +0100 |
| commit | 7844cacb257b14087bcb12764466ee0eeeb1921b (patch) | |
| tree | ccc9df7140e38e9fa55d190fc9e9f294e3987583 /internal/tui/components | |
| parent | 4b0ea68d7af9a6031a7ffda7ad66e0cb83315750 (diff) | |
| download | opencode-7844cacb257b14087bcb12764466ee0eeeb1921b.tar.gz opencode-7844cacb257b14087bcb12764466ee0eeeb1921b.zip | |
add help
Diffstat (limited to 'internal/tui/components')
| -rw-r--r-- | internal/tui/components/core/help.go | 119 | ||||
| -rw-r--r-- | internal/tui/components/core/status.go | 72 |
2 files changed, 191 insertions, 0 deletions
diff --git a/internal/tui/components/core/help.go b/internal/tui/components/core/help.go new file mode 100644 index 000000000..9402d9362 --- /dev/null +++ b/internal/tui/components/core/help.go @@ -0,0 +1,119 @@ +package core + +import ( + "strings" + + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/kujtimiihoxha/termai/internal/tui/styles" +) + +type HelpCmp interface { + tea.Model + SetBindings(bindings []key.Binding) + Height() int +} + +const ( + helpWidgetHeight = 12 +) + +type helpCmp struct { + width int + bindings []key.Binding +} + +func (m *helpCmp) Init() tea.Cmd { + return nil +} + +func (m *helpCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + } + return m, nil +} + +func (m *helpCmp) View() string { + helpKeyStyle := styles.Bold.Foreground(styles.Rosewater).Margin(0, 1, 0, 0) + helpDescStyle := styles.Regular.Foreground(styles.Flamingo) + // Compile list of bindings to render + bindings := removeDuplicateBindings(m.bindings) + // Enumerate through each group of bindings, populating a series of + // pairs of columns, one for keys, one for descriptions + var ( + pairs []string + width int + rows = helpWidgetHeight - 2 + ) + for i := 0; i < len(bindings); i += rows { + var ( + keys []string + descs []string + ) + for j := i; j < min(i+rows, len(bindings)); j++ { + keys = append(keys, helpKeyStyle.Render(bindings[j].Help().Key)) + descs = append(descs, helpDescStyle.Render(bindings[j].Help().Desc)) + } + // Render pair of columns; beyond the first pair, render a three space + // left margin, in order to visually separate the pairs. + var cols []string + if len(pairs) > 0 { + cols = []string{" "} + } + cols = append(cols, + strings.Join(keys, "\n"), + strings.Join(descs, "\n"), + ) + + pair := lipgloss.JoinHorizontal(lipgloss.Top, cols...) + // check whether it exceeds the maximum width avail (the width of the + // terminal, subtracting 2 for the borders). + width += lipgloss.Width(pair) + if width > m.width-2 { + break + } + pairs = append(pairs, pair) + } + + // Join pairs of columns and enclose in a border + content := lipgloss.JoinHorizontal(lipgloss.Top, pairs...) + return styles.DoubleBorder.Height(rows).PaddingLeft(1).Width(m.width - 2).Render(content) +} + +func removeDuplicateBindings(bindings []key.Binding) []key.Binding { + seen := make(map[string]struct{}) + result := make([]key.Binding, 0, len(bindings)) + + // Process bindings in reverse order + for i := len(bindings) - 1; i >= 0; i-- { + b := bindings[i] + k := strings.Join(b.Keys(), " ") + if _, ok := seen[k]; ok { + // duplicate, skip + continue + } + seen[k] = struct{}{} + // Add to the beginning of result to maintain original order + result = append([]key.Binding{b}, result...) + } + + return result +} + +func (m *helpCmp) SetBindings(bindings []key.Binding) { + m.bindings = bindings +} + +func (m helpCmp) Height() int { + return helpWidgetHeight +} + +func NewHelpCmp() HelpCmp { + return &helpCmp{ + width: 0, + bindings: make([]key.Binding, 0), + } +} diff --git a/internal/tui/components/core/status.go b/internal/tui/components/core/status.go index e69de29bb..8c9b3f60b 100644 --- a/internal/tui/components/core/status.go +++ b/internal/tui/components/core/status.go @@ -0,0 +1,72 @@ +package core + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/kujtimiihoxha/termai/internal/tui/styles" + "github.com/kujtimiihoxha/termai/internal/tui/util" + "github.com/kujtimiihoxha/termai/internal/version" +) + +type statusCmp struct { + err error + info string + width int +} + +func (m statusCmp) Init() tea.Cmd { + return nil +} + +func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + case util.ErrorMsg: + m.err = msg + case util.InfoMsg: + m.info = string(msg) + } + return m, nil +} + +var ( + versionWidget = styles.Padded.Background(styles.DarkGrey).Foreground(styles.Text).Render(version.Version) + helpWidget = styles.Padded.Background(styles.Grey).Foreground(styles.Text).Render("? help") +) + +func (m statusCmp) View() string { + status := styles.Padded.Background(styles.Grey).Foreground(styles.Text).Render("? help") + + if m.err != nil { + status += styles.Regular.Padding(0, 1). + Background(styles.Red). + Foreground(styles.Text). + Width(m.availableFooterMsgWidth()). + Render(m.err.Error()) + } else if m.info != "" { + status += styles.Padded. + Foreground(styles.Base). + Background(styles.Green). + Width(m.availableFooterMsgWidth()). + Render(m.info) + } else { + status += styles.Padded. + Foreground(styles.Base). + Background(styles.LightGrey). + Width(m.availableFooterMsgWidth()). + Render(m.info) + } + + status += versionWidget + return status +} + +func (m statusCmp) availableFooterMsgWidth() int { + // -2 to accommodate padding + return max(0, m.width-lipgloss.Width(helpWidget)-lipgloss.Width(versionWidget)) +} + +func NewStatusCmp() tea.Model { + return &statusCmp{} +} |
