all repos — todoxt @ main

TUI for todo.txt

style.go (view raw)

  1package main
  2
  3import (
  4	"fmt"
  5	"regexp"
  6
  7	todo "github.com/1set/todotxt"
  8	"github.com/charmbracelet/lipgloss"
  9
 10	catppuccin "github.com/catppuccin/go"
 11)
 12
 13var DATETIME_FORMAT = "02-01-2006 15:03"
 14var DATE_FORMAT = "02-01-2006"
 15
 16var Catppuccin catppuccin.Flavor = catppuccin.Mocha
 17
 18type Style struct {
 19	Task     TaskStyle
 20	Priority PriorityStyle
 21	Date     DateStyle
 22	Check    lipgloss.Style
 23	Context  lipgloss.Style
 24	Project  lipgloss.Style
 25	KeyValue lipgloss.Style
 26}
 27
 28type TaskStyle struct {
 29	Todo      lipgloss.Style
 30	Completed lipgloss.Style
 31	Futur     lipgloss.Style
 32}
 33
 34type PriorityStyle struct {
 35	A lipgloss.Style
 36	B lipgloss.Style
 37	C lipgloss.Style
 38}
 39
 40type DateStyle struct {
 41	done    lipgloss.Style
 42	today   lipgloss.Style
 43	overdue lipgloss.Style
 44	futur   lipgloss.Style
 45}
 46
 47func NewTextStyle() Style {
 48
 49	taskStyle := TaskStyle{
 50		Todo:      lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Text().Hex)),
 51		Completed: lipgloss.NewStyle().Strikethrough(true).Foreground(lipgloss.Color(Catppuccin.Overlay2().Hex)),
 52		Futur:     lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Overlay1().Hex)),
 53	}
 54
 55	priorityStyle := PriorityStyle{
 56		A: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Red().Hex)),
 57		B: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Peach().Hex)),
 58		C: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Green().Hex)),
 59	}
 60
 61	dateBaseStyle := lipgloss.NewStyle()
 62	dateStyle := DateStyle{
 63		done:    dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Overlay0().Hex)),
 64		today:   dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Crust().Hex)).Background(lipgloss.Color(Catppuccin.Blue().Hex)),
 65		overdue: dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Crust().Hex)).Background(lipgloss.Color(Catppuccin.Red().Hex)),
 66		futur:   dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Blue().Hex)),
 67	}
 68
 69	style := Style{
 70		Priority: priorityStyle,
 71		Task:     taskStyle,
 72		Date:     dateStyle,
 73		Check:    lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Blue().Hex)),
 74		Context:  lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Blue().Hex)),
 75		Project:  lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Mauve().Hex)),
 76		KeyValue: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Flamingo().Hex)),
 77	}
 78	return style
 79}
 80
 81func (s Style) getTaskStyle(task todo.Task, checked bool) string {
 82
 83	todoText := task.Original
 84
 85	checkedRegex := regexp.MustCompile(`^x (\d{4}-\d{2}-\d{2} )? ?`)
 86	priorityRegex := regexp.MustCompile(`^\([A-Z]\) `)
 87	contextRegex := regexp.MustCompile(`@\w+`)
 88	projectRegex := regexp.MustCompile(`\+\w+`)
 89	dueDateRegex := regexp.MustCompile(`due:\d{4}-\d{2}-\d{2}`)
 90	keyValueRegex := regexp.MustCompile(`\w+:.+`)
 91
 92	todoText = checkedRegex.ReplaceAllString(todoText, "")
 93	todoText = priorityRegex.ReplaceAllString(todoText, "")
 94	todoText = dueDateRegex.ReplaceAllString(todoText, "")
 95
 96	checkString := " "
 97	if checked {
 98		checkString = "✓"
 99	}
100
101	switch task.Completed {
102	case true:
103		dateString := s.Date.done.Render(task.CompletedDate.Local().Format(DATETIME_FORMAT))
104		return fmt.Sprintf("%s %s %s %s", s.Check.Render(checkString), s.getPriorityStyle(task.Priority), s.Task.Completed.Render(todoText), dateString)
105	default:
106		todoText = contextRegex.ReplaceAllStringFunc(todoText, func(match string) string {
107			return s.Context.Render(match)
108		})
109		todoText = projectRegex.ReplaceAllStringFunc(todoText, func(match string) string {
110			return s.Project.Render(match)
111		})
112		todoText = keyValueRegex.ReplaceAllStringFunc(todoText, func(match string) string {
113			return s.KeyValue.Render(match)
114		})
115		var dueDateString string
116		if task.HasDueDate() {
117			formatedDueDate := task.DueDate.Format(DATE_FORMAT)
118			switch {
119			case task.IsDueToday():
120				dueDateString = s.Date.today.Render(formatedDueDate)
121			case task.IsOverdue():
122				dueDateString = s.Date.overdue.Render(formatedDueDate)
123			default:
124				dueDateString = s.Date.futur.Render(formatedDueDate)
125			}
126		}
127
128		todoTextRender := s.Task.Todo
129		if task.AdditionalTags["freq"] != "" && !task.IsOverdue() && !task.IsDueToday() {
130			todoTextRender = s.Task.Futur
131		}
132
133		return fmt.Sprintf("%s %s %s %s", s.Check.Render(checkString), s.getPriorityStyle(task.Priority), todoTextRender.Render(todoText), dueDateString)
134	}
135}
136
137func (s Style) getPriorityStyle(priority string) string {
138	switch priority {
139	case "":
140		return " "
141	case "A":
142		return s.Priority.A.Render(priority)
143	case "B":
144		return s.Priority.B.Render(priority)
145	case "C":
146		return s.Priority.C.Render(priority)
147	default:
148		return priority
149	}
150}