322 lines
9.7 KiB
Go
322 lines
9.7 KiB
Go
package common
|
|
|
|
import (
|
|
"log/slog"
|
|
"strconv"
|
|
"strings"
|
|
|
|
// "github.com/charmbracelet/bubbles/table"
|
|
|
|
"github.com/charmbracelet/huh"
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
"tasksquire/taskwarrior"
|
|
)
|
|
|
|
type TableStyle struct {
|
|
Header lipgloss.Style
|
|
Cell lipgloss.Style
|
|
Selected lipgloss.Style
|
|
}
|
|
|
|
type Styles struct {
|
|
Base lipgloss.Style
|
|
|
|
Form *huh.Theme
|
|
TableStyle TableStyle
|
|
|
|
ColumnFocused lipgloss.Style
|
|
ColumnBlurred lipgloss.Style
|
|
ColumnInsert lipgloss.Style
|
|
|
|
Colors map[string]lipgloss.Style
|
|
|
|
// TODO: make color config completely dynamic to account for keyword., project., tag. and uda. colors
|
|
Active lipgloss.Style
|
|
Alternate lipgloss.Style
|
|
Blocked lipgloss.Style
|
|
Blocking lipgloss.Style
|
|
BurndownDone lipgloss.Style
|
|
BurndownPending lipgloss.Style
|
|
BurndownStarted lipgloss.Style
|
|
CalendarDue lipgloss.Style
|
|
CalendarDueToday lipgloss.Style
|
|
CalendarHoliday lipgloss.Style
|
|
CalendarOverdue lipgloss.Style
|
|
CalendarScheduled lipgloss.Style
|
|
CalendarToday lipgloss.Style
|
|
CalendarWeekend lipgloss.Style
|
|
CalendarWeeknumber lipgloss.Style
|
|
Completed lipgloss.Style
|
|
Debug lipgloss.Style
|
|
Deleted lipgloss.Style
|
|
Due lipgloss.Style
|
|
DueToday lipgloss.Style
|
|
Error lipgloss.Style
|
|
Footnote lipgloss.Style
|
|
Header lipgloss.Style
|
|
HistoryAdd lipgloss.Style
|
|
HistoryDelete lipgloss.Style
|
|
HistoryDone lipgloss.Style
|
|
Label lipgloss.Style
|
|
LabelSort lipgloss.Style
|
|
Overdue lipgloss.Style
|
|
ProjectNone lipgloss.Style
|
|
Recurring lipgloss.Style
|
|
Scheduled lipgloss.Style
|
|
SummaryBackground lipgloss.Style
|
|
SummaryBar lipgloss.Style
|
|
SyncAdded lipgloss.Style
|
|
SyncChanged lipgloss.Style
|
|
SyncRejected lipgloss.Style
|
|
TagNext lipgloss.Style
|
|
TagNone lipgloss.Style
|
|
Tagged lipgloss.Style
|
|
UdaPriorityH lipgloss.Style
|
|
UdaPriorityL lipgloss.Style
|
|
UdaPriorityM lipgloss.Style
|
|
UndoAfter lipgloss.Style
|
|
UndoBefore lipgloss.Style
|
|
Until lipgloss.Style
|
|
Warning lipgloss.Style
|
|
}
|
|
|
|
func NewStyles(config *taskwarrior.TWConfig) *Styles {
|
|
styles := parseColors(config.GetConfig())
|
|
styles.Base = lipgloss.NewStyle()
|
|
|
|
styles.TableStyle = TableStyle{
|
|
// Header: lipgloss.NewStyle().Bold(true).Padding(0, 1).BorderBottom(true),
|
|
Cell: lipgloss.NewStyle().Padding(0, 1, 0, 0),
|
|
Header: lipgloss.NewStyle().Bold(true).Padding(0, 1, 0, 0).Underline(true),
|
|
Selected: lipgloss.NewStyle().Bold(true).Reverse(true),
|
|
}
|
|
|
|
formTheme := huh.ThemeBase()
|
|
formTheme.Focused.Title = formTheme.Focused.Title.Bold(true)
|
|
formTheme.Focused.SelectSelector = formTheme.Focused.SelectSelector.SetString("> ")
|
|
formTheme.Focused.SelectedOption = formTheme.Focused.SelectedOption.Bold(true)
|
|
formTheme.Focused.MultiSelectSelector = formTheme.Focused.MultiSelectSelector.SetString("> ")
|
|
formTheme.Focused.SelectedPrefix = formTheme.Focused.SelectedPrefix.SetString("✓ ")
|
|
formTheme.Focused.UnselectedPrefix = formTheme.Focused.SelectedPrefix.SetString("• ")
|
|
formTheme.Blurred.SelectSelector = formTheme.Blurred.SelectSelector.SetString(" ")
|
|
formTheme.Blurred.SelectedOption = formTheme.Blurred.SelectedOption.Bold(true)
|
|
formTheme.Blurred.MultiSelectSelector = formTheme.Blurred.MultiSelectSelector.SetString(" ")
|
|
formTheme.Blurred.SelectedPrefix = formTheme.Blurred.SelectedPrefix.SetString("✓ ")
|
|
formTheme.Blurred.UnselectedPrefix = formTheme.Blurred.SelectedPrefix.SetString("• ")
|
|
|
|
styles.Form = formTheme
|
|
|
|
styles.ColumnFocused = lipgloss.NewStyle().Width(50).Height(30).Border(lipgloss.DoubleBorder(), true)
|
|
styles.ColumnBlurred = lipgloss.NewStyle().Width(50).Height(30).Border(lipgloss.HiddenBorder(), true)
|
|
styles.ColumnInsert = lipgloss.NewStyle().Width(50).Height(30).Border(lipgloss.DoubleBorder(), true).BorderForeground(styles.Active.GetForeground())
|
|
|
|
return styles
|
|
}
|
|
|
|
func parseColors(config map[string]string) *Styles {
|
|
styles := Styles{}
|
|
colors := make(map[string]lipgloss.Style)
|
|
|
|
for key, value := range config {
|
|
if strings.HasPrefix(key, "color.") {
|
|
_, colorValue, _ := strings.Cut(key, ".")
|
|
colors[colorValue] = parseColorString(value)
|
|
switch colorValue {
|
|
case "active":
|
|
styles.Active = parseColorString(value)
|
|
case "alternate":
|
|
styles.Alternate = parseColorString(value)
|
|
case "blocked":
|
|
styles.Blocked = parseColorString(value)
|
|
case "blocking":
|
|
styles.Blocking = parseColorString(value)
|
|
case "burndown.done":
|
|
styles.BurndownDone = parseColorString(value)
|
|
case "burndown.pending":
|
|
styles.BurndownPending = parseColorString(value)
|
|
case "burndown.started":
|
|
styles.BurndownStarted = parseColorString(value)
|
|
case "calendar.due":
|
|
styles.CalendarDue = parseColorString(value)
|
|
case "calendar.due.today":
|
|
styles.CalendarDueToday = parseColorString(value)
|
|
case "calendar.holiday":
|
|
styles.CalendarHoliday = parseColorString(value)
|
|
case "calendar.overdue":
|
|
styles.CalendarOverdue = parseColorString(value)
|
|
case "calendar.scheduled":
|
|
styles.CalendarScheduled = parseColorString(value)
|
|
case "calendar.today":
|
|
styles.CalendarToday = parseColorString(value)
|
|
case "calendar.weekend":
|
|
styles.CalendarWeekend = parseColorString(value)
|
|
case "calendar.weeknumber":
|
|
styles.CalendarWeeknumber = parseColorString(value)
|
|
case "completed":
|
|
styles.Completed = parseColorString(value)
|
|
case "debug":
|
|
styles.Debug = parseColorString(value)
|
|
case "deleted":
|
|
styles.Deleted = parseColorString(value)
|
|
case "due":
|
|
styles.Due = parseColorString(value)
|
|
case "due.today":
|
|
styles.DueToday = parseColorString(value)
|
|
case "error":
|
|
styles.Error = parseColorString(value)
|
|
case "footnote":
|
|
styles.Footnote = parseColorString(value)
|
|
case "header":
|
|
styles.Header = parseColorString(value)
|
|
case "history.add":
|
|
styles.HistoryAdd = parseColorString(value)
|
|
case "history.delete":
|
|
styles.HistoryDelete = parseColorString(value)
|
|
case "history.done":
|
|
styles.HistoryDone = parseColorString(value)
|
|
case "label":
|
|
styles.Label = parseColorString(value)
|
|
case "label.sort":
|
|
styles.LabelSort = parseColorString(value)
|
|
case "overdue":
|
|
styles.Overdue = parseColorString(value)
|
|
case "project.none":
|
|
styles.ProjectNone = parseColorString(value)
|
|
case "recurring":
|
|
styles.Recurring = parseColorString(value)
|
|
case "scheduled":
|
|
styles.Scheduled = parseColorString(value)
|
|
case "summary.background":
|
|
styles.SummaryBackground = parseColorString(value)
|
|
case "summary.bar":
|
|
styles.SummaryBar = parseColorString(value)
|
|
case "sync.added":
|
|
styles.SyncAdded = parseColorString(value)
|
|
case "sync.changed":
|
|
styles.SyncChanged = parseColorString(value)
|
|
case "sync.rejected":
|
|
styles.SyncRejected = parseColorString(value)
|
|
case "tag.next":
|
|
styles.TagNext = parseColorString(value)
|
|
case "tag.none":
|
|
styles.TagNone = parseColorString(value)
|
|
case "tagged":
|
|
styles.Tagged = parseColorString(value)
|
|
case "uda.priority.H":
|
|
styles.UdaPriorityH = parseColorString(value)
|
|
case "uda.priority.L":
|
|
styles.UdaPriorityL = parseColorString(value)
|
|
case "uda.priority.M":
|
|
styles.UdaPriorityM = parseColorString(value)
|
|
case "undo.after":
|
|
styles.UndoAfter = parseColorString(value)
|
|
case "undo.before":
|
|
styles.UndoBefore = parseColorString(value)
|
|
case "until":
|
|
styles.Until = parseColorString(value)
|
|
case "warning":
|
|
styles.Warning = parseColorString(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
styles.Colors = colors
|
|
|
|
return &styles
|
|
}
|
|
|
|
func parseColorString(color string) lipgloss.Style {
|
|
style := lipgloss.NewStyle()
|
|
if color == "" {
|
|
return style
|
|
}
|
|
|
|
if strings.Contains(color, "on") {
|
|
fgbg := strings.Split(color, "on")
|
|
fg := strings.TrimSpace(fgbg[0])
|
|
bg := strings.TrimSpace(fgbg[1])
|
|
if fg != "" {
|
|
style = style.Foreground(parseColor(fg))
|
|
}
|
|
if bg != "" {
|
|
style = style.Background(parseColor(bg))
|
|
}
|
|
} else {
|
|
style = style.Foreground(parseColor(strings.TrimSpace(color)))
|
|
}
|
|
|
|
return style
|
|
}
|
|
|
|
func parseColor(color string) lipgloss.Color {
|
|
if strings.HasPrefix(color, "rgb") {
|
|
return lipgloss.Color(convertRgbToAnsi(strings.TrimPrefix(color, "rgb")))
|
|
}
|
|
if strings.HasPrefix(color, "color") {
|
|
return lipgloss.Color(strings.TrimPrefix(color, "color"))
|
|
}
|
|
if strings.HasPrefix(color, "gray") {
|
|
gray, err := strconv.Atoi(strings.TrimPrefix(color, "gray"))
|
|
if err != nil {
|
|
slog.Error("Invalid gray color format")
|
|
return lipgloss.Color("0")
|
|
}
|
|
return lipgloss.Color(strconv.Itoa(gray + 232))
|
|
}
|
|
if ansi, okcolor := colorStrings[color]; okcolor {
|
|
return lipgloss.Color(strconv.Itoa(ansi))
|
|
}
|
|
|
|
slog.Error("Invalid color format")
|
|
return lipgloss.Color("0")
|
|
}
|
|
|
|
func convertRgbToAnsi(rgbString string) string {
|
|
var err error
|
|
|
|
if len(rgbString) != 3 {
|
|
slog.Error("Invalid RGB color format")
|
|
return ""
|
|
}
|
|
|
|
r, err := strconv.Atoi(string(rgbString[0]))
|
|
if err != nil {
|
|
slog.Error("Invalid value for R")
|
|
return ""
|
|
}
|
|
|
|
g, err := strconv.Atoi(string(rgbString[1]))
|
|
if err != nil {
|
|
slog.Error("Invalid value for G")
|
|
return ""
|
|
}
|
|
|
|
b, err := strconv.Atoi(string(rgbString[2]))
|
|
if err != nil {
|
|
slog.Error("Invalid value for B")
|
|
return ""
|
|
}
|
|
|
|
return strconv.Itoa(16 + (36 * r) + (6 * g) + b)
|
|
}
|
|
|
|
var colorStrings = map[string]int{
|
|
"black": 0,
|
|
"red": 1,
|
|
"green": 2,
|
|
"yellow": 3,
|
|
"blue": 4,
|
|
"magenta": 5,
|
|
"cyan": 6,
|
|
"white": 7,
|
|
"bright black": 8,
|
|
"bright red": 9,
|
|
"bright green": 10,
|
|
"bright yellow": 11,
|
|
"bright blue": 12,
|
|
"bright magenta": 13,
|
|
"bright cyan": 14,
|
|
"bright white": 15,
|
|
}
|