diff --git a/README.md b/README.md index b31cffb..3874fdd 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Configure OMP to use `tinty-current` where required. ### Conjure -The `apply-apps` flow writes `$XDG_CONFIG_HOME/conjure/theme.json`, falling back to `~/.config/conjure/theme.json` when `XDG_CONFIG_HOME` is unset, empty, or relative. The generated JSON contains seven opaque colors: `background`, `text`, `secondaryText`, `accent`, `glow`, `keyBackground`, and `error`, using Tinty semantic UI colors with Base16/Base24 or tinted8 palette fallbacks. +The `apply-apps` flow writes `$XDG_CONFIG_HOME/conjure/theme.json`, falling back to `~/.config/conjure/theme.json` when `XDG_CONFIG_HOME` is unset, empty, or relative. The generated JSON contains the selected scheme's `variant` (`light` or `dark`) and seven opaque colors: `background`, `text`, `secondaryText`, `accent`, `glow`, `keyBackground`, and `error`, using Tinty semantic UI colors with Base16/Base24 or tinted8 palette fallbacks. Conjure uses the variant to match its native window appearance to the palette without an additional user setting. Opt in by adding this to `conjure/config.toml` in the same configuration directory: diff --git a/apps.go b/apps.go index ccb1e82..58e4b8e 100644 --- a/apps.go +++ b/apps.go @@ -40,7 +40,7 @@ func applyApps(zedSource string) error { if err := writeHerdrTheme(colors, ui, syntax); err != nil { return err } - if err := writeConjureTheme(colors, ui); err != nil { + if err := writeConjureTheme(scheme.Variant, colors, ui); err != nil { return err } return writeZedTheme(zedSource) @@ -189,7 +189,10 @@ func semanticTheme(colors, ui, syntax map[string]string) (map[string]string, map return omp, herdr } -func writeConjureTheme(colors, ui map[string]string) error { +func writeConjureTheme(variant string, colors, ui map[string]string) error { + if variant != "light" && variant != "dark" { + return fmt.Errorf("invalid Conjure variant: %q", variant) + } mapping := map[string]string{ "background": pick(ui, "global.background.normal", colors, "base00"), "text": pick(ui, "global.foreground.normal", colors, "base05"), @@ -209,7 +212,7 @@ func writeConjureTheme(colors, ui map[string]string) error { if !filepath.IsAbs(configHome) { configHome = filepath.Join(homeDir(), ".config") } - return writeJSON(filepath.Join(configHome, "conjure", "theme.json"), map[string]any{"colors": mapping}) + return writeJSON(filepath.Join(configHome, "conjure", "theme.json"), map[string]any{"variant": variant, "colors": mapping}) } func writeOMPTheme(colors, ui, syntax map[string]string) error { diff --git a/main_test.go b/main_test.go index 0b06df0..4020ab3 100644 --- a/main_test.go +++ b/main_test.go @@ -138,6 +138,7 @@ func TestApplyAppsUsesTinted8Semantics(t *testing.T) { "black-normal": {Hex: "#000000"}, "black-bright": {Hex: "#111111"}, "gray-dim": {Hex: "#222222"}, "gray-normal": {Hex: "#333333"}, "gray-bright": {Hex: "#444444"}, "white-normal": {Hex: "#eeeeee"}, "white-bright": {Hex: "#ffffff"}, + "black-dim": {Hex: "#111111"}, "white-dim": {Hex: "#dddddd"}, "red-normal": {Hex: "#aa0000"}, "orange-normal": {Hex: "#bb5500"}, "yellow-normal": {Hex: "#aaaa00"}, "green-normal": {Hex: "#00aa00"}, "cyan-normal": {Hex: "#00aaaa"}, "blue-normal": {Hex: "#0000aa"}, @@ -211,11 +212,15 @@ esac t.Fatalf("Zed theme was not renamed:\n%s", zedOutput) } var conjure struct { - Colors map[string]string `json:"colors"` + Variant string `json:"variant"` + Colors map[string]string `json:"colors"` } if err := json.Unmarshal([]byte(readFile(t, filepath.Join(filepath.Dir(conjureConfig), "theme.json"))), &conjure); err != nil { t.Fatal(err) } + if conjure.Variant != "dark" { + t.Fatalf("Conjure variant = %q, want dark", conjure.Variant) + } expected := map[string]string{ "background": "#010101", "text": "#fefefe", "secondaryText": "#444444", "accent": "#123456", "glow": "#aa0000", "keyBackground": "#111111", "error": "#aa0000", @@ -231,6 +236,33 @@ esac if readFile(t, conjureConfig) != userConfig { t.Fatal("Conjure user configuration changed") } + scheme.Variant = "light" + expected["secondaryText"] = "#222222" + expected["keyBackground"] = "#dddddd" + data, err = json.Marshal([]tintyScheme{scheme}) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(listPath, data, 0o644); err != nil { + t.Fatal(err) + } + if err := applyApps(zed); err != nil { + t.Fatal(err) + } + if err := json.Unmarshal([]byte(readFile(t, filepath.Join(filepath.Dir(conjureConfig), "theme.json"))), &conjure); err != nil { + t.Fatal(err) + } + if conjure.Variant != "light" { + t.Fatalf("Conjure variant = %q, want light", conjure.Variant) + } + for key, value := range expected { + if conjure.Colors[key] != value { + t.Errorf("light Conjure %s = %q, want %q", key, conjure.Colors[key], value) + } + } + if readFile(t, conjureConfig) != userConfig { + t.Fatal("Conjure user configuration changed") + } } func TestBase16SemanticFallback(t *testing.T) { @@ -277,7 +309,7 @@ func TestConjureThemeFallbacks(t *testing.T) { if err != nil { t.Fatal(err) } - if err := writeConjureTheme(colors, nil); err != nil { + if err := writeConjureTheme("dark", colors, nil); err != nil { t.Fatal(err) } var theme struct { @@ -308,13 +340,29 @@ func TestConjureThemeRejectsInvalidColors(t *testing.T) { "base00": "#000000", "base01": "#111111", "base04": "#444444", "base05": "#555555", "base08": "#880000", "base0D": value, } - if err := writeConjureTheme(colors, nil); err == nil { + if err := writeConjureTheme("dark", colors, nil); err == nil { t.Fatalf("accepted invalid accent %q", value) } }) } } +func TestConjureThemeRejectsInvalidVariants(t *testing.T) { + t.Setenv("HOME", t.TempDir()) + t.Setenv("XDG_CONFIG_HOME", "") + colors := map[string]string{ + "base00": "#000000", "base01": "#111111", "base04": "#444444", + "base05": "#555555", "base08": "#880000", "base0D": "#0000dd", + } + for _, variant := range []string{"", "auto", "Light", "DARK"} { + t.Run(variant, func(t *testing.T) { + if err := writeConjureTheme(variant, colors, nil); err == nil { + t.Fatalf("accepted invalid variant %q", variant) + } + }) + } +} + func writeExecutable(t *testing.T, path, content string) { t.Helper() if err := os.WriteFile(path, []byte(content), 0o755); err != nil {