From e80381234460f712ba129c802c120d103e67519a Mon Sep 17 00:00:00 2001 From: purian23 Date: Tue, 17 Mar 2026 13:27:57 -0400 Subject: [PATCH] theme(greeter): fix auto theme accent variants & update selections --- core/cmd/dms/commands_greeter.go | 22 +- core/internal/greeter/installer.go | 257 +++++++++++++++++++++--- core/internal/greeter/installer_test.go | 98 +++++++++ core/internal/matugen/matugen.go | 13 +- core/internal/matugen/matugen_test.go | 49 +++++ quickshell/Common/Theme.qml | 20 +- 6 files changed, 425 insertions(+), 34 deletions(-) create mode 100644 core/internal/greeter/installer_test.go diff --git a/core/cmd/dms/commands_greeter.go b/core/cmd/dms/commands_greeter.go index eb4ababc..c1982bfb 100644 --- a/core/cmd/dms/commands_greeter.go +++ b/core/cmd/dms/commands_greeter.go @@ -1497,6 +1497,20 @@ func checkGreeterStatus() error { } fmt.Println("\nConfiguration Symlinks:") + colorSyncInfo, colorSyncErr := greeter.ResolveGreeterColorSyncInfo(homeDir) + if colorSyncErr != nil { + fmt.Printf(" ✗ Failed to resolve expected greeter color source: %v\n", colorSyncErr) + allGood = false + colorSyncInfo = greeter.GreeterColorSyncInfo{ + SourcePath: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"), + } + } + + colorThemeDesc := "Color theme" + if colorSyncInfo.UsesDynamicWallpaperOverride { + colorThemeDesc = "Color theme (greeter wallpaper override)" + } + symlinks := []struct { source string target string @@ -1513,9 +1527,9 @@ func checkGreeterStatus() error { desc: "Session state", }, { - source: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"), + source: colorSyncInfo.SourcePath, target: filepath.Join(cacheDir, "colors.json"), - desc: "Color theme", + desc: colorThemeDesc, }, } @@ -1557,6 +1571,10 @@ func checkGreeterStatus() error { fmt.Printf(" ✓ %s: synced correctly\n", link.desc) } + if colorSyncInfo.UsesDynamicWallpaperOverride { + fmt.Printf(" ℹ Dynamic theme uses greeter override colors from %s\n", colorSyncInfo.SourcePath) + } + fmt.Println("\nGreeter Wallpaper Override:") overridePath := filepath.Join(cacheDir, "greeter_wallpaper_override.jpg") if stat, err := os.Stat(overridePath); err == nil && !stat.IsDir() { diff --git a/core/internal/greeter/installer.go b/core/internal/greeter/installer.go index 58bf9959..26f47f00 100644 --- a/core/internal/greeter/installer.go +++ b/core/internal/greeter/installer.go @@ -5,6 +5,7 @@ import ( "context" _ "embed" "encoding/json" + "errors" "fmt" "os" "os/exec" @@ -14,6 +15,7 @@ import ( "github.com/AvengeMedia/DankMaterialShell/core/internal/config" "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" + "github.com/AvengeMedia/DankMaterialShell/core/internal/matugen" "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" "github.com/sblinch/kdl-go" "github.com/sblinch/kdl-go/document" @@ -1075,6 +1077,7 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error { }{ {filepath.Join(homeDir, ".config", "DankMaterialShell"), "DankMaterialShell config"}, {filepath.Join(homeDir, ".local", "state", "DankMaterialShell"), "DankMaterialShell state"}, + {filepath.Join(homeDir, ".cache", "DankMaterialShell"), "DankMaterialShell cache"}, {filepath.Join(homeDir, ".cache", "quickshell"), "quickshell cache"}, {filepath.Join(homeDir, ".config", "quickshell"), "quickshell config"}, {filepath.Join(homeDir, ".local", "share", "wayland-sessions"), "wayland sessions"}, @@ -1109,6 +1112,217 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error { return nil } +type GreeterColorSyncInfo struct { + SourcePath string + ThemeName string + UsesDynamicWallpaperOverride bool +} + +type greeterThemeSyncSettings struct { + CurrentThemeName string `json:"currentThemeName"` + GreeterWallpaperPath string `json:"greeterWallpaperPath"` + MatugenScheme string `json:"matugenScheme"` + IconTheme string `json:"iconTheme"` +} + +type greeterThemeSyncSession struct { + IsLightMode bool `json:"isLightMode"` +} + +type greeterThemeSyncState struct { + ThemeName string + GreeterWallpaperPath string + ResolvedGreeterWallpaperPath string + MatugenScheme string + IconTheme string + IsLightMode bool + UsesDynamicWallpaperOverride bool +} + +func defaultGreeterColorsSource(homeDir string) string { + return filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json") +} + +func greeterOverrideColorsStateDir(homeDir string) string { + return filepath.Join(homeDir, ".cache", "DankMaterialShell", "greeter-colors") +} + +func greeterOverrideColorsSource(homeDir string) string { + return filepath.Join(greeterOverrideColorsStateDir(homeDir), "dms-colors.json") +} + +func readOptionalJSONFile(path string, dst any) error { + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + if strings.TrimSpace(string(data)) == "" { + return nil + } + return json.Unmarshal(data, dst) +} + +func readGreeterThemeSyncSettings(homeDir string) (greeterThemeSyncSettings, error) { + settings := greeterThemeSyncSettings{ + CurrentThemeName: "purple", + MatugenScheme: "scheme-tonal-spot", + IconTheme: "System Default", + } + settingsPath := filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json") + if err := readOptionalJSONFile(settingsPath, &settings); err != nil { + return greeterThemeSyncSettings{}, fmt.Errorf("failed to parse settings at %s: %w", settingsPath, err) + } + return settings, nil +} + +func readGreeterThemeSyncSession(homeDir string) (greeterThemeSyncSession, error) { + session := greeterThemeSyncSession{} + sessionPath := filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json") + if err := readOptionalJSONFile(sessionPath, &session); err != nil { + return greeterThemeSyncSession{}, fmt.Errorf("failed to parse session at %s: %w", sessionPath, err) + } + return session, nil +} + +func resolveGreeterThemeSyncState(homeDir string) (greeterThemeSyncState, error) { + settings, err := readGreeterThemeSyncSettings(homeDir) + if err != nil { + return greeterThemeSyncState{}, err + } + session, err := readGreeterThemeSyncSession(homeDir) + if err != nil { + return greeterThemeSyncState{}, err + } + + resolvedWallpaperPath := "" + if settings.GreeterWallpaperPath != "" { + resolvedWallpaperPath = settings.GreeterWallpaperPath + if !filepath.IsAbs(resolvedWallpaperPath) { + resolvedWallpaperPath = filepath.Join(homeDir, resolvedWallpaperPath) + } + } + + usesDynamicWallpaperOverride := strings.EqualFold(strings.TrimSpace(settings.CurrentThemeName), "dynamic") && resolvedWallpaperPath != "" + + return greeterThemeSyncState{ + ThemeName: settings.CurrentThemeName, + GreeterWallpaperPath: settings.GreeterWallpaperPath, + ResolvedGreeterWallpaperPath: resolvedWallpaperPath, + MatugenScheme: settings.MatugenScheme, + IconTheme: settings.IconTheme, + IsLightMode: session.IsLightMode, + UsesDynamicWallpaperOverride: usesDynamicWallpaperOverride, + }, nil +} + +func (s greeterThemeSyncState) effectiveColorsSource(homeDir string) string { + if s.UsesDynamicWallpaperOverride { + return greeterOverrideColorsSource(homeDir) + } + return defaultGreeterColorsSource(homeDir) +} + +func ResolveGreeterColorSyncInfo(homeDir string) (GreeterColorSyncInfo, error) { + state, err := resolveGreeterThemeSyncState(homeDir) + if err != nil { + return GreeterColorSyncInfo{}, err + } + return GreeterColorSyncInfo{ + SourcePath: state.effectiveColorsSource(homeDir), + ThemeName: state.ThemeName, + UsesDynamicWallpaperOverride: state.UsesDynamicWallpaperOverride, + }, nil +} + +func ensureGreeterSyncSourceFile(path string) error { + sourceDir := filepath.Dir(path) + if err := os.MkdirAll(sourceDir, 0o755); err != nil { + return fmt.Errorf("failed to create source directory %s: %w", sourceDir, err) + } + + if _, err := os.Stat(path); os.IsNotExist(err) { + if err := os.WriteFile(path, []byte("{}"), 0o644); err != nil { + return fmt.Errorf("failed to create source file %s: %w", path, err) + } + } else if err != nil { + return fmt.Errorf("failed to inspect source file %s: %w", path, err) + } + + return nil +} + +func syncGreeterDynamicOverrideColors(dmsPath, homeDir string, state greeterThemeSyncState, logFunc func(string)) error { + if !state.UsesDynamicWallpaperOverride { + return nil + } + + st, err := os.Stat(state.ResolvedGreeterWallpaperPath) + if err != nil { + return fmt.Errorf("configured greeter wallpaper not found at %s: %w", state.ResolvedGreeterWallpaperPath, err) + } + if st.IsDir() { + return fmt.Errorf("configured greeter wallpaper path points to a directory: %s", state.ResolvedGreeterWallpaperPath) + } + + mode := matugen.ColorModeDark + if state.IsLightMode { + mode = matugen.ColorModeLight + } + + opts := matugen.Options{ + StateDir: greeterOverrideColorsStateDir(homeDir), + ShellDir: dmsPath, + ConfigDir: filepath.Join(homeDir, ".config"), + Kind: "image", + Value: state.ResolvedGreeterWallpaperPath, + Mode: mode, + IconTheme: state.IconTheme, + MatugenType: state.MatugenScheme, + RunUserTemplates: false, + ColorsOnly: true, + } + + err = matugen.Run(opts) + switch { + case errors.Is(err, matugen.ErrNoChanges): + logFunc("✓ Greeter dynamic override colors already up to date") + return nil + case err != nil: + return fmt.Errorf("failed to generate greeter dynamic colors from wallpaper override: %w", err) + default: + logFunc("✓ Generated greeter dynamic colors from wallpaper override") + return nil + } +} + +func syncGreeterColorSource(homeDir, cacheDir string, state greeterThemeSyncState, logFunc func(string), sudoPassword string) error { + source := state.effectiveColorsSource(homeDir) + if !state.UsesDynamicWallpaperOverride { + if err := ensureGreeterSyncSourceFile(source); err != nil { + return err + } + } else if _, err := os.Stat(source); err != nil { + return fmt.Errorf("expected generated greeter colors at %s: %w", source, err) + } + + target := filepath.Join(cacheDir, "colors.json") + _ = runSudoCmd(sudoPassword, "rm", "-f", target) + if err := runSudoCmd(sudoPassword, "ln", "-sf", source, target); err != nil { + return fmt.Errorf("failed to create symlink for wallpaper based theming (%s -> %s): %w", target, source, err) + } + + if state.UsesDynamicWallpaperOverride { + logFunc("✓ Synced wallpaper based theming (greeter wallpaper override)") + } else { + logFunc("✓ Synced wallpaper based theming") + } + + return nil +} + func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPassword string, forceAuth bool) error { homeDir, err := os.UserHomeDir() if err != nil { @@ -1132,11 +1346,6 @@ func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPasswo target: filepath.Join(cacheDir, "session.json"), desc: "state (wallpaper configuration)", }, - { - source: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"), - target: filepath.Join(cacheDir, "colors.json"), - desc: "wallpaper based theming", - }, } for _, link := range symlinks { @@ -1162,7 +1371,20 @@ func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPasswo logFunc(fmt.Sprintf("✓ Synced %s", link.desc)) } - if err := syncGreeterWallpaperOverride(homeDir, cacheDir, logFunc, sudoPassword); err != nil { + state, err := resolveGreeterThemeSyncState(homeDir) + if err != nil { + return fmt.Errorf("failed to resolve greeter color source: %w", err) + } + + if err := syncGreeterDynamicOverrideColors(dmsPath, homeDir, state, logFunc); err != nil { + return err + } + + if err := syncGreeterColorSource(homeDir, cacheDir, state, logFunc, sudoPassword); err != nil { + return err + } + + if err := syncGreeterWallpaperOverride(cacheDir, logFunc, sudoPassword, state); err != nil { return fmt.Errorf("greeter wallpaper override sync failed: %w", err) } @@ -1181,23 +1403,9 @@ func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPasswo return nil } -func syncGreeterWallpaperOverride(homeDir, cacheDir string, logFunc func(string), sudoPassword string) error { - settingsPath := filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json") - data, err := os.ReadFile(settingsPath) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return fmt.Errorf("failed to read settings at %s: %w", settingsPath, err) - } - var settings struct { - GreeterWallpaperPath string `json:"greeterWallpaperPath"` - } - if err := json.Unmarshal(data, &settings); err != nil { - return fmt.Errorf("failed to parse settings at %s: %w", settingsPath, err) - } +func syncGreeterWallpaperOverride(cacheDir string, logFunc func(string), sudoPassword string, state greeterThemeSyncState) error { destPath := filepath.Join(cacheDir, "greeter_wallpaper_override.jpg") - if settings.GreeterWallpaperPath == "" { + if state.ResolvedGreeterWallpaperPath == "" { if err := runSudoCmd(sudoPassword, "rm", "-f", destPath); err != nil { return fmt.Errorf("failed to clear override file %s: %w", destPath, err) } @@ -1207,10 +1415,7 @@ func syncGreeterWallpaperOverride(homeDir, cacheDir string, logFunc func(string) if err := runSudoCmd(sudoPassword, "rm", "-f", destPath); err != nil { return fmt.Errorf("failed to remove old override file %s: %w", destPath, err) } - src := settings.GreeterWallpaperPath - if !filepath.IsAbs(src) { - src = filepath.Join(homeDir, src) - } + src := state.ResolvedGreeterWallpaperPath st, err := os.Stat(src) if err != nil { return fmt.Errorf("configured greeter wallpaper not found at %s: %w", src, err) diff --git a/core/internal/greeter/installer_test.go b/core/internal/greeter/installer_test.go new file mode 100644 index 00000000..12f139e0 --- /dev/null +++ b/core/internal/greeter/installer_test.go @@ -0,0 +1,98 @@ +package greeter + +import ( + "os" + "path/filepath" + "testing" +) + +func writeTestJSON(t *testing.T, path string, content string) { + t.Helper() + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + t.Fatalf("failed to create parent dir for %s: %v", path, err) + } + if err := os.WriteFile(path, []byte(content), 0o644); err != nil { + t.Fatalf("failed to write %s: %v", path, err) + } +} + +func TestResolveGreeterThemeSyncState(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + settingsJSON string + sessionJSON string + wantSourcePath string + wantResolvedWallpaper string + wantDynamicOverrideUsed bool + }{ + { + name: "dynamic theme with greeter wallpaper override uses generated greeter colors", + settingsJSON: `{ + "currentThemeName": "dynamic", + "greeterWallpaperPath": "Pictures/blue.jpg", + "matugenScheme": "scheme-tonal-spot", + "iconTheme": "Papirus" +}`, + sessionJSON: `{"isLightMode":true}`, + wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "greeter-colors", "dms-colors.json"), + wantResolvedWallpaper: filepath.Join("Pictures", "blue.jpg"), + wantDynamicOverrideUsed: true, + }, + { + name: "dynamic theme without override uses desktop colors", + settingsJSON: `{ + "currentThemeName": "dynamic", + "greeterWallpaperPath": "" +}`, + sessionJSON: `{"isLightMode":false}`, + wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "dms-colors.json"), + wantResolvedWallpaper: "", + wantDynamicOverrideUsed: false, + }, + { + name: "non-dynamic theme keeps desktop colors even with override wallpaper", + settingsJSON: `{ + "currentThemeName": "purple", + "greeterWallpaperPath": "/tmp/blue.jpg" +}`, + sessionJSON: `{"isLightMode":false}`, + wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "dms-colors.json"), + wantResolvedWallpaper: "/tmp/blue.jpg", + wantDynamicOverrideUsed: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + homeDir := t.TempDir() + writeTestJSON(t, filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), tt.settingsJSON) + writeTestJSON(t, filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), tt.sessionJSON) + + state, err := resolveGreeterThemeSyncState(homeDir) + if err != nil { + t.Fatalf("resolveGreeterThemeSyncState returned error: %v", err) + } + + if got := state.effectiveColorsSource(homeDir); got != filepath.Join(homeDir, tt.wantSourcePath) { + t.Fatalf("effectiveColorsSource = %q, want %q", got, filepath.Join(homeDir, tt.wantSourcePath)) + } + + wantResolvedWallpaper := tt.wantResolvedWallpaper + if wantResolvedWallpaper != "" && !filepath.IsAbs(wantResolvedWallpaper) { + wantResolvedWallpaper = filepath.Join(homeDir, wantResolvedWallpaper) + } + if state.ResolvedGreeterWallpaperPath != wantResolvedWallpaper { + t.Fatalf("ResolvedGreeterWallpaperPath = %q, want %q", state.ResolvedGreeterWallpaperPath, wantResolvedWallpaper) + } + + if state.UsesDynamicWallpaperOverride != tt.wantDynamicOverrideUsed { + t.Fatalf("UsesDynamicWallpaperOverride = %v, want %v", state.UsesDynamicWallpaperOverride, tt.wantDynamicOverrideUsed) + } + }) + } +} diff --git a/core/internal/matugen/matugen.go b/core/internal/matugen/matugen.go index 7bb2b04c..0dbc9b93 100644 --- a/core/internal/matugen/matugen.go +++ b/core/internal/matugen/matugen.go @@ -100,6 +100,7 @@ type Options struct { IconTheme string MatugenType string RunUserTemplates bool + ColorsOnly bool StockColors string SyncModeWithPortal bool TerminalsAlwaysDark bool @@ -274,6 +275,10 @@ func buildOnce(opts *Options) (bool, error) { return false, nil } + if opts.ColorsOnly { + return true, nil + } + if isDMSGTKActive(opts.ConfigDir) { switch opts.Mode { case ColorModeLight: @@ -331,6 +336,10 @@ output_path = '%s' `, opts.ShellDir, opts.ColorsOutput()) + if opts.ColorsOnly { + return nil + } + homeDir, _ := os.UserHomeDir() for _, tmpl := range templateRegistry { if opts.ShouldSkipTemplate(tmpl.ID) { @@ -597,10 +606,10 @@ func detectMatugenVersionLocked() (matugenFlags, error) { matugenVersionOK = true if matugenSupportsCOE { - log.Infof("Matugen %s supports --continue-on-error", versionStr) + log.Debugf("Matugen %s detected: continue-on-error support enabled", versionStr) } if matugenIsV4 { - log.Infof("Matugen %s: using v4 flags", versionStr) + log.Debugf("Matugen %s detected: using v4 compatibility flags", versionStr) } return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil } diff --git a/core/internal/matugen/matugen_test.go b/core/internal/matugen/matugen_test.go index 231c4df3..48880df2 100644 --- a/core/internal/matugen/matugen_test.go +++ b/core/internal/matugen/matugen_test.go @@ -3,6 +3,7 @@ package matugen import ( "os" "path/filepath" + "strings" "testing" mocks_utils "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/utils" @@ -392,3 +393,51 @@ func TestSubstituteVars(t *testing.T) { }) } } + +func TestBuildMergedConfigColorsOnly(t *testing.T) { + tempDir := t.TempDir() + + shellDir := filepath.Join(tempDir, "shell") + configsDir := filepath.Join(shellDir, "matugen", "configs") + if err := os.MkdirAll(configsDir, 0o755); err != nil { + t.Fatalf("failed to create configs dir: %v", err) + } + + baseConfig := "[config]\ncustom_keywords = []\n" + if err := os.WriteFile(filepath.Join(configsDir, "base.toml"), []byte(baseConfig), 0o644); err != nil { + t.Fatalf("failed to write base config: %v", err) + } + + cfgFile, err := os.CreateTemp(tempDir, "merged-*.toml") + if err != nil { + t.Fatalf("failed to create temp config: %v", err) + } + defer os.Remove(cfgFile.Name()) + defer cfgFile.Close() + + opts := &Options{ + ShellDir: shellDir, + ConfigDir: filepath.Join(tempDir, "config"), + StateDir: filepath.Join(tempDir, "state"), + ColorsOnly: true, + } + + if err := buildMergedConfig(opts, cfgFile, filepath.Join(tempDir, "templates")); err != nil { + t.Fatalf("buildMergedConfig failed: %v", err) + } + + if err := cfgFile.Close(); err != nil { + t.Fatalf("failed to close merged config: %v", err) + } + + output, err := os.ReadFile(cfgFile.Name()) + if err != nil { + t.Fatalf("failed to read merged config: %v", err) + } + + content := string(output) + assert.Contains(t, content, "[templates.dank]") + assert.Contains(t, content, "output_path = '"+filepath.Join(opts.StateDir, "dms-colors.json")+"'") + assert.NotContains(t, content, "[templates.gtk]") + assert.False(t, strings.Contains(content, "output_path = 'CONFIG_DIR/"), "colors-only config should not emit app template outputs") +} diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index 6f5a4843..7441f191 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -1052,7 +1052,10 @@ Singleton { } if (themeData.variants.options && themeData.variants.options.length > 0) { - const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default; + const isGreeterMode = typeof SessionData !== "undefined" && SessionData.isGreeterMode; + const selectedVariantId = isGreeterMode + ? (typeof GreetdSettings.registryThemeVariants[themeId] === "string" ? GreetdSettings.registryThemeVariants[themeId] : themeData.variants.default) + : (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default); const variant = findVariant(themeData.variants.options, selectedVariantId); if (variant) { const variantColors = variant[colorMode] || variant.dark || variant.light || {}; @@ -1424,8 +1427,13 @@ Singleton { const defaults = customThemeRawData.variants.defaults || {}; const darkDefaults = defaults.dark || {}; const lightDefaults = defaults.light || defaults.dark || {}; - const storedDark = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, darkDefaults, "dark") : darkDefaults; - const storedLight = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, lightDefaults, "light") : lightDefaults; + const isGreeterMode = typeof SessionData !== "undefined" && SessionData.isGreeterMode; + const storedDark = isGreeterMode + ? (GreetdSettings.registryThemeVariants[themeId]?.dark || darkDefaults) + : (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, darkDefaults, "dark") : darkDefaults); + const storedLight = isGreeterMode + ? (GreetdSettings.registryThemeVariants[themeId]?.light || lightDefaults) + : (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, lightDefaults, "light") : lightDefaults); const darkFlavorId = storedDark.flavor || darkDefaults.flavor || ""; const lightFlavorId = storedLight.flavor || lightDefaults.flavor || ""; const accentId = storedDark.accent || darkDefaults.accent || ""; @@ -1443,7 +1451,10 @@ Singleton { lightTheme = mergeColors(lightTheme, accent[lightFlavor.id] || {}); } } else if (customThemeRawData.variants.options) { - const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default; + const isGreeterMode = typeof SessionData !== "undefined" && SessionData.isGreeterMode; + const selectedVariantId = isGreeterMode + ? (typeof GreetdSettings.registryThemeVariants[themeId] === "string" ? GreetdSettings.registryThemeVariants[themeId] : customThemeRawData.variants.default) + : (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default); const variant = findVariant(customThemeRawData.variants.options, selectedVariantId); if (variant) { darkTheme = mergeColors(darkTheme, variant.dark || {}); @@ -1771,6 +1782,7 @@ Singleton { const colorsPath = SessionData.isGreeterMode ? greetCfgDir + "/colors.json" : stateDir + "/dms-colors.json"; return colorsPath; } + blockLoading: false watchChanges: !SessionData.isGreeterMode function parseAndLoadColors() {