4e0ac8402f
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.
88 lines
2.5 KiB
Go
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"}}
|
|
}
|
|
}
|