todos.go

 1package todos
 2
 3import (
 4	"slices"
 5	"strings"
 6
 7	"charm.land/lipgloss/v2"
 8	"github.com/charmbracelet/crush/internal/session"
 9	"github.com/charmbracelet/crush/internal/tui/styles"
10	"github.com/charmbracelet/x/ansi"
11)
12
13func sortTodos(todos []session.Todo) {
14	slices.SortStableFunc(todos, func(a, b session.Todo) int {
15		return statusOrder(a.Status) - statusOrder(b.Status)
16	})
17}
18
19func statusOrder(s session.TodoStatus) int {
20	switch s {
21	case session.TodoStatusCompleted:
22		return 0
23	case session.TodoStatusInProgress:
24		return 1
25	default:
26		return 2
27	}
28}
29
30func FormatTodosList(todos []session.Todo, inProgressIcon string, t *styles.Theme, width int) string {
31	if len(todos) == 0 {
32		return ""
33	}
34
35	sorted := make([]session.Todo, len(todos))
36	copy(sorted, todos)
37	sortTodos(sorted)
38
39	var lines []string
40	for _, todo := range sorted {
41		var prefix string
42		var textStyle lipgloss.Style
43
44		switch todo.Status {
45		case session.TodoStatusCompleted:
46			prefix = t.S().Base.Foreground(t.Green).Render(styles.TodoCompletedIcon) + " "
47			textStyle = t.S().Base.Foreground(t.FgBase)
48		case session.TodoStatusInProgress:
49			prefix = t.S().Base.Foreground(t.GreenDark).Render(inProgressIcon + " ")
50			textStyle = t.S().Base.Foreground(t.FgBase)
51		default:
52			prefix = t.S().Base.Foreground(t.FgMuted).Render(styles.TodoPendingIcon) + " "
53			textStyle = t.S().Base.Foreground(t.FgBase)
54		}
55
56		text := todo.Content
57		if todo.Status == session.TodoStatusInProgress && todo.ActiveForm != "" {
58			text = todo.ActiveForm
59		}
60		line := prefix + textStyle.Render(text)
61		line = ansi.Truncate(line, width, "…")
62
63		lines = append(lines, line)
64	}
65
66	return strings.Join(lines, "\n")
67}