Files
nib-v1/internal/tui/statusbar.go
T
lerko 4e0ac8402f fix(tui): pin footer to bottom, style hint bar, auto-clear status
Content area now enforces full height so the context help bar stays
pinned to the terminal bottom. Hint keys rendered with bold highlight
color for scannability. Status messages (created, deleted, etc.)
auto-clear after 2 seconds, reverting to the entity count.
2026-05-20 11:01:13 -04:00

88 lines
2.5 KiB
Go

package tui
import (
"fmt"
"strings"
"github.com/charmbracelet/lipgloss"
)
type hint struct {
key string
desc string
}
func renderHints(hints []hint) string {
parts := make([]string, len(hints))
for i, h := range hints {
parts[i] = hintKeyStyle.Render(h.key) + " " + hintDescStyle.Render(h.desc)
}
return strings.Join(parts, " ")
}
func renderStatusBar(m model, width int) string {
left := countText(m)
if m.status != "" {
left = m.status
}
right := renderHints(contextHints(m))
leftRendered := statusStyle.Render(left)
gap := width - lipgloss.Width(leftRendered) - lipgloss.Width(right)
if gap < 0 {
gap = 0
}
pad := lipgloss.NewStyle().Width(gap).Render("")
return leftRendered + pad + right
}
func countText(m model) string {
var total int
if m.mode == modeCards {
total = len(m.cards.filtered)
} else {
total = len(m.list.displayEntities())
}
if m.filterTag != "" {
return fmt.Sprintf("%d entities #%s", total, m.filterTag)
}
return fmt.Sprintf("%d entities", total)
}
func contextHints(m model) []hint {
switch m.state {
case stateDetail:
switch m.detail.mode {
case detailRun:
return []hint{{"space", "toggle"}, {"j/k", "nav"}, {"r", "reset"}, {"esc", "save+exit"}}
case detailFill:
return []hint{{"tab", "next"}, {"⇧tab", "prev"}, {"enter", "copy"}, {"esc", "cancel"}}
default:
return []hint{{"p", "promote"}, {"D", "demote"}, {"c", "copy"}, {"e", "edit"}, {"r", "run"}, {"f", "fill"}, {"!", "pin"}, {"esc", "back"}}
}
case stateInput:
return nil
case stateTagFilter:
return []hint{{"j/k", "nav"}, {"enter", "select"}, {"esc", "cancel"}}
case stateConfirm:
return []hint{{"y", "confirm"}, {"n", "cancel"}}
case statePromote:
return []hint{{"j/k", "nav"}, {"enter", "select"}, {"esc", "cancel"}}
case stateAbsorb:
return []hint{{"j/k", "nav"}, {"enter", "absorb"}, {"esc", "cancel"}}
default:
if m.splitDetail {
if m.focus == focusDetail {
return []hint{{"h", "list"}, {"c", "copy"}, {"e", "edit"}, {"p", "promote"}, {"D", "demote"}, {"!", "pin"}, {"esc", "back"}}
}
return []hint{{"l", "detail"}, {"a", "add"}, {"d", "del"}, {"#", "filter"}, {"esc", "close"}, {"?", "help"}, {"q", "quit"}}
}
if m.mode == modeCards {
return []hint{{"1", "stream"}, {"2", "cards"}, {"s", "sort"}, {"tab", "intent"}, {"a", "add"}, {"?", "help"}, {"q", "quit"}}
}
return []hint{{"1", "stream"}, {"2", "cards"}, {"a", "add"}, {"?", "search"}, {"m", "absorb"}, {"d", "del"}, {"#", "filter"}, {"?", "help"}, {"q", "quit"}}
}
}