summaryrefslogtreecommitdiffhomepage
path: root/internal/tui/components
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-03-23 14:56:32 +0100
committerKujtim Hoxha <[email protected]>2025-03-23 15:00:12 +0100
commit7844cacb257b14087bcb12764466ee0eeeb1921b (patch)
treeccc9df7140e38e9fa55d190fc9e9f294e3987583 /internal/tui/components
parent4b0ea68d7af9a6031a7ffda7ad66e0cb83315750 (diff)
downloadopencode-7844cacb257b14087bcb12764466ee0eeeb1921b.tar.gz
opencode-7844cacb257b14087bcb12764466ee0eeeb1921b.zip
add help
Diffstat (limited to 'internal/tui/components')
-rw-r--r--internal/tui/components/core/help.go119
-rw-r--r--internal/tui/components/core/status.go72
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{}
+}