feat(tui): stumble mode — resurface stale entries card by card

Card-by-card walkthrough of entries untouched for 30+ days.
Prevents write-mostly decay by bringing old entries back to attention.

- S from list triggers stumble, loads entries where modified_at < 30d
- Single-card view with markdown body, glyph, tags, age indicator
- Actions: n skip, d dismiss, ! pin, p promote, m absorb, esc exit
- Progress indicator: stumble [3/12]
- After promote/absorb from stumble, returns to deck (not list)
- "All caught up" screen when deck exhausted
- DB: add ModifiedBefore to ListParams, modified_at sort column
This commit is contained in:
2026-05-20 16:40:40 -04:00
parent 388ae88d4a
commit a567b2ce73
8 changed files with 311 additions and 3 deletions
+7
View File
@@ -71,6 +71,7 @@ type ListParams struct {
From *string
To *string
Since *time.Time
ModifiedBefore *time.Time
CardsOnly bool
IncludeDeleted bool
CardTypeFilter *CardType
@@ -216,6 +217,10 @@ func listWhere(params ListParams) (string, []any) {
where = append(where, "e.card_type = ?")
args = append(args, string(*params.CardTypeFilter))
}
if params.ModifiedBefore != nil {
where = append(where, "e.modified_at < ?")
args = append(args, params.ModifiedBefore.Format(time.RFC3339))
}
clause := ""
if len(where) > 0 {
@@ -239,6 +244,8 @@ func (s *Store) List(params ListParams) ([]*Entity, error) {
switch params.Sort {
case "use_count":
orderCol = "e.use_count"
case "modified_at":
orderCol = "e.modified_at"
case "created_at", "":
orderCol = "e.created_at"
default: