@@ -20,21 +20,55 @@ type Theme struct {
2020 IsAetherGenerated bool `json:"isAetherGenerated"`
2121}
2222
23- // LoadAllThemes discovers themes from user and system directories.
24- func LoadAllThemes () ([]Theme , error ) {
23+ // themeSearchDirs returns the user and system-wide directories where
24+ // omarchy looks up themes, in precedence order.
25+ func themeSearchDirs () []string {
2526 home , err := os .UserHomeDir ()
2627 if err != nil {
27- return nil , err
28+ return nil
29+ }
30+ return []string {
31+ filepath .Join (home , ".config" , "omarchy" , "themes" ),
32+ filepath .Join (home , ".local" , "share" , "omarchy" , "themes" ),
33+ }
34+ }
35+
36+ func isImageFile (name string ) bool {
37+ switch strings .ToLower (filepath .Ext (name )) {
38+ case ".jpg" , ".jpeg" , ".png" , ".webp" :
39+ return true
40+ }
41+ return false
42+ }
43+
44+ // listBackgrounds returns absolute paths of image files in dir, sorted
45+ // by name (os.ReadDir's default).
46+ func listBackgrounds (dir string ) []string {
47+ entries , err := os .ReadDir (dir )
48+ if err != nil {
49+ return nil
2850 }
51+ var paths []string
52+ for _ , e := range entries {
53+ if isImageFile (e .Name ()) {
54+ paths = append (paths , filepath .Join (dir , e .Name ()))
55+ }
56+ }
57+ return paths
58+ }
2959
30- userDir := filepath .Join (home , ".config" , "omarchy" , "themes" )
31- sysDir := filepath .Join (home , ".local" , "share" , "omarchy" , "themes" )
60+ // LoadAllThemes discovers themes from user and system directories.
61+ func LoadAllThemes () ([]Theme , error ) {
62+ dirs := themeSearchDirs ()
63+ if dirs == nil {
64+ return nil , os .ErrNotExist
65+ }
3266 currentName := GetCurrentThemeName ()
3367
3468 seen := make (map [string ]bool )
3569 var themes []Theme
3670
37- for _ , dir := range [] string { userDir , sysDir } {
71+ for _ , dir := range dirs {
3872 entries , err := os .ReadDir (dir )
3973 if err != nil {
4074 continue
@@ -59,41 +93,25 @@ func LoadAllThemes() ([]Theme, error) {
5993 IsCurrentTheme : name == currentName ,
6094 }
6195
62- // Check for symlink ( ReadDir doesn't always report it)
63- if target , err := os .Readlink (themePath ); err == nil {
96+ // ReadDir doesn't always flag symlinks on the DirEntry.
97+ if _ , err := os .Readlink (themePath ); err == nil {
6498 theme .IsSymlink = true
65- _ = target
6699 }
67100
68- // Try colors.toml first
69- tomlPath := filepath .Join (themePath , "colors.toml" )
70- if data , err := os .ReadFile (tomlPath ); err == nil {
101+ if data , err := os .ReadFile (filepath .Join (themePath , "colors.toml" )); err == nil {
71102 colors , bg , fg := ParseColorsToml (string (data ))
72103 theme .Colors = colors [:]
73104 theme .Background = bg
74105 theme .Foreground = fg
75106 theme .IsAetherGenerated = true
76- } else {
77- // Fall back to kitty.conf
78- kittyPath := filepath .Join (themePath , "kitty.conf" )
79- if data , err := os .ReadFile (kittyPath ); err == nil {
80- colors , bg , fg := ParseKittyConf (string (data ))
81- theme .Colors = colors [:]
82- theme .Background = bg
83- theme .Foreground = fg
84- }
107+ } else if data , err := os .ReadFile (filepath .Join (themePath , "kitty.conf" )); err == nil {
108+ colors , bg , fg := ParseKittyConf (string (data ))
109+ theme .Colors = colors [:]
110+ theme .Background = bg
111+ theme .Foreground = fg
85112 }
86113
87- // Scan wallpapers
88- bgDir := filepath .Join (themePath , "backgrounds" )
89- if entries , err := os .ReadDir (bgDir ); err == nil {
90- for _ , e := range entries {
91- ext := strings .ToLower (filepath .Ext (e .Name ()))
92- if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".webp" {
93- theme .Wallpapers = append (theme .Wallpapers , filepath .Join (bgDir , e .Name ()))
94- }
95- }
96- }
114+ theme .Wallpapers = listBackgrounds (filepath .Join (themePath , "backgrounds" ))
97115
98116 themes = append (themes , theme )
99117 }
@@ -107,40 +125,21 @@ func LoadAllThemes() ([]Theme, error) {
107125}
108126
109127// TokyoNightDefaults loads the tokyo-night palette and its first
110- // wallpaper from a local omarchy install, for use as Aether's
111- // out-of-the-box defaults. Returns ok=false when the theme isn't
112- // present (e.g. standalone installs without omarchy).
113- func TokyoNightDefaults () (palette [16 ]string , bg , fg , wallpaper string , ok bool ) {
114- home , err := os .UserHomeDir ()
115- if err != nil {
116- return
117- }
118- candidates := []string {
119- filepath .Join (home , ".config" , "omarchy" , "themes" , "tokyo-night" ),
120- filepath .Join (home , ".local" , "share" , "omarchy" , "themes" , "tokyo-night" ),
121- }
122-
123- for _ , themeDir := range candidates {
128+ // wallpaper (the "0-" file, by omarchy's naming convention) from a
129+ // local omarchy install. Returns ok=false on standalone systems where
130+ // the theme isn't present.
131+ func TokyoNightDefaults () (palette [16 ]string , wallpaper string , ok bool ) {
132+ for _ , root := range themeSearchDirs () {
133+ themeDir := filepath .Join (root , "tokyo-night" )
124134 data , err := os .ReadFile (filepath .Join (themeDir , "colors.toml" ))
125135 if err != nil {
126136 continue
127137 }
128- palette , bg , fg = ParseColorsToml (string (data ))
129-
130- // os.ReadDir returns entries sorted by filename, so the first
131- // image is tokyo-night's "0-*" wallpaper by convention.
132- if entries , err := os .ReadDir (filepath .Join (themeDir , "backgrounds" )); err == nil {
133- for _ , e := range entries {
134- ext := strings .ToLower (filepath .Ext (e .Name ()))
135- if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".webp" {
136- wallpaper = filepath .Join (themeDir , "backgrounds" , e .Name ())
137- break
138- }
139- }
138+ palette , _ , _ = ParseColorsToml (string (data ))
139+ if bgs := listBackgrounds (filepath .Join (themeDir , "backgrounds" )); len (bgs ) > 0 {
140+ wallpaper = bgs [0 ]
140141 }
141-
142- ok = true
143- return
142+ return palette , wallpaper , true
144143 }
145144 return
146145}
0 commit comments