diff options
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16')
3 files changed, 1467 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go new file mode 100644 index 0000000..fedd783 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go @@ -0,0 +1,651 @@ +package dank16 + +import ( + "fmt" + "math" + + "github.com/lucasb-eyer/go-colorful" +) + +type RGB struct { + R, G, B float64 +} + +type HSV struct { + H, S, V float64 +} + +type ColorInfo struct { + Hex string `json:"hex"` + HexStripped string `json:"hex_stripped"` + R int `json:"r"` + G int `json:"g"` + B int `json:"b"` +} + +type VariantColorValue struct { + Hex string `json:"hex"` + HexStripped string `json:"hex_stripped"` +} + +type VariantColorInfo struct { + Dark VariantColorValue `json:"dark"` + Light VariantColorValue `json:"light"` + Default VariantColorValue `json:"default"` +} + +type Palette struct { + Color0 ColorInfo `json:"color0"` + Color1 ColorInfo `json:"color1"` + Color2 ColorInfo `json:"color2"` + Color3 ColorInfo `json:"color3"` + Color4 ColorInfo `json:"color4"` + Color5 ColorInfo `json:"color5"` + Color6 ColorInfo `json:"color6"` + Color7 ColorInfo `json:"color7"` + Color8 ColorInfo `json:"color8"` + Color9 ColorInfo `json:"color9"` + Color10 ColorInfo `json:"color10"` + Color11 ColorInfo `json:"color11"` + Color12 ColorInfo `json:"color12"` + Color13 ColorInfo `json:"color13"` + Color14 ColorInfo `json:"color14"` + Color15 ColorInfo `json:"color15"` +} + +type VariantPalette struct { + Color0 VariantColorInfo `json:"color0"` + Color1 VariantColorInfo `json:"color1"` + Color2 VariantColorInfo `json:"color2"` + Color3 VariantColorInfo `json:"color3"` + Color4 VariantColorInfo `json:"color4"` + Color5 VariantColorInfo `json:"color5"` + Color6 VariantColorInfo `json:"color6"` + Color7 VariantColorInfo `json:"color7"` + Color8 VariantColorInfo `json:"color8"` + Color9 VariantColorInfo `json:"color9"` + Color10 VariantColorInfo `json:"color10"` + Color11 VariantColorInfo `json:"color11"` + Color12 VariantColorInfo `json:"color12"` + Color13 VariantColorInfo `json:"color13"` + Color14 VariantColorInfo `json:"color14"` + Color15 VariantColorInfo `json:"color15"` +} + +func NewColorInfo(hex string) ColorInfo { + rgb := HexToRGB(hex) + stripped := hex + if len(hex) > 0 && hex[0] == '#' { + stripped = hex[1:] + } + return ColorInfo{ + Hex: hex, + HexStripped: stripped, + R: int(math.Round(rgb.R * 255)), + G: int(math.Round(rgb.G * 255)), + B: int(math.Round(rgb.B * 255)), + } +} + +func HexToRGB(hex string) RGB { + if hex[0] == '#' { + hex = hex[1:] + } + var r, g, b uint8 + fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b) + return RGB{ + R: float64(r) / 255.0, + G: float64(g) / 255.0, + B: float64(b) / 255.0, + } +} + +func RGBToHex(rgb RGB) string { + r := math.Max(0, math.Min(1, rgb.R)) + g := math.Max(0, math.Min(1, rgb.G)) + b := math.Max(0, math.Min(1, rgb.B)) + return fmt.Sprintf("#%02x%02x%02x", int(r*255), int(g*255), int(b*255)) +} + +func RGBToHSV(rgb RGB) HSV { + max := math.Max(math.Max(rgb.R, rgb.G), rgb.B) + min := math.Min(math.Min(rgb.R, rgb.G), rgb.B) + delta := max - min + + var h float64 + switch { + case delta == 0: + h = 0 + case max == rgb.R: + h = math.Mod((rgb.G-rgb.B)/delta, 6.0) / 6.0 + case max == rgb.G: + h = ((rgb.B-rgb.R)/delta + 2.0) / 6.0 + default: + h = ((rgb.R-rgb.G)/delta + 4.0) / 6.0 + } + + if h < 0 { + h += 1.0 + } + + var s float64 + if max == 0 { + s = 0 + } else { + s = delta / max + } + + return HSV{H: h, S: s, V: max} +} + +func HSVToRGB(hsv HSV) RGB { + h := hsv.H * 6.0 + c := hsv.V * hsv.S + x := c * (1.0 - math.Abs(math.Mod(h, 2.0)-1.0)) + m := hsv.V - c + + var r, g, b float64 + switch int(h) { + case 0: + r, g, b = c, x, 0 + case 1: + r, g, b = x, c, 0 + case 2: + r, g, b = 0, c, x + case 3: + r, g, b = 0, x, c + case 4: + r, g, b = x, 0, c + case 5: + r, g, b = c, 0, x + default: + r, g, b = c, 0, x + } + + return RGB{R: r + m, G: g + m, B: b + m} +} + +func sRGBToLinear(c float64) float64 { + if c <= 0.04045 { + return c / 12.92 + } + return math.Pow((c+0.055)/1.055, 2.4) +} + +func Luminance(hex string) float64 { + rgb := HexToRGB(hex) + return 0.2126*sRGBToLinear(rgb.R) + 0.7152*sRGBToLinear(rgb.G) + 0.0722*sRGBToLinear(rgb.B) +} + +func ContrastRatio(hexFg, hexBg string) float64 { + lumFg := Luminance(hexFg) + lumBg := Luminance(hexBg) + lighter := math.Max(lumFg, lumBg) + darker := math.Min(lumFg, lumBg) + return (lighter + 0.05) / (darker + 0.05) +} + +func getLstar(hex string) float64 { + rgb := HexToRGB(hex) + col := colorful.Color{R: rgb.R, G: rgb.G, B: rgb.B} + L, _, _ := col.Lab() + return L * 100.0 // go-colorful uses 0-1, we need 0-100 for DPS +} + +// Lab to hex, clamping if needed +func labToHex(L, a, b float64) string { + c := colorful.Lab(L/100.0, a, b) // back to 0-1 for go-colorful + r, g, b2 := c.Clamped().RGB255() + return fmt.Sprintf("#%02x%02x%02x", r, g, b2) +} + +func DeltaPhiStar(hexFg, hexBg string, negativePolarity bool) float64 { + Lf := getLstar(hexFg) + Lb := getLstar(hexBg) + + phi := 1.618 + inv := 0.618 + lc := math.Pow(math.Abs(math.Pow(Lb, phi)-math.Pow(Lf, phi)), inv)*1.414 - 40 + + if negativePolarity { + lc += 5 + } + + return lc +} + +func DeltaPhiStarContrast(hexFg, hexBg string, isLightMode bool) float64 { + negativePolarity := !isLightMode + return DeltaPhiStar(hexFg, hexBg, negativePolarity) +} + +func EnsureContrast(hexColor, hexBg string, minRatio float64, isLightMode bool) string { + currentRatio := ContrastRatio(hexColor, hexBg) + if currentRatio >= minRatio { + return hexColor + } + + rgb := HexToRGB(hexColor) + hsv := RGBToHSV(rgb) + + for step := 1; step < 30; step++ { + delta := float64(step) * 0.02 + + if isLightMode { + newV := math.Max(0, hsv.V-delta) + candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if ContrastRatio(candidate, hexBg) >= minRatio { + return candidate + } + + newV = math.Min(1, hsv.V+delta) + candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if ContrastRatio(candidate, hexBg) >= minRatio { + return candidate + } + } else { + newV := math.Min(1, hsv.V+delta) + candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if ContrastRatio(candidate, hexBg) >= minRatio { + return candidate + } + + newV = math.Max(0, hsv.V-delta) + candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if ContrastRatio(candidate, hexBg) >= minRatio { + return candidate + } + } + } + + return hexColor +} + +func EnsureContrastDPS(hexColor, hexBg string, minLc float64, isLightMode bool) string { + currentLc := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) + if currentLc >= minLc { + return hexColor + } + + rgb := HexToRGB(hexColor) + hsv := RGBToHSV(rgb) + + for step := 1; step < 50; step++ { + delta := float64(step) * 0.015 + + if isLightMode { + newV := math.Max(0, hsv.V-delta) + candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { + return candidate + } + + newV = math.Min(1, hsv.V+delta) + candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { + return candidate + } + } else { + newV := math.Min(1, hsv.V+delta) + candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { + return candidate + } + + newV = math.Max(0, hsv.V-delta) + candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) + if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { + return candidate + } + } + } + + return hexColor +} + +// Nudge L* until contrast is good enough. Keeps hue intact unlike HSV fiddling. +func EnsureContrastDPSLstar(hexColor, hexBg string, minLc float64, isLightMode bool) string { + current := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) + if current >= minLc { + return hexColor + } + + fg := HexToRGB(hexColor) + cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B} + Lf, af, bf := cf.Lab() + + dir := 1.0 + if isLightMode { + dir = -1.0 // light mode = darker text + } + + step := 0.5 + for range 120 { + Lf = math.Max(0, math.Min(100, Lf+dir*step)) + cand := labToHex(Lf, af, bf) + if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { + return cand + } + } + + return hexColor +} + +// Bidirectional contrast - tries both lighter and darker, picks closest to original +func EnsureContrastDPSBidirectional(hexColor, hexBg string, minLc float64, isLightMode bool) string { + current := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) + if current >= minLc { + return hexColor + } + + fg := HexToRGB(hexColor) + cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B} + origL, af, bf := cf.Lab() + + var darkerResult, lighterResult string + darkerL, lighterL := origL, origL + darkerFound, lighterFound := false, false + + step := 0.5 + for i := range 120 { + if !darkerFound { + darkerL = math.Max(0, origL-float64(i)*step) + cand := labToHex(darkerL, af, bf) + if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { + darkerResult = cand + darkerFound = true + } + } + if !lighterFound { + lighterL = math.Min(100, origL+float64(i)*step) + cand := labToHex(lighterL, af, bf) + if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { + lighterResult = cand + lighterFound = true + } + } + if darkerFound && lighterFound { + break + } + } + + if darkerFound && lighterFound { + if math.Abs(darkerL-origL) <= math.Abs(lighterL-origL) { + return darkerResult + } + return lighterResult + } + if darkerFound { + return darkerResult + } + if lighterFound { + return lighterResult + } + return hexColor +} + +type PaletteOptions struct { + IsLight bool + Background string + UseDPS bool +} + +func ensureContrastAuto(hexColor, hexBg string, target float64, opts PaletteOptions) string { + if opts.UseDPS { + return EnsureContrastDPSLstar(hexColor, hexBg, target, opts.IsLight) + } + return EnsureContrast(hexColor, hexBg, target, opts.IsLight) +} + +func ensureContrastBidirectional(hexColor, hexBg string, target float64, opts PaletteOptions) string { + if opts.UseDPS { + return EnsureContrastDPSBidirectional(hexColor, hexBg, target, opts.IsLight) + } + return EnsureContrast(hexColor, hexBg, target, opts.IsLight) +} + +func blendHue(base, target, factor float64) float64 { + diff := target - base + if diff > 0.5 { + diff -= 1.0 + } else if diff < -0.5 { + diff += 1.0 + } + result := base + diff*factor + if result < 0 { + result += 1.0 + } else if result >= 1.0 { + result -= 1.0 + } + return result +} + +func DeriveContainer(primary string, isLight bool) string { + rgb := HexToRGB(primary) + hsv := RGBToHSV(rgb) + + if isLight { + containerV := math.Min(hsv.V*1.77, 1.0) + containerS := hsv.S * 0.32 + return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV})) + } + containerV := hsv.V * 0.463 + containerS := math.Min(hsv.S*1.834, 1.0) + return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV})) +} + +func GeneratePalette(primaryColor string, opts PaletteOptions) Palette { + baseColor := DeriveContainer(primaryColor, opts.IsLight) + + rgb := HexToRGB(baseColor) + hsv := RGBToHSV(rgb) + + pr := HexToRGB(primaryColor) + ph := RGBToHSV(pr) + + var palette Palette + + var normalTextTarget, secondaryTarget float64 + if opts.UseDPS { + normalTextTarget = 40.0 + secondaryTarget = 35.0 + } else { + normalTextTarget = 4.5 + secondaryTarget = 3.0 + } + + var bgColor string + if opts.Background != "" { + bgColor = opts.Background + } else if opts.IsLight { + bgColor = "#f8f8f8" + } else { + bgColor = "#1a1a1a" + } + palette.Color0 = NewColorInfo(bgColor) + + baseSat := math.Max(ph.S, 0.5) + baseVal := math.Max(ph.V, 0.5) + + redH := blendHue(0.0, ph.H, 0.12) + greenH := blendHue(0.33, ph.H, 0.10) + yellowH := blendHue(0.14, ph.H, 0.04) + + accentTarget := secondaryTarget * 0.7 + + if opts.IsLight { + redS := math.Min(baseSat*1.2, 1.0) + redV := baseVal * 0.95 + palette.Color1 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: redH, S: redS, V: redV})), bgColor, normalTextTarget, opts)) + + greenS := math.Min(baseSat*1.3, 1.0) + greenV := baseVal * 0.75 + palette.Color2 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: greenH, S: greenS, V: greenV})), bgColor, normalTextTarget, opts)) + + yellowS := math.Min(baseSat*1.5, 1.0) + yellowV := math.Min(baseVal*1.2, 1.0) + palette.Color3 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: yellowH, S: yellowS, V: yellowV})), bgColor, accentTarget, opts)) + + blueS := math.Min(ph.S*1.05, 1.0) + blueV := math.Min(ph.V*1.05, 1.0) + palette.Color4 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: ph.H, S: blueS, V: blueV})), bgColor, normalTextTarget, opts)) + + // Color5 matches primary_container exactly (light container in light mode) + container5 := DeriveContainer(primaryColor, true) + palette.Color5 = NewColorInfo(container5) + + palette.Color6 = NewColorInfo(primaryColor) + + gray7S := baseSat * 0.08 + gray7V := baseVal * 0.28 + palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts)) + + gray8S := baseSat * 0.05 + gray8V := baseVal * 0.85 + dimTarget := secondaryTarget * 0.5 + palette.Color8 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray8S, V: gray8V})), bgColor, dimTarget, opts)) + + brightRedS := math.Min(baseSat*1.0, 1.0) + brightRedV := math.Min(baseVal*1.2, 1.0) + palette.Color9 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: redH, S: brightRedS, V: brightRedV})), bgColor, accentTarget, opts)) + + brightGreenS := math.Min(baseSat*1.1, 1.0) + brightGreenV := math.Min(baseVal*1.1, 1.0) + palette.Color10 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: greenH, S: brightGreenS, V: brightGreenV})), bgColor, accentTarget, opts)) + + brightYellowS := math.Min(baseSat*1.4, 1.0) + brightYellowV := math.Min(baseVal*1.3, 1.0) + palette.Color11 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: yellowH, S: brightYellowS, V: brightYellowV})), bgColor, accentTarget, opts)) + + brightBlueS := math.Min(ph.S*1.1, 1.0) + brightBlueV := math.Min(ph.V*1.15, 1.0) + palette.Color12 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: ph.H, S: brightBlueS, V: brightBlueV})), bgColor, accentTarget, opts)) + + lightContainer := DeriveContainer(primaryColor, true) + palette.Color13 = NewColorInfo(lightContainer) + + brightCyanS := ph.S * 0.5 + brightCyanV := math.Min(ph.V*1.3, 1.0) + palette.Color14 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: ph.H, S: brightCyanS, V: brightCyanV}))) + + white15S := baseSat * 0.04 + white15V := math.Min(baseVal*1.5, 1.0) + palette.Color15 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: white15S, V: white15V}))) + } else { + redS := math.Min(baseSat*1.1, 1.0) + redV := math.Min(baseVal*1.15, 1.0) + palette.Color1 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: redH, S: redS, V: redV})), bgColor, normalTextTarget, opts)) + + greenS := math.Min(baseSat*1.0, 1.0) + greenV := math.Min(baseVal*1.0, 1.0) + palette.Color2 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: greenH, S: greenS, V: greenV})), bgColor, normalTextTarget, opts)) + + yellowS := math.Min(baseSat*1.1, 1.0) + yellowV := math.Min(baseVal*1.25, 1.0) + palette.Color3 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: yellowH, S: yellowS, V: yellowV})), bgColor, normalTextTarget, opts)) + + // Slightly more saturated variant of primary + blueS := math.Min(ph.S*1.2, 1.0) + blueV := ph.V * 0.95 + palette.Color4 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: ph.H, S: blueS, V: blueV})), bgColor, normalTextTarget, opts)) + + // Color5 matches primary_container exactly (dark container in dark mode) + darkContainer := DeriveContainer(primaryColor, false) + palette.Color5 = NewColorInfo(darkContainer) + + palette.Color6 = NewColorInfo(primaryColor) + + gray7S := baseSat * 0.12 + gray7V := math.Min(baseVal*1.05, 1.0) + palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts)) + + gray8S := baseSat * 0.15 + gray8V := baseVal * 0.65 + palette.Color8 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray8S, V: gray8V})), bgColor, secondaryTarget, opts)) + + brightRedS := math.Min(baseSat*0.75, 1.0) + brightRedV := math.Min(baseVal*1.35, 1.0) + palette.Color9 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: redH, S: brightRedS, V: brightRedV})), bgColor, accentTarget, opts)) + + brightGreenS := math.Min(baseSat*0.7, 1.0) + brightGreenV := math.Min(baseVal*1.2, 1.0) + palette.Color10 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: greenH, S: brightGreenS, V: brightGreenV})), bgColor, accentTarget, opts)) + + brightYellowS := math.Min(baseSat*0.7, 1.0) + brightYellowV := math.Min(baseVal*1.5, 1.0) + palette.Color11 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: yellowH, S: brightYellowS, V: brightYellowV})), bgColor, accentTarget, opts)) + + // Create a gradient of primary variants: Color12 -> Color13 -> Color14 -> Color15 (near white) + // Color12: Start of the lighter gradient - slightly desaturated + brightBlueS := ph.S * 0.85 + brightBlueV := math.Min(ph.V*1.1, 1.0) + palette.Color12 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: ph.H, S: brightBlueS, V: brightBlueV})), bgColor, accentTarget, opts)) + + // Medium-high saturation pastel primary + color13S := ph.S * 0.7 + color13V := math.Min(ph.V*1.3, 1.0) + palette.Color13 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: ph.H, S: color13S, V: color13V}))) + + // Lower saturation, lighter variant + color14S := ph.S * 0.45 + color14V := math.Min(ph.V*1.4, 1.0) + palette.Color14 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: ph.H, S: color14S, V: color14V}))) + + white15S := baseSat * 0.05 + white15V := math.Min(baseVal*1.45, 1.0) + palette.Color15 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: white15S, V: white15V})), bgColor, normalTextTarget, opts)) + } + + return palette +} + +type VariantOptions struct { + PrimaryDark string + PrimaryLight string + Background string + UseDPS bool + IsLightMode bool +} + +func mergeColorInfo(dark, light ColorInfo, isLightMode bool) VariantColorInfo { + darkVal := VariantColorValue{Hex: dark.Hex, HexStripped: dark.HexStripped} + lightVal := VariantColorValue{Hex: light.Hex, HexStripped: light.HexStripped} + + defaultVal := darkVal + if isLightMode { + defaultVal = lightVal + } + + return VariantColorInfo{ + Dark: darkVal, + Light: lightVal, + Default: defaultVal, + } +} + +func GenerateVariantPalette(opts VariantOptions) VariantPalette { + darkOpts := PaletteOptions{IsLight: false, Background: opts.Background, UseDPS: opts.UseDPS} + lightOpts := PaletteOptions{IsLight: true, Background: opts.Background, UseDPS: opts.UseDPS} + + dark := GeneratePalette(opts.PrimaryDark, darkOpts) + light := GeneratePalette(opts.PrimaryLight, lightOpts) + + return VariantPalette{ + Color0: mergeColorInfo(dark.Color0, light.Color0, opts.IsLightMode), + Color1: mergeColorInfo(dark.Color1, light.Color1, opts.IsLightMode), + Color2: mergeColorInfo(dark.Color2, light.Color2, opts.IsLightMode), + Color3: mergeColorInfo(dark.Color3, light.Color3, opts.IsLightMode), + Color4: mergeColorInfo(dark.Color4, light.Color4, opts.IsLightMode), + Color5: mergeColorInfo(dark.Color5, light.Color5, opts.IsLightMode), + Color6: mergeColorInfo(dark.Color6, light.Color6, opts.IsLightMode), + Color7: mergeColorInfo(dark.Color7, light.Color7, opts.IsLightMode), + Color8: mergeColorInfo(dark.Color8, light.Color8, opts.IsLightMode), + Color9: mergeColorInfo(dark.Color9, light.Color9, opts.IsLightMode), + Color10: mergeColorInfo(dark.Color10, light.Color10, opts.IsLightMode), + Color11: mergeColorInfo(dark.Color11, light.Color11, opts.IsLightMode), + Color12: mergeColorInfo(dark.Color12, light.Color12, opts.IsLightMode), + Color13: mergeColorInfo(dark.Color13, light.Color13, opts.IsLightMode), + Color14: mergeColorInfo(dark.Color14, light.Color14, opts.IsLightMode), + Color15: mergeColorInfo(dark.Color15, light.Color15, opts.IsLightMode), + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go new file mode 100644 index 0000000..9e6b5a8 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go @@ -0,0 +1,681 @@ +package dank16 + +import ( + "math" + "testing" +) + +func TestHexToRGB(t *testing.T) { + tests := []struct { + name string + input string + expected RGB + }{ + { + name: "black with hash", + input: "#000000", + expected: RGB{R: 0.0, G: 0.0, B: 0.0}, + }, + { + name: "white with hash", + input: "#ffffff", + expected: RGB{R: 1.0, G: 1.0, B: 1.0}, + }, + { + name: "red without hash", + input: "ff0000", + expected: RGB{R: 1.0, G: 0.0, B: 0.0}, + }, + { + name: "purple", + input: "#625690", + expected: RGB{R: 0.3843137254901961, G: 0.33725490196078434, B: 0.5647058823529412}, + }, + { + name: "mid gray", + input: "#808080", + expected: RGB{R: 0.5019607843137255, G: 0.5019607843137255, B: 0.5019607843137255}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := HexToRGB(tt.input) + if !floatEqual(result.R, tt.expected.R) || !floatEqual(result.G, tt.expected.G) || !floatEqual(result.B, tt.expected.B) { + t.Errorf("HexToRGB(%s) = %v, expected %v", tt.input, result, tt.expected) + } + }) + } +} + +func TestRGBToHex(t *testing.T) { + tests := []struct { + name string + input RGB + expected string + }{ + { + name: "black", + input: RGB{R: 0.0, G: 0.0, B: 0.0}, + expected: "#000000", + }, + { + name: "white", + input: RGB{R: 1.0, G: 1.0, B: 1.0}, + expected: "#ffffff", + }, + { + name: "red", + input: RGB{R: 1.0, G: 0.0, B: 0.0}, + expected: "#ff0000", + }, + { + name: "clamping above 1.0", + input: RGB{R: 1.5, G: 0.5, B: 0.5}, + expected: "#ff7f7f", + }, + { + name: "clamping below 0.0", + input: RGB{R: -0.5, G: 0.5, B: 0.5}, + expected: "#007f7f", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := RGBToHex(tt.input) + if result != tt.expected { + t.Errorf("RGBToHex(%v) = %s, expected %s", tt.input, result, tt.expected) + } + }) + } +} + +func TestRGBToHSV(t *testing.T) { + tests := []struct { + name string + input RGB + expected HSV + }{ + { + name: "black", + input: RGB{R: 0.0, G: 0.0, B: 0.0}, + expected: HSV{H: 0.0, S: 0.0, V: 0.0}, + }, + { + name: "white", + input: RGB{R: 1.0, G: 1.0, B: 1.0}, + expected: HSV{H: 0.0, S: 0.0, V: 1.0}, + }, + { + name: "red", + input: RGB{R: 1.0, G: 0.0, B: 0.0}, + expected: HSV{H: 0.0, S: 1.0, V: 1.0}, + }, + { + name: "green", + input: RGB{R: 0.0, G: 1.0, B: 0.0}, + expected: HSV{H: 0.3333333333333333, S: 1.0, V: 1.0}, + }, + { + name: "blue", + input: RGB{R: 0.0, G: 0.0, B: 1.0}, + expected: HSV{H: 0.6666666666666666, S: 1.0, V: 1.0}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := RGBToHSV(tt.input) + if !floatEqual(result.H, tt.expected.H) || !floatEqual(result.S, tt.expected.S) || !floatEqual(result.V, tt.expected.V) { + t.Errorf("RGBToHSV(%v) = %v, expected %v", tt.input, result, tt.expected) + } + }) + } +} + +func TestHSVToRGB(t *testing.T) { + tests := []struct { + name string + input HSV + expected RGB + }{ + { + name: "black", + input: HSV{H: 0.0, S: 0.0, V: 0.0}, + expected: RGB{R: 0.0, G: 0.0, B: 0.0}, + }, + { + name: "white", + input: HSV{H: 0.0, S: 0.0, V: 1.0}, + expected: RGB{R: 1.0, G: 1.0, B: 1.0}, + }, + { + name: "red", + input: HSV{H: 0.0, S: 1.0, V: 1.0}, + expected: RGB{R: 1.0, G: 0.0, B: 0.0}, + }, + { + name: "green", + input: HSV{H: 0.3333333333333333, S: 1.0, V: 1.0}, + expected: RGB{R: 0.0, G: 1.0, B: 0.0}, + }, + { + name: "blue", + input: HSV{H: 0.6666666666666666, S: 1.0, V: 1.0}, + expected: RGB{R: 0.0, G: 0.0, B: 1.0}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := HSVToRGB(tt.input) + if !floatEqual(result.R, tt.expected.R) || !floatEqual(result.G, tt.expected.G) || !floatEqual(result.B, tt.expected.B) { + t.Errorf("HSVToRGB(%v) = %v, expected %v", tt.input, result, tt.expected) + } + }) + } +} + +func TestLuminance(t *testing.T) { + tests := []struct { + name string + input string + expected float64 + }{ + { + name: "black", + input: "#000000", + expected: 0.0, + }, + { + name: "white", + input: "#ffffff", + expected: 1.0, + }, + { + name: "red", + input: "#ff0000", + expected: 0.2126, + }, + { + name: "green", + input: "#00ff00", + expected: 0.7152, + }, + { + name: "blue", + input: "#0000ff", + expected: 0.0722, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := Luminance(tt.input) + if !floatEqual(result, tt.expected) { + t.Errorf("Luminance(%s) = %f, expected %f", tt.input, result, tt.expected) + } + }) + } +} + +func TestContrastRatio(t *testing.T) { + tests := []struct { + name string + fg string + bg string + expected float64 + }{ + { + name: "black on white", + fg: "#000000", + bg: "#ffffff", + expected: 21.0, + }, + { + name: "white on black", + fg: "#ffffff", + bg: "#000000", + expected: 21.0, + }, + { + name: "same color", + fg: "#808080", + bg: "#808080", + expected: 1.0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ContrastRatio(tt.fg, tt.bg) + if !floatEqual(result, tt.expected) { + t.Errorf("ContrastRatio(%s, %s) = %f, expected %f", tt.fg, tt.bg, result, tt.expected) + } + }) + } +} + +func TestEnsureContrast(t *testing.T) { + tests := []struct { + name string + color string + bg string + minRatio float64 + isLightMode bool + }{ + { + name: "already sufficient contrast dark mode", + color: "#ffffff", + bg: "#000000", + minRatio: 4.5, + isLightMode: false, + }, + { + name: "already sufficient contrast light mode", + color: "#000000", + bg: "#ffffff", + minRatio: 4.5, + isLightMode: true, + }, + { + name: "needs adjustment dark mode", + color: "#404040", + bg: "#1a1a1a", + minRatio: 4.5, + isLightMode: false, + }, + { + name: "needs adjustment light mode", + color: "#c0c0c0", + bg: "#f8f8f8", + minRatio: 4.5, + isLightMode: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := EnsureContrast(tt.color, tt.bg, tt.minRatio, tt.isLightMode) + actualRatio := ContrastRatio(result, tt.bg) + if actualRatio < tt.minRatio { + t.Errorf("EnsureContrast(%s, %s, %f, %t) = %s with ratio %f, expected ratio >= %f", + tt.color, tt.bg, tt.minRatio, tt.isLightMode, result, actualRatio, tt.minRatio) + } + }) + } +} + +func TestGeneratePalette(t *testing.T) { + tests := []struct { + name string + base string + opts PaletteOptions + }{ + { + name: "dark theme default", + base: "#625690", + opts: PaletteOptions{IsLight: false}, + }, + { + name: "light theme default", + base: "#625690", + opts: PaletteOptions{IsLight: true}, + }, + { + name: "light theme with custom background", + base: "#625690", + opts: PaletteOptions{ + IsLight: true, + Background: "#fafafa", + }, + }, + { + name: "dark theme with custom background", + base: "#625690", + opts: PaletteOptions{ + IsLight: false, + Background: "#0a0a0a", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := GeneratePalette(tt.base, tt.opts) + + colors := []ColorInfo{ + result.Color0, result.Color1, result.Color2, result.Color3, + result.Color4, result.Color5, result.Color6, result.Color7, + result.Color8, result.Color9, result.Color10, result.Color11, + result.Color12, result.Color13, result.Color14, result.Color15, + } + + for i, color := range colors { + if len(color.Hex) != 7 || color.Hex[0] != '#' { + t.Errorf("Color at index %d (%s) is not a valid hex color", i, color.Hex) + } + } + + if tt.opts.Background != "" && result.Color0.Hex != tt.opts.Background { + t.Errorf("Background color = %s, expected %s", result.Color0.Hex, tt.opts.Background) + } else if !tt.opts.IsLight && tt.opts.Background == "" && result.Color0.Hex != "#1a1a1a" { + t.Errorf("Dark mode background = %s, expected #1a1a1a", result.Color0.Hex) + } else if tt.opts.IsLight && tt.opts.Background == "" && result.Color0.Hex != "#f8f8f8" { + t.Errorf("Light mode background = %s, expected #f8f8f8", result.Color0.Hex) + } + + // Color15 is now derived from primary, so just verify it's a valid color + // and has appropriate luminance for the mode (now theme-tinted, not pure white/black) + color15Lum := Luminance(result.Color15.Hex) + if tt.opts.IsLight { + // Light mode: Color15 should still be relatively light + if color15Lum < 0.5 { + t.Errorf("Light mode Color15 = %s (lum %.2f) is too dark", result.Color15.Hex, color15Lum) + } + } else { + // Dark mode: Color15 should be light (but may have theme tint, so lower threshold) + if color15Lum < 0.5 { + t.Errorf("Dark mode Color15 = %s (lum %.2f) is too dark", result.Color15.Hex, color15Lum) + } + } + }) + } +} + +func TestRoundTripConversion(t *testing.T) { + testColors := []string{"#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#625690", "#808080"} + + for _, hex := range testColors { + t.Run(hex, func(t *testing.T) { + rgb := HexToRGB(hex) + result := RGBToHex(rgb) + if result != hex { + t.Errorf("Round trip %s -> RGB -> %s failed", hex, result) + } + }) + } +} + +func TestRGBHSVRoundTrip(t *testing.T) { + testCases := []RGB{ + {R: 0.0, G: 0.0, B: 0.0}, + {R: 1.0, G: 1.0, B: 1.0}, + {R: 1.0, G: 0.0, B: 0.0}, + {R: 0.0, G: 1.0, B: 0.0}, + {R: 0.0, G: 0.0, B: 1.0}, + {R: 0.5, G: 0.5, B: 0.5}, + {R: 0.3843137254901961, G: 0.33725490196078434, B: 0.5647058823529412}, + } + + for _, rgb := range testCases { + t.Run("", func(t *testing.T) { + hsv := RGBToHSV(rgb) + result := HSVToRGB(hsv) + if !floatEqual(result.R, rgb.R) || !floatEqual(result.G, rgb.G) || !floatEqual(result.B, rgb.B) { + t.Errorf("Round trip RGB->HSV->RGB failed: %v -> %v -> %v", rgb, hsv, result) + } + }) + } +} + +func floatEqual(a, b float64) bool { + return math.Abs(a-b) < 1e-9 +} + +func TestDeltaPhiStar(t *testing.T) { + tests := []struct { + name string + fg string + bg string + negativePolarity bool + minExpected float64 + }{ + { + name: "white on black (negative polarity)", + fg: "#ffffff", + bg: "#000000", + negativePolarity: true, + minExpected: 100.0, + }, + { + name: "black on white (positive polarity)", + fg: "#000000", + bg: "#ffffff", + negativePolarity: false, + minExpected: 100.0, + }, + { + name: "low contrast same color", + fg: "#808080", + bg: "#808080", + negativePolarity: false, + minExpected: -40.0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := DeltaPhiStar(tt.fg, tt.bg, tt.negativePolarity) + if result < tt.minExpected { + t.Errorf("DeltaPhiStar(%s, %s, %v) = %f, expected >= %f", + tt.fg, tt.bg, tt.negativePolarity, result, tt.minExpected) + } + }) + } +} + +func TestDeltaPhiStarContrast(t *testing.T) { + tests := []struct { + name string + fg string + bg string + isLightMode bool + minExpected float64 + }{ + { + name: "white on black (dark mode)", + fg: "#ffffff", + bg: "#000000", + isLightMode: false, + minExpected: 100.0, + }, + { + name: "black on white (light mode)", + fg: "#000000", + bg: "#ffffff", + isLightMode: true, + minExpected: 100.0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := DeltaPhiStarContrast(tt.fg, tt.bg, tt.isLightMode) + if result < tt.minExpected { + t.Errorf("DeltaPhiStarContrast(%s, %s, %v) = %f, expected >= %f", + tt.fg, tt.bg, tt.isLightMode, result, tt.minExpected) + } + }) + } +} + +func TestEnsureContrastDPS(t *testing.T) { + tests := []struct { + name string + color string + bg string + minLc float64 + isLightMode bool + }{ + { + name: "already sufficient contrast dark mode", + color: "#ffffff", + bg: "#000000", + minLc: 60.0, + isLightMode: false, + }, + { + name: "already sufficient contrast light mode", + color: "#000000", + bg: "#ffffff", + minLc: 60.0, + isLightMode: true, + }, + { + name: "needs adjustment dark mode", + color: "#404040", + bg: "#1a1a1a", + minLc: 60.0, + isLightMode: false, + }, + { + name: "needs adjustment light mode", + color: "#c0c0c0", + bg: "#f8f8f8", + minLc: 60.0, + isLightMode: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := EnsureContrastDPS(tt.color, tt.bg, tt.minLc, tt.isLightMode) + actualLc := DeltaPhiStarContrast(result, tt.bg, tt.isLightMode) + if actualLc < tt.minLc { + t.Errorf("EnsureContrastDPS(%s, %s, %f, %t) = %s with Lc %f, expected Lc >= %f", + tt.color, tt.bg, tt.minLc, tt.isLightMode, result, actualLc, tt.minLc) + } + }) + } +} + +func TestGeneratePaletteWithDPS(t *testing.T) { + tests := []struct { + name string + base string + opts PaletteOptions + }{ + { + name: "dark theme with DPS", + base: "#625690", + opts: PaletteOptions{IsLight: false, UseDPS: true}, + }, + { + name: "light theme with DPS", + base: "#625690", + opts: PaletteOptions{IsLight: true, UseDPS: true}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := GeneratePalette(tt.base, tt.opts) + + colors := []ColorInfo{ + result.Color0, result.Color1, result.Color2, result.Color3, + result.Color4, result.Color5, result.Color6, result.Color7, + result.Color8, result.Color9, result.Color10, result.Color11, + result.Color12, result.Color13, result.Color14, result.Color15, + } + + for i, color := range colors { + if len(color.Hex) != 7 || color.Hex[0] != '#' { + t.Errorf("Color at index %d (%s) is not a valid hex color", i, color.Hex) + } + } + + bgColor := result.Color0.Hex + for i := 1; i < 8; i++ { + // Skip Color5 (container) and Color6 (exact primary) - intentionally not contrast-adjusted + if i == 5 || i == 6 { + continue + } + lc := DeltaPhiStarContrast(colors[i].Hex, bgColor, tt.opts.IsLight) + minLc := 30.0 + if lc < minLc && lc > 0 { + t.Errorf("Color %d (%s) has insufficient DPS contrast %f with background %s (expected >= %f)", + i, colors[i].Hex, lc, bgColor, minLc) + } + } + }) + } +} + +func TestDeriveContainer(t *testing.T) { + tests := []struct { + name string + primary string + isLight bool + expected string + }{ + { + name: "dark mode", + primary: "#ccbdff", + isLight: false, + expected: "#4a3e76", + }, + { + name: "light mode", + primary: "#625690", + isLight: true, + expected: "#e7deff", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := DeriveContainer(tt.primary, tt.isLight) + + resultRGB := HexToRGB(result) + expectedRGB := HexToRGB(tt.expected) + + rDiff := math.Abs(resultRGB.R - expectedRGB.R) + gDiff := math.Abs(resultRGB.G - expectedRGB.G) + bDiff := math.Abs(resultRGB.B - expectedRGB.B) + + tolerance := 0.02 + if rDiff > tolerance || gDiff > tolerance || bDiff > tolerance { + t.Errorf("DeriveContainer(%s, %v) = %s, expected %s (RGB diff: R:%.4f G:%.4f B:%.4f)", + tt.primary, tt.isLight, result, tt.expected, rDiff, gDiff, bDiff) + } + }) + } +} + +func TestContrastAlgorithmComparison(t *testing.T) { + base := "#625690" + + optsWCAG := PaletteOptions{IsLight: false, UseDPS: false} + optsDPS := PaletteOptions{IsLight: false, UseDPS: true} + + paletteWCAG := GeneratePalette(base, optsWCAG) + paletteDPS := GeneratePalette(base, optsDPS) + + wcagColors := []ColorInfo{ + paletteWCAG.Color0, paletteWCAG.Color1, paletteWCAG.Color2, paletteWCAG.Color3, + paletteWCAG.Color4, paletteWCAG.Color5, paletteWCAG.Color6, paletteWCAG.Color7, + paletteWCAG.Color8, paletteWCAG.Color9, paletteWCAG.Color10, paletteWCAG.Color11, + paletteWCAG.Color12, paletteWCAG.Color13, paletteWCAG.Color14, paletteWCAG.Color15, + } + dpsColors := []ColorInfo{ + paletteDPS.Color0, paletteDPS.Color1, paletteDPS.Color2, paletteDPS.Color3, + paletteDPS.Color4, paletteDPS.Color5, paletteDPS.Color6, paletteDPS.Color7, + paletteDPS.Color8, paletteDPS.Color9, paletteDPS.Color10, paletteDPS.Color11, + paletteDPS.Color12, paletteDPS.Color13, paletteDPS.Color14, paletteDPS.Color15, + } + + if paletteWCAG.Color0.Hex != paletteDPS.Color0.Hex { + t.Errorf("Background colors differ: WCAG=%s, DPS=%s", paletteWCAG.Color0.Hex, paletteDPS.Color0.Hex) + } + + differentCount := 0 + for i := range 16 { + if wcagColors[i].Hex != dpsColors[i].Hex { + differentCount++ + } + } + + t.Logf("WCAG and DPS palettes differ in %d/16 colors", differentCount) +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go new file mode 100644 index 0000000..9423311 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go @@ -0,0 +1,135 @@ +package dank16 + +import ( + "encoding/json" + "fmt" + "strings" +) + +func GenerateJSON(p Palette) string { + marshalled, _ := json.Marshal(p) + return string(marshalled) +} + +func GenerateVariantJSON(p VariantPalette) string { + marshalled, _ := json.Marshal(p) + return string(marshalled) +} + +func GenerateKittyTheme(p Palette) string { + var result strings.Builder + fmt.Fprintf(&result, "color0 %s\n", p.Color0.Hex) + fmt.Fprintf(&result, "color1 %s\n", p.Color1.Hex) + fmt.Fprintf(&result, "color2 %s\n", p.Color2.Hex) + fmt.Fprintf(&result, "color3 %s\n", p.Color3.Hex) + fmt.Fprintf(&result, "color4 %s\n", p.Color4.Hex) + fmt.Fprintf(&result, "color5 %s\n", p.Color5.Hex) + fmt.Fprintf(&result, "color6 %s\n", p.Color6.Hex) + fmt.Fprintf(&result, "color7 %s\n", p.Color7.Hex) + fmt.Fprintf(&result, "color8 %s\n", p.Color8.Hex) + fmt.Fprintf(&result, "color9 %s\n", p.Color9.Hex) + fmt.Fprintf(&result, "color10 %s\n", p.Color10.Hex) + fmt.Fprintf(&result, "color11 %s\n", p.Color11.Hex) + fmt.Fprintf(&result, "color12 %s\n", p.Color12.Hex) + fmt.Fprintf(&result, "color13 %s\n", p.Color13.Hex) + fmt.Fprintf(&result, "color14 %s\n", p.Color14.Hex) + fmt.Fprintf(&result, "color15 %s\n", p.Color15.Hex) + return result.String() +} + +func GenerateFootTheme(p Palette) string { + var result strings.Builder + fmt.Fprintf(&result, "regular0=%s\n", p.Color0.HexStripped) + fmt.Fprintf(&result, "regular1=%s\n", p.Color1.HexStripped) + fmt.Fprintf(&result, "regular2=%s\n", p.Color2.HexStripped) + fmt.Fprintf(&result, "regular3=%s\n", p.Color3.HexStripped) + fmt.Fprintf(&result, "regular4=%s\n", p.Color4.HexStripped) + fmt.Fprintf(&result, "regular5=%s\n", p.Color5.HexStripped) + fmt.Fprintf(&result, "regular6=%s\n", p.Color6.HexStripped) + fmt.Fprintf(&result, "regular7=%s\n", p.Color7.HexStripped) + fmt.Fprintf(&result, "bright0=%s\n", p.Color8.HexStripped) + fmt.Fprintf(&result, "bright1=%s\n", p.Color9.HexStripped) + fmt.Fprintf(&result, "bright2=%s\n", p.Color10.HexStripped) + fmt.Fprintf(&result, "bright3=%s\n", p.Color11.HexStripped) + fmt.Fprintf(&result, "bright4=%s\n", p.Color12.HexStripped) + fmt.Fprintf(&result, "bright5=%s\n", p.Color13.HexStripped) + fmt.Fprintf(&result, "bright6=%s\n", p.Color14.HexStripped) + fmt.Fprintf(&result, "bright7=%s\n", p.Color15.HexStripped) + return result.String() +} + +func GenerateAlacrittyTheme(p Palette) string { + var result strings.Builder + result.WriteString("[colors.normal]\n") + fmt.Fprintf(&result, "black = '%s'\n", p.Color0.Hex) + fmt.Fprintf(&result, "red = '%s'\n", p.Color1.Hex) + fmt.Fprintf(&result, "green = '%s'\n", p.Color2.Hex) + fmt.Fprintf(&result, "yellow = '%s'\n", p.Color3.Hex) + fmt.Fprintf(&result, "blue = '%s'\n", p.Color4.Hex) + fmt.Fprintf(&result, "magenta = '%s'\n", p.Color5.Hex) + fmt.Fprintf(&result, "cyan = '%s'\n", p.Color6.Hex) + fmt.Fprintf(&result, "white = '%s'\n", p.Color7.Hex) + result.WriteString("\n[colors.bright]\n") + fmt.Fprintf(&result, "black = '%s'\n", p.Color8.Hex) + fmt.Fprintf(&result, "red = '%s'\n", p.Color9.Hex) + fmt.Fprintf(&result, "green = '%s'\n", p.Color10.Hex) + fmt.Fprintf(&result, "yellow = '%s'\n", p.Color11.Hex) + fmt.Fprintf(&result, "blue = '%s'\n", p.Color12.Hex) + fmt.Fprintf(&result, "magenta = '%s'\n", p.Color13.Hex) + fmt.Fprintf(&result, "cyan = '%s'\n", p.Color14.Hex) + fmt.Fprintf(&result, "white = '%s'\n", p.Color15.Hex) + return result.String() +} + +func GenerateGhosttyTheme(p Palette) string { + var result strings.Builder + fmt.Fprintf(&result, "palette = 0=%s\n", p.Color0.Hex) + fmt.Fprintf(&result, "palette = 1=%s\n", p.Color1.Hex) + fmt.Fprintf(&result, "palette = 2=%s\n", p.Color2.Hex) + fmt.Fprintf(&result, "palette = 3=%s\n", p.Color3.Hex) + fmt.Fprintf(&result, "palette = 4=%s\n", p.Color4.Hex) + fmt.Fprintf(&result, "palette = 5=%s\n", p.Color5.Hex) + fmt.Fprintf(&result, "palette = 6=%s\n", p.Color6.Hex) + fmt.Fprintf(&result, "palette = 7=%s\n", p.Color7.Hex) + fmt.Fprintf(&result, "palette = 8=%s\n", p.Color8.Hex) + fmt.Fprintf(&result, "palette = 9=%s\n", p.Color9.Hex) + fmt.Fprintf(&result, "palette = 10=%s\n", p.Color10.Hex) + fmt.Fprintf(&result, "palette = 11=%s\n", p.Color11.Hex) + fmt.Fprintf(&result, "palette = 12=%s\n", p.Color12.Hex) + fmt.Fprintf(&result, "palette = 13=%s\n", p.Color13.Hex) + fmt.Fprintf(&result, "palette = 14=%s\n", p.Color14.Hex) + fmt.Fprintf(&result, "palette = 15=%s\n", p.Color15.Hex) + return result.String() +} + +func GenerateWeztermTheme(p Palette) string { + var result strings.Builder + fmt.Fprintf(&result, "ansi = ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']\n", + p.Color0.Hex, p.Color1.Hex, p.Color2.Hex, p.Color3.Hex, + p.Color4.Hex, p.Color5.Hex, p.Color6.Hex, p.Color7.Hex) + fmt.Fprintf(&result, "brights = ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']\n", + p.Color8.Hex, p.Color9.Hex, p.Color10.Hex, p.Color11.Hex, + p.Color12.Hex, p.Color13.Hex, p.Color14.Hex, p.Color15.Hex) + return result.String() +} + +func GenerateNeovimTheme(p Palette) string { + var result strings.Builder + fmt.Fprintf(&result, "vim.g.terminal_color_0 = \"%s\"\n", p.Color0.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_1 = \"%s\"\n", p.Color1.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_2 = \"%s\"\n", p.Color2.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_3 = \"%s\"\n", p.Color3.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_4 = \"%s\"\n", p.Color4.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_5 = \"%s\"\n", p.Color5.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_6 = \"%s\"\n", p.Color6.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_7 = \"%s\"\n", p.Color7.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_8 = \"%s\"\n", p.Color8.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_9 = \"%s\"\n", p.Color9.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_10 = \"%s\"\n", p.Color10.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_11 = \"%s\"\n", p.Color11.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_12 = \"%s\"\n", p.Color12.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_13 = \"%s\"\n", p.Color13.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_14 = \"%s\"\n", p.Color14.Hex) + fmt.Fprintf(&result, "vim.g.terminal_color_15 = \"%s\"\n", p.Color15.Hex) + return result.String() +} |