mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 19:38:23 +00:00 
			
		
		
		
	Initial support for colorblindness-friendly themes (#30625)
Initial support for #25680 This PR only adds some simple styles from GitHub, it is big enough and it focuses on adding the necessary framework-level supports. More styles could be fine-tuned later.
This commit is contained in:
		| @@ -1231,7 +1231,8 @@ LEVEL = Info | ||||
| ;DEFAULT_THEME = gitea-auto | ||||
| ;; | ||||
| ;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`. | ||||
| ;THEMES = gitea-auto,gitea-light,gitea-dark | ||||
| ;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css" | ||||
| ;THEMES = | ||||
| ;; | ||||
| ;; All available reactions users can choose on issues/prs and comments. | ||||
| ;; Values can be emoji alias (:smile:) or a unicode emoji. | ||||
|   | ||||
| @@ -214,10 +214,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a | ||||
| - `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap. | ||||
| - `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph. | ||||
| - `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment. | ||||
| - `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: Set the default theme for the Gitea installation. | ||||
| - `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by "{CustomPath}/public/assets/css/theme-*.css". | ||||
| - `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page. | ||||
| - `THEMES`:  **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes. | ||||
|   regardless of the value of `DEFAULT_THEME`. | ||||
| - `THEMES`: **_empty_**: All available themes by "{CustomPath}/public/assets/css/theme-*.css". Allow users select personalized themes. | ||||
| - `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB) | ||||
| - `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI | ||||
| - `REACTIONS`: All available reactions users can choose on issues/prs and comments | ||||
|   | ||||
| @@ -212,10 +212,9 @@ menu: | ||||
| - `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。 | ||||
| - `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。 | ||||
| - `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。 | ||||
| - `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: 在Gitea安装时候设置的默认主题。 | ||||
| - `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 "{CustomPath}/public/assets/css/theme-*.css" 提供。 | ||||
| - `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。 | ||||
| - `THEMES`:  **gitea-auto,gitea-light,gitea-dark**: 所有可用的主题。允许用户选择个性化的主题, | ||||
|   而不受DEFAULT_THEME 值的影响。 | ||||
| - `THEMES`:  **_empty_**: 所有可用的主题(由 "{CustomPath}/public/assets/css/theme-*.css" 提供)。允许用户选择个性化的主题, | ||||
| - `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。 | ||||
| - `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。 | ||||
|     这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。 | ||||
|   | ||||
| @@ -381,7 +381,7 @@ To make a custom theme available to all users: | ||||
|  | ||||
| 1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`. | ||||
|   The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath". | ||||
| 2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini` | ||||
| 2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`, or leave `THEMES` empty to allow all themes. | ||||
|  | ||||
| Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes). | ||||
|  | ||||
|   | ||||
| @@ -178,17 +178,6 @@ At some point, a customer or third party needs access to a specific repo and onl | ||||
|  | ||||
| Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns | ||||
|  | ||||
| ## How to add/use custom themes | ||||
|  | ||||
| Gitea supports three official themes right now, `gitea-light`, `gitea-dark`, and `gitea-auto` (automatically switches between the previous two depending on operating system settings). | ||||
| To add your own theme, currently the only way is to provide a complete theme (not just color overrides) | ||||
|  | ||||
| As an example, let's say our theme is `arc-blue` (this is a real theme, and can be found [in this issue](https://github.com/go-gitea/gitea/issues/6011)) | ||||
|  | ||||
| Name the `.css` file `theme-arc-blue.css` and add it to your custom folder in `custom/public/assets/css` | ||||
|  | ||||
| Allow users to use it by adding `arc-blue` to the list of `THEMES` in your `app.ini` | ||||
|  | ||||
| ## SSHD vs built-in SSH | ||||
|  | ||||
| SSHD is the built-in SSH server on most Unix systems. | ||||
|   | ||||
| @@ -182,17 +182,6 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供 | ||||
|  | ||||
| 使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。 | ||||
|  | ||||
| ## 如何添加/使用自定义主题 | ||||
|  | ||||
| Gitea 目前支持三个官方主题,分别是 `gitea-light`、`gitea-dark` 和 `gitea-auto`(根据操作系统设置自动切换前两个主题)。 | ||||
| 要添加自己的主题,目前唯一的方法是提供一个完整的主题(不仅仅是颜色覆盖)。 | ||||
|  | ||||
| 假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到) | ||||
|  | ||||
| 将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中 | ||||
|  | ||||
| 通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题 | ||||
|  | ||||
| ## SSHD vs 内建SSH | ||||
|  | ||||
| SSHD是大多数Unix系统上内建的SSH服务器。 | ||||
|   | ||||
| @@ -318,7 +318,7 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) { | ||||
| // StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc | ||||
| var StartupProblems []string | ||||
|  | ||||
| func logStartupProblem(skip int, level log.Level, format string, args ...any) { | ||||
| func LogStartupProblem(skip int, level log.Level, format string, args ...any) { | ||||
| 	msg := fmt.Sprintf(format, args...) | ||||
| 	log.Log(skip+1, level, "%s", msg) | ||||
| 	StartupProblems = append(StartupProblems, msg) | ||||
| @@ -326,14 +326,14 @@ func logStartupProblem(skip int, level log.Level, format string, args ...any) { | ||||
|  | ||||
| func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) { | ||||
| 	if rootCfg.Section(oldSection).HasKey(oldKey) { | ||||
| 		logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version) | ||||
| 		LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini | ||||
| func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { | ||||
| 	if rootCfg.Section(oldSection).HasKey(oldKey) { | ||||
| 		logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey) | ||||
| 		LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -174,7 +174,7 @@ func GetGeneralTokenSigningSecret() []byte { | ||||
| 		} | ||||
| 		if generalSigningSecret.CompareAndSwap(old, &jwtSecret) { | ||||
| 			// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...) | ||||
| 			logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes") | ||||
| 			LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes") | ||||
| 			return jwtSecret | ||||
| 		} | ||||
| 		return *generalSigningSecret.Load() | ||||
|   | ||||
| @@ -235,7 +235,7 @@ var configuredPaths = make(map[string]string) | ||||
| func checkOverlappedPath(name, path string) { | ||||
| 	// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path) | ||||
| 	if targetName, ok := configuredPaths[path]; ok && targetName != name { | ||||
| 		logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name) | ||||
| 		LogStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name) | ||||
| 	} | ||||
| 	configuredPaths[path] = name | ||||
| } | ||||
|   | ||||
| @@ -82,7 +82,6 @@ var UI = struct { | ||||
| 	ReactionMaxUserNum:      10, | ||||
| 	MaxDisplayFileSize:      8388608, | ||||
| 	DefaultTheme:            `gitea-auto`, | ||||
| 	Themes:                  []string{`gitea-auto`, `gitea-light`, `gitea-dark`}, | ||||
| 	Reactions:               []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, | ||||
| 	CustomEmojis:            []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, | ||||
| 	CustomEmojisMap:         map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/services/gitdiff" | ||||
| 	"code.gitea.io/gitea/services/webtheme" | ||||
| ) | ||||
|  | ||||
| // NewFuncMap returns functions for injecting to templates | ||||
| @@ -137,12 +138,7 @@ func NewFuncMap() template.FuncMap { | ||||
| 		"DisableImportLocal": func() bool { | ||||
| 			return !setting.ImportLocalPaths | ||||
| 		}, | ||||
| 		"ThemeName": func(user *user_model.User) string { | ||||
| 			if user == nil || user.Theme == "" { | ||||
| 				return setting.UI.DefaultTheme | ||||
| 			} | ||||
| 			return user.Theme | ||||
| 		}, | ||||
| 		"UserThemeName": UserThemeName, | ||||
| 		"NotificationSettings": func() map[string]any { | ||||
| 			return map[string]any{ | ||||
| 				"MinTimeout":            int(setting.UI.Notification.MinTimeout / time.Millisecond), | ||||
| @@ -261,3 +257,13 @@ func Eval(tokens ...any) (any, error) { | ||||
| 	n, err := eval.Expr(tokens...) | ||||
| 	return n.Value, err | ||||
| } | ||||
|  | ||||
| func UserThemeName(user *user_model.User) string { | ||||
| 	if user == nil || user.Theme == "" { | ||||
| 		return setting.UI.DefaultTheme | ||||
| 	} | ||||
| 	if webtheme.IsThemeAvailable(user.Theme) { | ||||
| 		return user.Theme | ||||
| 	} | ||||
| 	return setting.UI.DefaultTheme | ||||
| } | ||||
|   | ||||
| @@ -763,6 +763,8 @@ manage_themes = Select default theme | ||||
| manage_openid = Manage OpenID Addresses | ||||
| email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations. | ||||
| theme_desc = This will be your default theme across the site. | ||||
| theme_colorblindness_help = Colorblindness Theme Support | ||||
| theme_colorblindness_prompt = Gitea just gets some themes with basic colorblindness support, which only have a few colors defined. The work is still in progress. More improvements could be done by defining more colors in the theme CSS files. | ||||
| primary = Primary | ||||
| activated = Activated | ||||
| requires_activation = Requires activation | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/services/context" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| 	user_service "code.gitea.io/gitea/services/user" | ||||
| 	"code.gitea.io/gitea/services/webtheme" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -319,6 +320,13 @@ func Appearance(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings.appearance") | ||||
| 	ctx.Data["PageIsSettingsAppearance"] = true | ||||
|  | ||||
| 	allThemes := webtheme.GetAvailableThemes() | ||||
| 	if webtheme.IsThemeAvailable(setting.UI.DefaultTheme) { | ||||
| 		allThemes = util.SliceRemoveAll(allThemes, setting.UI.DefaultTheme) | ||||
| 		allThemes = append([]string{setting.UI.DefaultTheme}, allThemes...) // move the default theme to the top | ||||
| 	} | ||||
| 	ctx.Data["AllThemes"] = allThemes | ||||
|  | ||||
| 	var hiddenCommentTypes *big.Int | ||||
| 	val, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyHiddenCommentTypes) | ||||
| 	if err != nil { | ||||
| @@ -341,11 +349,12 @@ func UpdateUIThemePost(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsSettingsAppearance"] = true | ||||
|  | ||||
| 	if ctx.HasError() { | ||||
| 		ctx.Flash.Error(ctx.GetErrMsg()) | ||||
| 		ctx.Redirect(setting.AppSubURL + "/user/settings/appearance") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if !form.IsThemeExists() { | ||||
| 	if !webtheme.IsThemeAvailable(form.Theme) { | ||||
| 		ctx.Flash.Error(ctx.Tr("settings.theme_update_error")) | ||||
| 		ctx.Redirect(setting.AppSubURL + "/user/settings/appearance") | ||||
| 		return | ||||
|   | ||||
| @@ -652,7 +652,7 @@ func registerRoutes(m *web.Route) { | ||||
| 			m.Get("", user_setting.BlockedUsers) | ||||
| 			m.Post("", web.Bind(forms.BlockUserForm{}), user_setting.BlockedUsersPost) | ||||
| 		}) | ||||
| 	}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "AllThemes", setting.UI.Themes, "EnablePackages", setting.Packages.Enabled)) | ||||
| 	}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "EnablePackages", setting.Packages.Enabled)) | ||||
|  | ||||
| 	m.Group("/user", func() { | ||||
| 		m.Get("/activate", auth.Activate) | ||||
|   | ||||
| @@ -230,6 +230,7 @@ func Contexter() func(next http.Handler) http.Handler { | ||||
|  | ||||
| // HasError returns true if error occurs in form validation. | ||||
| // Attention: this function changes ctx.Data and ctx.Flash | ||||
| // If HasError is called, then before Redirect, the error message should be stored by ctx.Flash.Error(ctx.GetErrMsg()) again. | ||||
| func (ctx *Context) HasError() bool { | ||||
| 	hasErr, ok := ctx.Data["HasError"] | ||||
| 	if !ok { | ||||
|   | ||||
| @@ -11,7 +11,6 @@ import ( | ||||
|  | ||||
| 	auth_model "code.gitea.io/gitea/models/auth" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/web/middleware" | ||||
| 	"code.gitea.io/gitea/services/context" | ||||
| @@ -273,7 +272,7 @@ func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding. | ||||
|  | ||||
| // UpdateThemeForm form for updating a users' theme | ||||
| type UpdateThemeForm struct { | ||||
| 	Theme string `binding:"Required;MaxSize(30)"` | ||||
| 	Theme string `binding:"Required;MaxSize(255)"` | ||||
| } | ||||
|  | ||||
| // Validate validates the field | ||||
| @@ -282,20 +281,6 @@ func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) bindi | ||||
| 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | ||||
| } | ||||
|  | ||||
| // IsThemeExists checks if the theme is a theme available in the config. | ||||
| func (f UpdateThemeForm) IsThemeExists() bool { | ||||
| 	var exists bool | ||||
|  | ||||
| 	for _, v := range setting.UI.Themes { | ||||
| 		if strings.EqualFold(v, f.Theme) { | ||||
| 			exists = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return exists | ||||
| } | ||||
|  | ||||
| // ChangePasswordForm form for changing password | ||||
| type ChangePasswordForm struct { | ||||
| 	OldPassword string `form:"old_password" binding:"MaxSize(255)"` | ||||
|   | ||||
							
								
								
									
										74
									
								
								services/webtheme/webtheme.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								services/webtheme/webtheme.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package webtheme | ||||
|  | ||||
| import ( | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/container" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/public" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	availableThemes    []string | ||||
| 	availableThemesSet container.Set[string] | ||||
| 	themeOnce          sync.Once | ||||
| ) | ||||
|  | ||||
| func initThemes() { | ||||
| 	availableThemes = nil | ||||
| 	defer func() { | ||||
| 		availableThemesSet = container.SetOf(availableThemes...) | ||||
| 		if !availableThemesSet.Contains(setting.UI.DefaultTheme) { | ||||
| 			setting.LogStartupProblem(1, log.ERROR, "Default theme %q is not available, please correct the '[ui].DEFAULT_THEME' setting in the config file", setting.UI.DefaultTheme) | ||||
| 		} | ||||
| 	}() | ||||
| 	cssFiles, err := public.AssetFS().ListFiles("/assets/css") | ||||
| 	if err != nil { | ||||
| 		log.Error("Failed to list themes: %v", err) | ||||
| 		availableThemes = []string{setting.UI.DefaultTheme} | ||||
| 		return | ||||
| 	} | ||||
| 	var foundThemes []string | ||||
| 	for _, name := range cssFiles { | ||||
| 		name, ok := strings.CutPrefix(name, "theme-") | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		name, ok = strings.CutSuffix(name, ".css") | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		foundThemes = append(foundThemes, name) | ||||
| 	} | ||||
| 	if len(setting.UI.Themes) > 0 { | ||||
| 		allowedThemes := container.SetOf(setting.UI.Themes...) | ||||
| 		for _, theme := range foundThemes { | ||||
| 			if allowedThemes.Contains(theme) { | ||||
| 				availableThemes = append(availableThemes, theme) | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		availableThemes = foundThemes | ||||
| 	} | ||||
| 	sort.Strings(availableThemes) | ||||
| 	if len(availableThemes) == 0 { | ||||
| 		setting.LogStartupProblem(1, log.ERROR, "No theme candidate in asset files, but Gitea requires there should be at least one usable theme") | ||||
| 		availableThemes = []string{setting.UI.DefaultTheme} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func GetAvailableThemes() []string { | ||||
| 	themeOnce.Do(initThemes) | ||||
| 	return availableThemes | ||||
| } | ||||
|  | ||||
| func IsThemeAvailable(name string) bool { | ||||
| 	themeOnce.Do(initThemes) | ||||
| 	return availableThemesSet.Contains(name) | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="{{ctx.Locale.Lang}}" data-theme="{{ThemeName .SignedUser}}"> | ||||
| <html lang="{{ctx.Locale.Lang}}" data-theme="{{UserThemeName .SignedUser}}"> | ||||
| <head> | ||||
| 	<meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
| 	<title>{{if .Title}}{{.Title}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title> | ||||
|   | ||||
| @@ -1,2 +1,2 @@ | ||||
| <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/index.css?v={{AssetVersion}}"> | ||||
| <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{ThemeName .SignedUser | PathEscape}}.css?v={{AssetVersion}}"> | ||||
| <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{UserThemeName .SignedUser | PathEscape}}.css?v={{AssetVersion}}"> | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| {{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics. | ||||
| * base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, ThemeName | ||||
| * base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, UserThemeName | ||||
| * ctx.Locale | ||||
| * .Flash | ||||
| * .ErrorMsg | ||||
| * .SignedUser (optional) | ||||
| */}} | ||||
| <!DOCTYPE html> | ||||
| <html lang="{{ctx.Locale.Lang}}" data-theme="{{ThemeName .SignedUser}}"> | ||||
| <html lang="{{ctx.Locale.Lang}}" data-theme="{{UserThemeName .SignedUser}}"> | ||||
| <head> | ||||
| 	<meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
| 	<title>Internal Server Error - {{AppName}}</title> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| {{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings sshkeys")}} | ||||
| {{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings")}} | ||||
| 	<div class="user-setting-content"> | ||||
|  | ||||
| 		<!-- Theme --> | ||||
| @@ -6,39 +6,26 @@ | ||||
| 			{{ctx.Locale.Tr "settings.manage_themes"}} | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<div class="ui email list"> | ||||
| 				<div class="item"> | ||||
| 					{{ctx.Locale.Tr "settings.theme_desc"}} | ||||
| 				</div> | ||||
|  | ||||
| 			<form class="ui form" action="{{.Link}}/theme" method="post"> | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 					<div class="field"> | ||||
| 						<label for="ui">{{ctx.Locale.Tr "settings.ui"}}</label> | ||||
| 						<div class="ui selection dropdown" id="ui"> | ||||
| 							<input name="theme" type="hidden" value="{{.SignedUser.Theme}}"> | ||||
| 							{{svg "octicon-triangle-down" 14 "dropdown icon"}} | ||||
| 							<div class="text"> | ||||
| 								{{range $i,$a := .AllThemes}} | ||||
| 									{{if eq $.SignedUser.Theme $a}}{{$a}}{{end}} | ||||
| 								{{end}} | ||||
| 							</div> | ||||
|  | ||||
| 							<div class="menu"> | ||||
| 							{{range $i,$a := .AllThemes}} | ||||
| 								<div class="item{{if eq $.SignedUser.Theme $a}} active selected{{end}}" data-value="{{$a}}"> | ||||
| 									{{$a}} | ||||
| 								</div> | ||||
| 							{{end}} | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  | ||||
| 				<div class="field"> | ||||
| 					{{ctx.Locale.Tr "settings.theme_desc"}} | ||||
| 					<a class="muted" target="_blank" href="https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/" data-tooltip-content="{{ctx.Locale.Tr "settings.theme_colorblindness_prompt"}}"> | ||||
| 						{{svg "octicon-question"}} {{ctx.Locale.Tr "settings.theme_colorblindness_help"}} | ||||
| 					</a> | ||||
| 				</div> | ||||
| 				<div class="field"> | ||||
| 					<label>{{ctx.Locale.Tr "settings.ui"}}</label> | ||||
| 					<select name="theme" class="ui dropdown"> | ||||
| 						{{range $theme := .AllThemes}} | ||||
| 						<option value="{{$theme}}" {{Iif (eq $.SignedUser.Theme $theme) "selected"}}>{{$theme}}</option> | ||||
| 						{{end}} | ||||
| 					</select> | ||||
| 				</div> | ||||
| 				<div class="field"> | ||||
| 					<button class="ui primary button">{{ctx.Locale.Tr "settings.update_theme"}}</button> | ||||
| 				</div> | ||||
| 			</form> | ||||
| 			</div> | ||||
| 		</div> | ||||
|  | ||||
| 		<!-- Language --> | ||||
|   | ||||
| @@ -0,0 +1,11 @@ | ||||
| @import "./theme-gitea-dark.css"; | ||||
|  | ||||
| /* red/green colorblind-friendly colors */ | ||||
| /* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */ | ||||
| :root { | ||||
|   --color-diff-added-word-bg: #388bfd66; | ||||
|   --color-diff-added-row-bg: #388bfd26; | ||||
|  | ||||
|   --color-diff-removed-word-bg: #db6d2866; | ||||
|   --color-diff-removed-row-bg: #db6d2826; | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| @import "./theme-gitea-light.css"; | ||||
|  | ||||
| /* red/green colorblind-friendly colors */ | ||||
| /* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */ | ||||
| :root { | ||||
|   --color-diff-added-word-bg: #54aeff66; | ||||
|   --color-diff-added-row-bg: #ddf4ff80; | ||||
|  | ||||
|   --color-diff-removed-word-bg: #ffb77c80; | ||||
|   --color-diff-removed-row-bg: #fff1e580; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user