collect colors

This commit is contained in:
booleanmaybe 2026-04-09 09:21:48 -04:00
parent 5eed6072cb
commit 69a0b01c0f
19 changed files with 56 additions and 64 deletions

View file

@ -68,7 +68,7 @@ func DefaultTheme() Theme {
LabelColor: colors.BurndownChartLabelColor,
ValueColor: colors.BurndownChartValueColor,
BarColor: colors.BurndownChartBarColor,
BackgroundColor: config.GetContentBackgroundColor(),
BackgroundColor: config.GetColors().ContentBackgroundColor,
BarGradientFrom: colors.BurndownChartGradientFrom.Start,
BarGradientTo: colors.BurndownChartGradientTo.Start,
DotChar: '⣿', // braille full cell for dense dot matrix

View file

@ -44,7 +44,7 @@ func barFillColor(bar Bar, row, total int, theme Theme) tcell.Color {
// Use adaptive gradient: solid color when gradients disabled
if !config.UseGradients {
return config.FallbackBurndownColor
return config.GetColors().FallbackBurndownColor
}
t := float64(row) / float64(total-1)

View file

@ -25,10 +25,9 @@ func NewCompletionPrompt(words []string) *CompletionPrompt {
inputField := tview.NewInputField()
// Configure the input field
inputField.SetFieldBackgroundColor(config.GetContentBackgroundColor())
inputField.SetFieldTextColor(config.GetContentTextColor())
colors := config.GetColors()
inputField.SetFieldBackgroundColor(colors.ContentBackgroundColor)
inputField.SetFieldTextColor(colors.ContentTextColor)
cp := &CompletionPrompt{
InputField: inputField,
words: words,

View file

@ -27,8 +27,9 @@ type DateEdit struct {
// NewDateEdit creates a new date input field.
func NewDateEdit() *DateEdit {
inputField := tview.NewInputField()
inputField.SetFieldBackgroundColor(config.GetContentBackgroundColor())
inputField.SetFieldTextColor(config.GetContentTextColor())
colors := config.GetColors()
inputField.SetFieldBackgroundColor(colors.ContentBackgroundColor)
inputField.SetFieldTextColor(colors.ContentTextColor)
de := &DateEdit{
InputField: inputField,

View file

@ -29,8 +29,9 @@ func NewEditSelectList(values []string, allowTyping bool) *EditSelectList {
inputField := tview.NewInputField()
// Configure the input field
inputField.SetFieldBackgroundColor(config.GetContentBackgroundColor())
inputField.SetFieldTextColor(config.GetContentTextColor())
colors := config.GetColors()
inputField.SetFieldBackgroundColor(colors.ContentBackgroundColor)
inputField.SetFieldTextColor(colors.ContentTextColor)
esl := &EditSelectList{
InputField: inputField,

View file

@ -37,8 +37,9 @@ func NewIntEditSelect(min, max int, allowTyping bool) *IntEditSelect {
}
inputField := tview.NewInputField()
inputField.SetFieldBackgroundColor(config.GetContentBackgroundColor())
inputField.SetFieldTextColor(config.GetContentTextColor())
colors := config.GetColors()
inputField.SetFieldBackgroundColor(colors.ContentBackgroundColor)
inputField.SetFieldTextColor(colors.ContentTextColor)
ies := &IntEditSelect{
InputField: inputField,

View file

@ -31,8 +31,9 @@ type RecurrenceEdit struct {
// NewRecurrenceEdit creates a new recurrence editor.
func NewRecurrenceEdit() *RecurrenceEdit {
inputField := tview.NewInputField()
inputField.SetFieldBackgroundColor(config.GetContentBackgroundColor())
inputField.SetFieldTextColor(config.GetContentTextColor())
colors := config.GetColors()
inputField.SetFieldBackgroundColor(colors.ContentBackgroundColor)
inputField.SetFieldTextColor(colors.ContentTextColor)
re := &RecurrenceEdit{
InputField: inputField,

View file

@ -37,7 +37,7 @@ func NewTaskList(maxVisibleRows int) *TaskList {
Box: tview.NewBox(),
maxVisibleRows: maxVisibleRows,
idGradient: colors.TaskBoxIDColor,
idFallback: config.FallbackTaskIDColor,
idFallback: colors.FallbackTaskIDColor,
titleColor: colors.TaskBoxTitleColor,
selectionColor: colors.TaskListSelectionColor,
statusDoneColor: colors.TaskListStatusDoneColor,

View file

@ -40,7 +40,7 @@ func TestNewTaskList(t *testing.T) {
if tl.idGradient != colors.TaskBoxIDColor {
t.Error("Expected ID gradient from config")
}
if tl.idFallback != config.FallbackTaskIDColor {
if tl.idFallback != config.GetColors().FallbackTaskIDColor {
t.Error("Expected ID fallback from config")
}
if tl.titleColor != colors.TaskBoxTitleColor {

View file

@ -59,7 +59,7 @@ func (w *WordList) Draw(screen tcell.Screen) {
}
wordStyle := tcell.StyleDefault.Foreground(w.fgColor).Background(w.bgColor)
spaceStyle := tcell.StyleDefault.Background(config.GetContentBackgroundColor())
spaceStyle := tcell.StyleDefault.Background(config.GetColors().ContentBackgroundColor)
currentX := x
currentY := y

View file

@ -45,6 +45,11 @@ type ColorConfig struct {
TaskDetailEditFocusText string // tview color string like "[white]"
TaskDetailTagForeground tcell.Color
TaskDetailTagBackground tcell.Color
TaskDetailPlaceholderColor tcell.Color
// Content area colors (base canvas for editable/readable content)
ContentBackgroundColor tcell.Color
ContentTextColor tcell.Color
// Search box colors
SearchBoxLabelColor tcell.Color
@ -87,6 +92,13 @@ type ColorConfig struct {
HeaderActionViewKeyColor string // tview color string for view action keys
HeaderActionViewLabelColor string // tview color string for view action labels
// Plugin-specific colors
DepsEditorBackground tcell.Color // muted slate for dependency editor caption
// Fallback solid colors for gradient scenarios (used when UseGradients = false)
FallbackTaskIDColor tcell.Color // Deep Sky Blue (end of task ID gradient)
FallbackBurndownColor tcell.Color // Purple (start of burndown gradient)
// Statusline colors (bottom bar, powerline style)
StatuslineBg string // hex color for stat segment background, e.g. "#3a3a5c"
StatuslineFg string // hex color for stat segment text, e.g. "#cccccc"
@ -142,6 +154,11 @@ func DefaultColors() *ColorConfig {
TaskDetailEditFocusText: "[white]", // White text after arrow
TaskDetailTagForeground: tcell.NewRGBColor(180, 200, 220), // Light blue-gray text
TaskDetailTagBackground: tcell.NewRGBColor(30, 50, 120), // Dark blue background (more bluish)
TaskDetailPlaceholderColor: tcell.ColorGray, // Gray for placeholder text in edit fields
// Content area (base canvas)
ContentBackgroundColor: tcell.ColorBlack, // dark theme: explicit black
ContentTextColor: tcell.ColorWhite, // dark theme: white text
// Search box
SearchBoxLabelColor: tcell.ColorWhite,
@ -196,6 +213,13 @@ func DefaultColors() *ColorConfig {
HeaderActionViewKeyColor: "#5fafff", // cyan for view-specific actions
HeaderActionViewLabelColor: "#808080", // gray for view-specific labels
// Plugin-specific
DepsEditorBackground: tcell.NewHexColor(0x4e5768), // Muted slate
// Fallback solid colors (no-gradient terminals)
FallbackTaskIDColor: tcell.NewRGBColor(0, 191, 255), // Deep Sky Blue
FallbackBurndownColor: tcell.NewRGBColor(134, 90, 214), // Purple
// Statusline (Nord theme)
StatuslineBg: "#434c5e", // Nord polar night 3
StatuslineFg: "#d8dee9", // Nord snow storm 1
@ -221,30 +245,14 @@ var UseGradients bool
// Screen-wide gradients show more banding on 256-color terminals, so require truecolor
var UseWideGradients bool
// Plugin-specific background colors for code-only plugins
var (
// DepsEditorBackground: muted slate for the dependency editor caption
DepsEditorBackground = tcell.NewHexColor(0x4e5768)
)
// Fallback solid colors for gradient scenarios (used when UseGradients = false)
var (
// Caption title fallback: Royal Blue (end of gradient)
FallbackTitleColor = tcell.NewRGBColor(65, 105, 225)
// Task ID fallback: Deep Sky Blue (end of gradient)
FallbackTaskIDColor = tcell.NewRGBColor(0, 191, 255)
// Burndown chart fallback: Purple (start of gradient)
FallbackBurndownColor = tcell.NewRGBColor(134, 90, 214)
// Caption row fallback: Midpoint of Midnight Blue to Royal Blue
FallbackCaptionColor = tcell.NewRGBColor(45, 65, 169)
)
// GetColors returns the global color configuration with theme-aware overrides
func GetColors() *ColorConfig {
if !colorsInitialized {
globalColors = DefaultColors()
// Apply theme-aware overrides for critical text colors
if GetEffectiveTheme() == "light" {
globalColors.ContentBackgroundColor = tcell.ColorDefault
globalColors.ContentTextColor = tcell.ColorBlack
globalColors.SearchBoxLabelColor = tcell.ColorBlack
globalColors.SearchBoxTextColor = tcell.ColorBlack
globalColors.InputFieldTextColor = tcell.ColorBlack

View file

@ -10,7 +10,6 @@ import (
"path/filepath"
"strings"
"github.com/gdamore/tcell/v2"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
@ -375,24 +374,6 @@ func GetEffectiveTheme() string {
return "dark" // default fallback
}
// GetContentBackgroundColor returns the background color for markdown content areas
// Dark theme needs black background for light text; light theme uses terminal default
func GetContentBackgroundColor() tcell.Color {
if GetEffectiveTheme() == "dark" {
return tcell.ColorBlack
}
return tcell.ColorDefault
}
// GetContentTextColor returns the appropriate text color for content areas
// Dark theme uses white text; light theme uses black text
func GetContentTextColor() tcell.Color {
if GetEffectiveTheme() == "dark" {
return tcell.ColorWhite
}
return tcell.ColorBlack
}
// GetGradientThreshold returns the minimum color count required for gradients
// Valid values: 16, 256, 16777216 (truecolor)
func GetGradientThreshold() int {

View file

@ -232,7 +232,7 @@ func (ir *InputRouter) openDepsEditor(taskID string) bool {
Description: model.DepsEditorViewDesc,
ConfigIndex: -1,
Type: "tiki",
Background: config.DepsEditorBackground,
Background: config.GetColors().DepsEditorBackground,
},
TaskID: taskID,
Lanes: []plugin.TikiLane{

View file

@ -25,7 +25,7 @@ func DrawSingleLineBorder(screen tcell.Screen, x, y, width, height int) {
}
colors := config.GetColors()
style := tcell.StyleDefault.Foreground(colors.TaskBoxUnselectedBorder).Background(config.GetContentBackgroundColor())
style := tcell.StyleDefault.Foreground(colors.TaskBoxUnselectedBorder).Background(colors.ContentBackgroundColor)
DrawSingleLineBorderWithStyle(screen, x, y, width, height, style)
}

View file

@ -47,7 +47,7 @@ func NewNavigableMarkdown(cfg NavigableMarkdownConfig) *NavigableMarkdown {
renderer = renderer.WithCodeBorder(b)
}
nm.viewer.SetRenderer(renderer)
nm.viewer.SetBackgroundColor(config.GetContentBackgroundColor())
nm.viewer.SetBackgroundColor(config.GetColors().ContentBackgroundColor)
if cfg.ImageManager != nil && cfg.ImageManager.Supported() {
nm.viewer.SetImageManager(cfg.ImageManager)
}

View file

@ -22,8 +22,8 @@ func NewSearchBox() *SearchBox {
// Configure the input field (border drawn manually in Draw)
inputField.SetLabel("> ")
inputField.SetLabelColor(colors.SearchBoxLabelColor)
inputField.SetFieldBackgroundColor(config.GetContentBackgroundColor())
inputField.SetFieldTextColor(config.GetContentTextColor())
inputField.SetFieldBackgroundColor(colors.ContentBackgroundColor)
inputField.SetFieldTextColor(colors.ContentTextColor)
inputField.SetBorder(false)
sb := &SearchBox{
@ -60,7 +60,7 @@ func (sb *SearchBox) Draw(screen tcell.Screen) {
}
// Fill interior with theme-aware background color
bgColor := config.GetContentBackgroundColor()
bgColor := config.GetColors().ContentBackgroundColor
bgStyle := tcell.StyleDefault.Background(bgColor)
for row := y; row < y+height; row++ {
for col := x; col < x+width; col++ {

View file

@ -30,7 +30,7 @@ func applyFrameStyle(frame *tview.Frame, selected bool, colors *config.ColorConf
// buildCompactTaskContent builds the content string for compact task display
func buildCompactTaskContent(task *taskpkg.Task, colors *config.ColorConfig, availableWidth int) string {
emoji := taskpkg.TypeEmoji(task.Type)
idGradient := gradient.RenderAdaptiveGradientText(task.ID, colors.TaskBoxIDColor, config.FallbackTaskIDColor)
idGradient := gradient.RenderAdaptiveGradientText(task.ID, colors.TaskBoxIDColor, colors.FallbackTaskIDColor)
truncatedTitle := tview.Escape(util.TruncateText(task.Title, availableWidth))
priorityEmoji := taskpkg.PriorityLabel(task.Priority)
pointsVisual := util.GeneratePointsVisual(task.Points, config.GetMaxPoints(), colors.PointsFilledColor, colors.PointsUnfilledColor)
@ -45,7 +45,7 @@ func buildCompactTaskContent(task *taskpkg.Task, colors *config.ColorConfig, ava
// buildExpandedTaskContent builds the content string for expanded task display
func buildExpandedTaskContent(task *taskpkg.Task, colors *config.ColorConfig, availableWidth int) string {
emoji := taskpkg.TypeEmoji(task.Type)
idGradient := gradient.RenderAdaptiveGradientText(task.ID, colors.TaskBoxIDColor, config.FallbackTaskIDColor)
idGradient := gradient.RenderAdaptiveGradientText(task.ID, colors.TaskBoxIDColor, colors.FallbackTaskIDColor)
truncatedTitle := tview.Escape(util.TruncateText(task.Title, availableWidth))
// Extract first 3 lines of description

View file

@ -114,7 +114,7 @@ func (b *Base) assembleMetadataBox(
metadataBox := tview.NewFrame(metadataContainer).SetBorders(0, 0, 0, 0, 0, 0)
metadataBox.SetBorder(true).SetTitle(
fmt.Sprintf(" %s ", gradient.RenderAdaptiveGradientText(task.ID, colors.TaskDetailIDColor, config.FallbackTaskIDColor)),
fmt.Sprintf(" %s ", gradient.RenderAdaptiveGradientText(task.ID, colors.TaskDetailIDColor, colors.FallbackTaskIDColor)),
).SetBorderColor(colors.TaskBoxUnselectedBorder)
metadataBox.SetBorderPadding(1, 0, 2, 2)

View file

@ -381,7 +381,7 @@ func (ev *TaskEditView) ensureTagsTextArea(task *taskpkg.Task) *tview.TextArea {
ev.tagsTextArea.SetBorder(false)
ev.tagsTextArea.SetBorderPadding(1, 1, 2, 2)
ev.tagsTextArea.SetPlaceholder("Enter tags separated by spaces")
ev.tagsTextArea.SetPlaceholderStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
ev.tagsTextArea.SetPlaceholderStyle(tcell.StyleDefault.Foreground(config.GetColors().TaskDetailPlaceholderColor))
ev.tagsTextArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyCtrlS {
@ -448,7 +448,7 @@ func (ev *TaskEditView) ensureTitleInput(task *taskpkg.Task) *tview.InputField {
if ev.titleInput == nil {
colors := config.GetColors()
ev.titleInput = tview.NewInputField()
ev.titleInput.SetFieldBackgroundColor(config.GetContentBackgroundColor())
ev.titleInput.SetFieldBackgroundColor(colors.ContentBackgroundColor)
ev.titleInput.SetFieldTextColor(colors.InputFieldTextColor)
ev.titleInput.SetBorder(false)