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.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user