package main import ( "fmt" "regexp" todo "github.com/1set/todotxt" "github.com/charmbracelet/lipgloss" catppuccin "github.com/catppuccin/go" ) var DATETIME_FORMAT = "02-01-2006 15:03" var DATE_FORMAT = "02-01-2006" var Catppuccin catppuccin.Flavor = catppuccin.Mocha type Style struct { Task TaskStyle Priority PriorityStyle Date DateStyle Check lipgloss.Style Context lipgloss.Style Project lipgloss.Style KeyValue lipgloss.Style } type TaskStyle struct { Todo lipgloss.Style Completed lipgloss.Style } type PriorityStyle struct { A lipgloss.Style B lipgloss.Style C lipgloss.Style } type DateStyle struct { done lipgloss.Style today lipgloss.Style overdue lipgloss.Style futur lipgloss.Style } func NewTextStyle() Style { taskStyle := TaskStyle{ Todo: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Text().Hex)), Completed: lipgloss.NewStyle().Strikethrough(true).Foreground(lipgloss.Color(Catppuccin.Overlay2().Hex)), } priorityStyle := PriorityStyle{ A: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Red().Hex)), B: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Peach().Hex)), C: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Green().Hex)), } dateBaseStyle := lipgloss.NewStyle() dateStyle := DateStyle{ done: dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Overlay0().Hex)), today: dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Crust().Hex)).Background(lipgloss.Color(Catppuccin.Blue().Hex)), overdue: dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Crust().Hex)).Background(lipgloss.Color(Catppuccin.Red().Hex)), futur: dateBaseStyle.Foreground(lipgloss.Color(Catppuccin.Blue().Hex)), } style := Style{ Priority: priorityStyle, Task: taskStyle, Date: dateStyle, Check: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Blue().Hex)), Context: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Blue().Hex)), Project: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Mauve().Hex)), KeyValue: lipgloss.NewStyle().Foreground(lipgloss.Color(Catppuccin.Flamingo().Hex)), } return style } func (s Style) getTaskStyle(task todo.Task, checked bool) string { todoText := task.Original checkedRegex := regexp.MustCompile(`^x (\d{4}-\d{2}-\d{2} )? ?`) priorityRegex := regexp.MustCompile(`^\([A-Z]\) `) contextRegex := regexp.MustCompile(`@\w+`) projectRegex := regexp.MustCompile(`\+\w+`) dueDateRegex := regexp.MustCompile(`due:\d{4}-\d{2}-\d{2}`) keyValueRegex := regexp.MustCompile(`\w+:.+`) todoText = checkedRegex.ReplaceAllString(todoText, "") todoText = priorityRegex.ReplaceAllString(todoText, "") todoText = dueDateRegex.ReplaceAllString(todoText, "") checkString := " " if checked { checkString = "✓" } switch task.Completed { case true: dateString := s.Date.done.Render(task.CompletedDate.Local().Format(DATETIME_FORMAT)) return fmt.Sprintf("%s %s %s %s", s.Check.Render(checkString), s.getPriorityStyle(task.Priority), s.Task.Completed.Render(todoText), dateString) default: todoText = contextRegex.ReplaceAllStringFunc(todoText, func(match string) string { return s.Context.Render(match) }) todoText = projectRegex.ReplaceAllStringFunc(todoText, func(match string) string { return s.Project.Render(match) }) todoText = keyValueRegex.ReplaceAllStringFunc(todoText, func(match string) string { return s.KeyValue.Render(match) }) var dueDateString string if task.HasDueDate() { formatedDueDate := task.DueDate.Format(DATE_FORMAT) switch { case task.IsDueToday(): dueDateString = s.Date.today.Render(formatedDueDate) case task.IsOverdue(): dueDateString = s.Date.overdue.Render(formatedDueDate) default: dueDateString = s.Date.futur.Render(formatedDueDate) } } return fmt.Sprintf("%s %s %s %s", s.Check.Render(checkString), s.getPriorityStyle(task.Priority), s.Task.Todo.Render(todoText), dueDateString) } } func (s Style) getPriorityStyle(priority string) string { switch priority { case "": return " " case "A": return s.Priority.A.Render(priority) case "B": return s.Priority.B.Render(priority) case "C": return s.Priority.C.Render(priority) default: return priority } }