From d71c372080e4008551db79f4d92a2fbdfcde19cc Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Sun, 16 Mar 2025 12:59:37 +0100 Subject: [PATCH] Dynamic theme loading Signed-off-by: Minecon724 --- modules/theme/theme.go | 80 +++++++++++++++++++++++++++++ routers/web/user/setting/profile.go | 10 ++++ 2 files changed, 90 insertions(+) create mode 100644 modules/theme/theme.go diff --git a/modules/theme/theme.go b/modules/theme/theme.go new file mode 100644 index 0000000000..762926d78f --- /dev/null +++ b/modules/theme/theme.go @@ -0,0 +1,80 @@ +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") + } +} diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 271621872f..1dee0d641f 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/theme" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" @@ -327,6 +328,15 @@ func Repos(ctx *context.Context) { // Appearance render user's appearance settings func Appearance(ctx *context.Context) { + err := theme.LoadThemes() + + if err != nil { + ctx.ServerError("Failed to load themes", err) + return + } + + ctx.Data["AllThemes"] = setting.UI.Themes + ctx.Data["Title"] = ctx.Tr("settings.appearance") ctx.Data["PageIsSettingsAppearance"] = true ctx.Data["AllThemes"] = setting.UI.Themes