package theme import ( "fmt" "strings" "code.gitea.io/gitea/modules/assetfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/setting" "golang.org/x/sync/singleflight" ) const ( key string = "load-themes" ) var ( group singleflight.Group assetFs *assetfs.LayeredFS loaded bool ) // LoadThemes loads installed themes // // Note that you can't just run this during init, because webpack mightn't have completed yet. // Hence, we're loading on first demand. func LoadThemes() error { if loaded { return nil } if assetFs == nil { assetFs = public.AssetFS() } _, err, _ := group.Do(key, func() (interface{}, error) { themes, err := loadThemesInner(assetFs) if err != nil { group.Forget(key) } else { setting.UI.Themes = themes loaded = true } return nil, err }) return err } func loadThemesInner(assetFs *assetfs.LayeredFS) ([]string, error) { entries, err := assetFs.ListFiles("assets/css") if err != nil { return nil, err } var themes []string for _, entry := range entries { if !(strings.HasPrefix(entry, "theme-") && strings.HasSuffix(entry, ".css")) { continue } theme := entry[6 : len(entry)-4] themes = append(themes, theme) log.Info("Found theme: %s", theme) } if len(themes) > 0 { log.Info("Loaded %d themes", len(themes)) return themes, nil } else { return nil, fmt.Errorf("no themes found") } } // GetFriendlyThemeName converts an raw theme name (forgejo-dark) to a friendly name (Forgejo Dark) // // Example: forgejo-dark -> Forgejo Dark, catppuccin-maroon-auto -> Catppuccin Maroon Auto func GetFriendlyThemeName(themeName string) string { themeName = strings.ReplaceAll(themeName, "-", " ") themeName = strings.ToLower(themeName) themeName = strings.Title(themeName) return themeName }