diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index fde680d12e1..798d613f430 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -233,8 +233,20 @@ type ThemeConfig struct { MarkedBaseCommitFgColor []string `yaml:"markedBaseCommitFgColor"` // Background color of marked base commit (for rebase) MarkedBaseCommitBgColor []string `yaml:"markedBaseCommitBgColor"` - // Color for file with unstaged changes + // Color for file with unstaged changes (used as fallback when per-status colors are not set) UnstagedChangesColor []string `yaml:"unstagedChangesColor" jsonschema:"minItems=1,uniqueItems=true"` + // Color for modified files. Defaults to unstagedChangesColor if empty. + ModifiedColor []string `yaml:"modifiedColor"` + // Color for deleted files. Defaults to unstagedChangesColor if empty. + DeletedColor []string `yaml:"deletedColor"` + // Color for untracked (new) files. Defaults to unstagedChangesColor if empty. + UntrackedColor []string `yaml:"untrackedColor"` + // Color for added (staged new) files. Defaults to green if empty. + AddedColor []string `yaml:"addedColor"` + // Color for renamed files. Defaults to unstagedChangesColor if empty. + RenamedColor []string `yaml:"renamedColor"` + // Color for copied files. Defaults to cyan if empty. + CopiedColor []string `yaml:"copiedColor"` // Default text color DefaultFgColor []string `yaml:"defaultFgColor" jsonschema:"minItems=1,uniqueItems=true"` } @@ -796,6 +808,12 @@ func GetDefaultConfig() *UserConfig { MarkedBaseCommitBgColor: []string{"yellow"}, MarkedBaseCommitFgColor: []string{"blue"}, UnstagedChangesColor: []string{"red"}, + ModifiedColor: []string{}, + DeletedColor: []string{}, + UntrackedColor: []string{}, + AddedColor: []string{}, + RenamedColor: []string{}, + CopiedColor: []string{}, DefaultFgColor: []string{"default"}, }, CommitLength: CommitLengthConfig{Show: true}, diff --git a/pkg/gui/presentation/files.go b/pkg/gui/presentation/files.go index cc0a9388972..0d2afa47436 100644 --- a/pkg/gui/presentation/files.go +++ b/pkg/gui/presentation/files.go @@ -183,23 +183,46 @@ func getFileLine( func formatFileStatus(file *models.File, restColor style.TextStyle) string { firstChar := file.ShortStatus[0:1] - firstCharCl := style.FgGreen - switch firstChar { - case "?": - firstCharCl = theme.UnstagedChangesColor - case " ": + firstCharCl := colorForStatusChar(firstChar, true) + if firstCharCl == (style.TextStyle{}) { firstCharCl = restColor } secondChar := file.ShortStatus[1:2] - secondCharCl := theme.UnstagedChangesColor - if secondChar == " " { + secondCharCl := colorForStatusChar(secondChar, false) + if secondCharCl == (style.TextStyle{}) { secondCharCl = restColor } return firstCharCl.Sprint(firstChar) + secondCharCl.Sprint(secondChar) } +// colorForStatusChar returns the color for a single status character from git short status. +// isStagedColumn indicates whether this is the first (index/staged) or second (worktree) column. +func colorForStatusChar(ch string, isStagedColumn bool) style.TextStyle { + switch ch { + case "M": + return theme.ModifiedColor + case "D": + return theme.DeletedColor + case "?": + return theme.UntrackedColor + case "A": + return theme.AddedColor + case "R": + return theme.RenamedColor + case "C": + return theme.CopiedColor + case "T": + return theme.ModifiedColor + case " ": + // space means no change in this column — use rest/name color (caller handles this) + return style.TextStyle{} + default: + return theme.UnstagedChangesColor + } +} + func formatLineChanges(linesAdded, linesDeleted int) string { output := "" @@ -285,15 +308,17 @@ func getCommitFileLine( func getColorForChangeStatus(changeStatus string) style.TextStyle { switch changeStatus { case "A": - return style.FgGreen - case "M", "R": - return style.FgYellow + return theme.AddedColor + case "M": + return theme.ModifiedColor + case "R": + return theme.RenamedColor case "D": - return theme.UnstagedChangesColor + return theme.DeletedColor case "C": - return style.FgCyan + return theme.CopiedColor case "T": - return style.FgMagenta + return theme.ModifiedColor default: return theme.DefaultTextColor } diff --git a/pkg/theme/theme.go b/pkg/theme/theme.go index acd8ebf7161..f1c9c2fb972 100644 --- a/pkg/theme/theme.go +++ b/pkg/theme/theme.go @@ -45,6 +45,13 @@ var ( DiffTerminalColor = style.FgMagenta UnstagedChangesColor = style.New() + + ModifiedColor = style.New() + DeletedColor = style.New() + UntrackedColor = style.New() + AddedColor = style.New() + RenamedColor = style.New() + CopiedColor = style.New() ) // UpdateTheme updates all theme variables @@ -66,6 +73,38 @@ func UpdateTheme(themeConfig config.ThemeConfig) { unstagedChangesTextStyle := GetTextStyle(themeConfig.UnstagedChangesColor, false) UnstagedChangesColor = unstagedChangesTextStyle + // Per-status colors — fall back to UnstagedChangesColor or hardcoded defaults if not set + if len(themeConfig.ModifiedColor) > 0 { + ModifiedColor = GetTextStyle(themeConfig.ModifiedColor, false) + } else { + ModifiedColor = UnstagedChangesColor + } + if len(themeConfig.DeletedColor) > 0 { + DeletedColor = GetTextStyle(themeConfig.DeletedColor, false) + } else { + DeletedColor = UnstagedChangesColor + } + if len(themeConfig.UntrackedColor) > 0 { + UntrackedColor = GetTextStyle(themeConfig.UntrackedColor, false) + } else { + UntrackedColor = UnstagedChangesColor + } + if len(themeConfig.AddedColor) > 0 { + AddedColor = GetTextStyle(themeConfig.AddedColor, false) + } else { + AddedColor = style.FgGreen + } + if len(themeConfig.RenamedColor) > 0 { + RenamedColor = GetTextStyle(themeConfig.RenamedColor, false) + } else { + RenamedColor = UnstagedChangesColor + } + if len(themeConfig.CopiedColor) > 0 { + CopiedColor = GetTextStyle(themeConfig.CopiedColor, false) + } else { + CopiedColor = style.FgCyan + } + GocuiSelectedLineBgColor = GetGocuiStyle(themeConfig.SelectedLineBgColor) GocuiInactiveViewSelectedLineBgColor = GetGocuiStyle(themeConfig.InactiveViewSelectedLineBgColor) OptionsColor = GetGocuiStyle(themeConfig.OptionsTextColor)