feat(tui): add search via capture bar and absorb flow
Search uses existing parse grammar ?prefix — type `?query #tag` in capture bar to filter entities client-side. Substring match on body+title+description with AND tag filtering. Esc clears search. Absorb via m key on fluid entities — opens source picker showing all other entities, enter merges source into target. Uses existing store.Absorb() backend.
This commit is contained in:
+18
-7
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
type listModel struct {
|
||||
entities []*db.Entity
|
||||
filtered []*db.Entity
|
||||
cursor int
|
||||
offset int
|
||||
height int
|
||||
@@ -25,6 +26,7 @@ func newListModel() listModel {
|
||||
|
||||
func (l *listModel) setEntities(entities []*db.Entity) {
|
||||
l.entities = entities
|
||||
l.filtered = nil
|
||||
if l.cursor >= len(entities) {
|
||||
l.cursor = max(0, len(entities)-1)
|
||||
}
|
||||
@@ -35,11 +37,19 @@ func (l *listModel) setSize(width, height int) {
|
||||
l.height = height
|
||||
}
|
||||
|
||||
func (l listModel) displayEntities() []*db.Entity {
|
||||
if l.filtered != nil {
|
||||
return l.filtered
|
||||
}
|
||||
return l.entities
|
||||
}
|
||||
|
||||
func (l listModel) selected() *db.Entity {
|
||||
if len(l.entities) == 0 || l.cursor >= len(l.entities) {
|
||||
ents := l.displayEntities()
|
||||
if len(ents) == 0 || l.cursor >= len(ents) {
|
||||
return nil
|
||||
}
|
||||
return l.entities[l.cursor]
|
||||
return ents[l.cursor]
|
||||
}
|
||||
|
||||
func (l listModel) update(msg tea.KeyMsg) listModel {
|
||||
@@ -52,7 +62,7 @@ func (l listModel) update(msg tea.KeyMsg) listModel {
|
||||
}
|
||||
}
|
||||
case "down", "j":
|
||||
if l.cursor < len(l.entities)-1 {
|
||||
if l.cursor < len(l.displayEntities())-1 {
|
||||
l.cursor++
|
||||
visible := l.visibleCount()
|
||||
if l.cursor >= l.offset+visible {
|
||||
@@ -63,7 +73,7 @@ func (l listModel) update(msg tea.KeyMsg) listModel {
|
||||
l.cursor = 0
|
||||
l.offset = 0
|
||||
case "end", "G":
|
||||
l.cursor = max(0, len(l.entities)-1)
|
||||
l.cursor = max(0, len(l.displayEntities())-1)
|
||||
visible := l.visibleCount()
|
||||
if l.cursor >= visible {
|
||||
l.offset = l.cursor - visible + 1
|
||||
@@ -74,7 +84,7 @@ func (l listModel) update(msg tea.KeyMsg) listModel {
|
||||
l.offset = l.cursor
|
||||
}
|
||||
case "pgdown", "ctrl+d":
|
||||
l.cursor = min(len(l.entities)-1, l.cursor+l.visibleCount())
|
||||
l.cursor = min(len(l.displayEntities())-1, l.cursor+l.visibleCount())
|
||||
visible := l.visibleCount()
|
||||
if l.cursor >= l.offset+visible {
|
||||
l.offset = l.cursor - visible + 1
|
||||
@@ -84,11 +94,12 @@ func (l listModel) update(msg tea.KeyMsg) listModel {
|
||||
}
|
||||
|
||||
func (l listModel) view(width int) string {
|
||||
if len(l.entities) == 0 {
|
||||
ents := l.displayEntities()
|
||||
if len(ents) == 0 {
|
||||
return statusStyle.Render("no entities")
|
||||
}
|
||||
|
||||
groups := groupByDate(l.entities)
|
||||
groups := groupByDate(ents)
|
||||
|
||||
type displayLine struct {
|
||||
text string
|
||||
|
||||
Reference in New Issue
Block a user