Files
lerko c2ea63dd16 feat(tui): add status bar, help overlay, tag filter, and entity actions
Status bar with entity count and context-sensitive key hints. Help
overlay via ? key. Tag filter via # with cursor-navigable tag list.
Todo toggle (x), pin (!), promote (p), demote (D), copy (c), edit (e)
via $EDITOR. Delete confirmation with 3s timeout. Date-grouped list
with completed todo and pinned indicators. Esc clears active tag filter.

Adds CompletedAt/ClearCompleted to EntityUpdate for todo toggling.
2026-05-17 20:33:34 -04:00

85 lines
1.4 KiB
Go

package tui
import (
"fmt"
"strings"
"github.com/lerko/nib/internal/db"
)
type filterModel struct {
tags []db.TagCount
cursor int
height int
}
func newFilterModel() filterModel {
return filterModel{}
}
func (f *filterModel) setTags(tags []db.TagCount) {
f.tags = tags
f.cursor = 0
}
func (f *filterModel) setHeight(h int) {
f.height = h
}
func (f filterModel) selectedTag() string {
if len(f.tags) == 0 || f.cursor >= len(f.tags) {
return ""
}
return f.tags[f.cursor].Tag
}
func (f filterModel) update(key string) filterModel {
switch key {
case "up", "k":
if f.cursor > 0 {
f.cursor--
}
case "down", "j":
if f.cursor < len(f.tags)-1 {
f.cursor++
}
}
return f
}
func (f filterModel) view(width int) string {
if len(f.tags) == 0 {
return statusStyle.Render("no tags")
}
var b strings.Builder
b.WriteString(titleStyle.Render("filter by tag"))
b.WriteString("\n\n")
visible := f.height - 4
if visible <= 0 {
visible = 10
}
offset := 0
if f.cursor >= visible {
offset = f.cursor - visible + 1
}
end := min(offset+visible, len(f.tags))
for i := offset; i < end; i++ {
tc := f.tags[i]
tag := fmt.Sprintf("#%-20s %d", tc.Tag, tc.Count)
if i == f.cursor {
b.WriteString(selectedItemStyle.Render(" " + tagStyle.Render(tag)))
} else {
b.WriteString(listItemStyle.Render(tagStyle.Render(tag)))
}
if i < end-1 {
b.WriteString("\n")
}
}
return b.String()
}