mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
fix: derive diff/code bg colors from active theme instead of hardcoding KITT defaults
- makeTheme() and fileConfigToTheme() now compute DiffInsertBg, DiffDeleteBg, DiffEqualBg, DiffMissingBg, CodeBg, GutterBg, and WriteBg by blending the theme's own Background with its Success/Error colors, so every theme gets properly tinted diff backgrounds. - Added color derivation helpers: parseHexColor, blendHex, deriveDiffBg. - File-based themes still allow explicit diff color overrides; derived colors are used only as fallbacks. - formatToolParams() now skips body-content keys (content, old_text, new_text, etc.) from the header line regardless of value length, preventing raw unformatted code from appearing above the formatted body.
This commit is contained in:
+13
-2
@@ -111,14 +111,25 @@ func formatToolParams(toolArgs string, maxWidth int) string {
|
||||
result.WriteString(primaryVal)
|
||||
}
|
||||
|
||||
// Collect remaining parameters (skip large values like file content)
|
||||
// Collect remaining parameters, skipping body-content keys (already
|
||||
// rendered in the tool body) and any values that are too large.
|
||||
bodyKeys := map[string]bool{
|
||||
"content": true,
|
||||
"old_text": true,
|
||||
"new_text": true,
|
||||
"oldText": true,
|
||||
"newText": true,
|
||||
"todos": true,
|
||||
}
|
||||
var remaining []string
|
||||
for key, val := range params {
|
||||
if key == primaryKey {
|
||||
continue
|
||||
}
|
||||
if bodyKeys[key] {
|
||||
continue
|
||||
}
|
||||
valStr := fmt.Sprintf("%v", val)
|
||||
// Skip very large values (e.g., oldString, newString, content, todos)
|
||||
if len(valStr) > 100 {
|
||||
continue
|
||||
}
|
||||
|
||||
+110
-15
@@ -7,11 +7,86 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color derivation helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// parseHexColor parses a "#RRGGBB" hex string into r, g, b components (0-255).
|
||||
func parseHexColor(hex string) (r, g, b int) {
|
||||
hex = strings.TrimPrefix(hex, "#")
|
||||
if len(hex) == 6 {
|
||||
if v, err := strconv.ParseUint(hex[0:2], 16, 8); err == nil {
|
||||
r = int(v)
|
||||
}
|
||||
if v, err := strconv.ParseUint(hex[2:4], 16, 8); err == nil {
|
||||
g = int(v)
|
||||
}
|
||||
if v, err := strconv.ParseUint(hex[4:6], 16, 8); err == nil {
|
||||
b = int(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// blendHex linearly interpolates between two hex colors by amount (0.0–1.0).
|
||||
func blendHex(base, tint string, amount float64) string {
|
||||
br, bg, bb := parseHexColor(base)
|
||||
tr, tg, tb := parseHexColor(tint)
|
||||
clamp := func(v int) int {
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
if v > 255 {
|
||||
return 255
|
||||
}
|
||||
return v
|
||||
}
|
||||
r := clamp(int(float64(br)*(1-amount) + float64(tr)*amount))
|
||||
g := clamp(int(float64(bg)*(1-amount) + float64(tg)*amount))
|
||||
b := clamp(int(float64(bb)*(1-amount) + float64(tb)*amount))
|
||||
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
|
||||
}
|
||||
|
||||
// deriveDiffBg computes diff / code background colors from the theme's
|
||||
// background, success, and error hex pairs. Returns an adaptive color for each
|
||||
// diff element. The tint amounts are tuned for subtle differentiation.
|
||||
func deriveDiffBg(bgPair, successPair, errorPair [2]string) (diffInsert, diffDelete, diffEqual, diffMissing, codeBg, gutterBg, writeBg color.Color) {
|
||||
derive := func(idx int) (color.Color, color.Color, color.Color, color.Color) {
|
||||
bg := bgPair[idx]
|
||||
// Contrast target: darken for light mode (idx 0), lighten for dark (idx 1).
|
||||
contrast := "#000000"
|
||||
if idx == 1 {
|
||||
contrast = "#ffffff"
|
||||
}
|
||||
ins := blendHex(bg, successPair[idx], 0.13)
|
||||
del := blendHex(bg, errorPair[idx], 0.13)
|
||||
eq := blendHex(bg, contrast, 0.05)
|
||||
miss := blendHex(bg, contrast, 0.03)
|
||||
return AdaptiveColor(ins, ins), AdaptiveColor(del, del), AdaptiveColor(eq, eq), AdaptiveColor(miss, miss)
|
||||
}
|
||||
|
||||
// Pick the correct index based on detected background.
|
||||
idx := 0
|
||||
if isDarkBg {
|
||||
idx = 1
|
||||
}
|
||||
insL, delL, eqL, missL := derive(idx)
|
||||
diffInsert = insL
|
||||
diffDelete = delL
|
||||
diffEqual = eqL
|
||||
diffMissing = missL
|
||||
codeBg = eqL
|
||||
gutterBg = missL
|
||||
writeBg = insL
|
||||
return
|
||||
}
|
||||
|
||||
// ThemeEntry is a named, loadable theme — either built-in or discovered from disk.
|
||||
type ThemeEntry struct {
|
||||
Name string // Display name (filename stem or preset name)
|
||||
@@ -80,14 +155,9 @@ func makeTheme(p presetColors) Theme {
|
||||
Accent: acOr(p.accent, ac(p.primary)),
|
||||
Highlight: acOr(p.highlight, def.Highlight),
|
||||
}
|
||||
// Derive diff/code backgrounds from the base background.
|
||||
t.DiffInsertBg = def.DiffInsertBg
|
||||
t.DiffDeleteBg = def.DiffDeleteBg
|
||||
t.DiffEqualBg = def.DiffEqualBg
|
||||
t.DiffMissingBg = def.DiffMissingBg
|
||||
t.CodeBg = def.CodeBg
|
||||
t.GutterBg = def.GutterBg
|
||||
t.WriteBg = def.WriteBg
|
||||
// Derive diff/code backgrounds from the theme's own palette.
|
||||
t.DiffInsertBg, t.DiffDeleteBg, t.DiffEqualBg, t.DiffMissingBg,
|
||||
t.CodeBg, t.GutterBg, t.WriteBg = deriveDiffBg(p.background, p.success, p.error_)
|
||||
// Markdown colors.
|
||||
t.Markdown = MarkdownThemeColors{
|
||||
Text: t.Text,
|
||||
@@ -609,6 +679,17 @@ func loadThemeFile(path string) (Theme, error) {
|
||||
|
||||
func fileConfigToTheme(cfg themeFileConfig) Theme {
|
||||
def := DefaultTheme()
|
||||
|
||||
// Resolve the base background/success/error hex pairs for diff derivation.
|
||||
// We need the raw hex strings to feed deriveDiffBg.
|
||||
bgPair := resolveHexPair(cfg.Background, [2]string{"#F0F0F0", "#0D0D0D"})
|
||||
successPair := resolveHexPair(cfg.Success, [2]string{"#998800", "#CCAA00"})
|
||||
errorPair := resolveHexPair(cfg.Error, [2]string{"#CC0000", "#FF3333"})
|
||||
|
||||
// Derive diff backgrounds from the theme's own palette.
|
||||
derivedInsert, derivedDelete, derivedEqual, derivedMissing,
|
||||
derivedCodeBg, derivedGutterBg, derivedWriteBg := deriveDiffBg(bgPair, successPair, errorPair)
|
||||
|
||||
return Theme{
|
||||
Primary: cfg.Primary.resolve(def.Primary),
|
||||
Secondary: cfg.Secondary.resolve(def.Secondary),
|
||||
@@ -627,13 +708,13 @@ func fileConfigToTheme(cfg themeFileConfig) Theme {
|
||||
Accent: cfg.Accent.resolve(def.Accent),
|
||||
Highlight: cfg.Highlight.resolve(def.Highlight),
|
||||
|
||||
DiffInsertBg: cfg.DiffInsertBg.resolve(def.DiffInsertBg),
|
||||
DiffDeleteBg: cfg.DiffDeleteBg.resolve(def.DiffDeleteBg),
|
||||
DiffEqualBg: cfg.DiffEqualBg.resolve(def.DiffEqualBg),
|
||||
DiffMissingBg: cfg.DiffMissingBg.resolve(def.DiffMissingBg),
|
||||
CodeBg: cfg.CodeBg.resolve(def.CodeBg),
|
||||
GutterBg: cfg.GutterBg.resolve(def.GutterBg),
|
||||
WriteBg: cfg.WriteBg.resolve(def.WriteBg),
|
||||
DiffInsertBg: cfg.DiffInsertBg.resolve(derivedInsert),
|
||||
DiffDeleteBg: cfg.DiffDeleteBg.resolve(derivedDelete),
|
||||
DiffEqualBg: cfg.DiffEqualBg.resolve(derivedEqual),
|
||||
DiffMissingBg: cfg.DiffMissingBg.resolve(derivedMissing),
|
||||
CodeBg: cfg.CodeBg.resolve(derivedCodeBg),
|
||||
GutterBg: cfg.GutterBg.resolve(derivedGutterBg),
|
||||
WriteBg: cfg.WriteBg.resolve(derivedWriteBg),
|
||||
|
||||
Markdown: MarkdownThemeColors{
|
||||
Text: cfg.Markdown.Text.resolve(def.Markdown.Text),
|
||||
@@ -651,3 +732,17 @@ func fileConfigToTheme(cfg themeFileConfig) Theme {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// resolveHexPair returns the hex pair from an adaptiveColorPair, falling back
|
||||
// to defaults when the pair is empty.
|
||||
func resolveHexPair(a adaptiveColorPair, fallback [2]string) [2]string {
|
||||
light := a.Light
|
||||
if light == "" {
|
||||
light = fallback[0]
|
||||
}
|
||||
dark := a.Dark
|
||||
if dark == "" {
|
||||
dark = fallback[1]
|
||||
}
|
||||
return [2]string{light, dark}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseHexColor(t *testing.T) {
|
||||
tests := []struct {
|
||||
hex string
|
||||
r, g, b int
|
||||
}{
|
||||
{"#000000", 0, 0, 0},
|
||||
{"#ffffff", 255, 255, 255},
|
||||
{"#1e1e2e", 0x1e, 0x1e, 0x2e},
|
||||
{"#a6e3a1", 0xa6, 0xe3, 0xa1},
|
||||
{"#f38ba8", 0xf3, 0x8b, 0xa8},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
r, g, b := parseHexColor(tt.hex)
|
||||
if r != tt.r || g != tt.g || b != tt.b {
|
||||
t.Errorf("parseHexColor(%q) = (%d,%d,%d), want (%d,%d,%d)",
|
||||
tt.hex, r, g, b, tt.r, tt.g, tt.b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlendHex(t *testing.T) {
|
||||
// Blending with 0 amount should return the base color.
|
||||
got := blendHex("#1e1e2e", "#a6e3a1", 0.0)
|
||||
if got != "#1e1e2e" {
|
||||
t.Errorf("blendHex with 0.0 = %q, want #1e1e2e", got)
|
||||
}
|
||||
|
||||
// Blending with 1.0 amount should return the tint color.
|
||||
got = blendHex("#1e1e2e", "#a6e3a1", 1.0)
|
||||
if got != "#a6e3a1" {
|
||||
t.Errorf("blendHex with 1.0 = %q, want #a6e3a1", got)
|
||||
}
|
||||
|
||||
// Blending black and white at 0.5 should give mid gray.
|
||||
got = blendHex("#000000", "#ffffff", 0.5)
|
||||
// 127 = int(0 + 255*0.5) — truncated, so #7f7f7f
|
||||
if got != "#7f7f7f" {
|
||||
t.Errorf("blendHex black/white at 0.5 = %q, want #7f7f7f", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeriveDiffBgProducesDifferentColorsPerTheme(t *testing.T) {
|
||||
// Catppuccin palette
|
||||
catBg := [2]string{"#eff1f5", "#1e1e2e"}
|
||||
catSuccess := [2]string{"#40a02b", "#a6e3a1"}
|
||||
catError := [2]string{"#d20f39", "#f38ba8"}
|
||||
|
||||
// KITT palette
|
||||
kittBg := [2]string{"#F0F0F0", "#0D0D0D"}
|
||||
kittSuccess := [2]string{"#998800", "#CCAA00"}
|
||||
kittError := [2]string{"#CC0000", "#FF3333"}
|
||||
|
||||
catInsert, catDelete, _, _, _, _, _ := deriveDiffBg(catBg, catSuccess, catError)
|
||||
kittInsert, kittDelete, _, _, _, _, _ := deriveDiffBg(kittBg, kittSuccess, kittError)
|
||||
|
||||
if catInsert == kittInsert {
|
||||
t.Error("catppuccin DiffInsertBg should differ from kitt DiffInsertBg")
|
||||
}
|
||||
if catDelete == kittDelete {
|
||||
t.Error("catppuccin DiffDeleteBg should differ from kitt DiffDeleteBg")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeThemeDerivesUniqueDiffColors(t *testing.T) {
|
||||
themes := builtinThemes()
|
||||
kitt := themes["kitt"]
|
||||
cat := themes["catppuccin"]
|
||||
|
||||
// The catppuccin diff backgrounds should NOT equal the kitt defaults.
|
||||
if cat.DiffInsertBg == kitt.DiffInsertBg {
|
||||
t.Error("catppuccin DiffInsertBg should differ from kitt default")
|
||||
}
|
||||
if cat.DiffDeleteBg == kitt.DiffDeleteBg {
|
||||
t.Error("catppuccin DiffDeleteBg should differ from kitt default")
|
||||
}
|
||||
if cat.DiffEqualBg == kitt.DiffEqualBg {
|
||||
t.Error("catppuccin DiffEqualBg should differ from kitt default")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user